mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 07:35:16 +00:00
Compare commits
962 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| cc9274c9af | |||
| bbdf7efc34 | |||
| 38a986995a | |||
| ee8d35df7a | |||
| be411cd5ff | |||
| 2104019171 | |||
| 4e564707df | |||
| da8611e0c3 | |||
| 5d11641a5f | |||
| 24d38e4fcf | |||
| efb65a8413 | |||
| 3fc90554f5 | |||
| 4ebe28faae | |||
| 693171cb05 | |||
| 42a2d090aa | |||
| c84eaa83dd | |||
| 74c3ff80cb | |||
| 5893d3ee64 | |||
| 7f435c56a0 | |||
| 59b7100cf6 | |||
| 0923d1aa24 | |||
| ffe6c36f2f | |||
| 8c4752c5d8 | |||
| d6202e522b | |||
| 50a24c62c5 | |||
| 6b17d08892 | |||
| 79c91fd8c5 | |||
| 95764258ab | |||
| 41e6872557 | |||
| 30c96c37c2 | |||
| 20f1cf3dff | |||
| 8de0d6186e | |||
| edd7d8aefe | |||
| a8da4903f4 | |||
| c8df5033f7 | |||
| 412f418da3 | |||
| 3782d21069 | |||
| 82a29f4470 | |||
| 4a65326b24 | |||
| d75d8729be | |||
| c87060454e | |||
| bbba52c7ff | |||
| 33418bdb33 | |||
| de47db2b66 | |||
| fa6fcffbe1 | |||
| 93d3f11891 | |||
| 59479f10fc | |||
| 11e72253d4 | |||
| 37fca9c88a | |||
| 6d67048e98 | |||
| c516a5c1bd | |||
| 429431c13c | |||
| b80dfd7b9a | |||
| 4c144b6ceb | |||
| 718e32773a | |||
| fb75d5d3ee | |||
| 99bbcbebfc | |||
| 289e4b5dc1 | |||
| a974dcdab6 | |||
| 9a48620152 | |||
| e444b3a402 | |||
| fabf4f0c0a | |||
| d467888d6a | |||
| eb46311561 | |||
| 63839bb0a7 | |||
| 793fada0fb | |||
| 494ba78156 | |||
| 9816b27ebf | |||
| 1c43b7891b | |||
| 9d7739213e | |||
| 2ab5f6a305 | |||
| c73103969f | |||
| 6cbbdf5d5b | |||
| 3a48435497 | |||
| 084925de8b | |||
| 5bac2fe695 | |||
| 2c29fb241f | |||
| 77dc11bcc2 | |||
| 5154622566 | |||
| c681cd286a | |||
| c1d53ec11f | |||
| 36b49c66c6 | |||
| 80b8e340da | |||
| b51470db39 | |||
| a5f4596f8d | |||
| 5f3c3760c0 | |||
| 8b05885d57 | |||
| d9d7ab4ee5 | |||
| 30bce5f9bb | |||
| fbe0282738 | |||
| 5bad08b9a0 | |||
| 310691bca2 | |||
| 8f7967acd0 | |||
| faf18476b1 | |||
| 9acccad23a | |||
| db1a0aedf8 | |||
| 01a26fda25 | |||
| 0229272b0a | |||
| c817acff09 | |||
| a5eec81819 | |||
| 98f70879bd | |||
| ceb4627f4e | |||
| 8d9d4fe23f | |||
| 730298110c | |||
| 8578f7d0d1 | |||
| 07a5daefb0 | |||
| 0ed524bce5 | |||
| 94199af7b2 | |||
| b8d7a00f58 | |||
| aca963befc | |||
| 3ea4e334bf | |||
| 9955fa8c29 | |||
| fb37a6bcab | |||
| 4f8bdbb518 | |||
| 73c190891d | |||
| 93e701f471 | |||
| 68b9bf7e38 | |||
| 826f90505c | |||
| 7ae36f16c8 | |||
| 281f6d5535 | |||
| b4072a06c4 | |||
| cb3ee423c9 | |||
| 1360a744a7 | |||
| f3058598dc | |||
| 94db0f6ec9 | |||
| f08732095d | |||
| f946650ddb | |||
| 14fd6bbdce | |||
| 5059e8df34 | |||
| 3d405a1ac5 | |||
| 78e00db018 | |||
| e62bc31e63 | |||
| 4763f1342c | |||
| 9413e53b00 | |||
| 35d352bbeb | |||
| 8dd5e0c250 | |||
| 139e487998 | |||
| 9bb0a3fbd3 | |||
| 9a1a6f2625 | |||
| e781d6d2b1 | |||
| 5b342b50ce | |||
| 869822d180 | |||
| e50422bce5 | |||
| 999882e723 | |||
| d68dc82159 | |||
| cc9fdd0032 | |||
| 503cc4a729 | |||
| d585269228 | |||
| 50f535b910 | |||
| 978bdd149e | |||
| a9d558e891 | |||
| 8291eb58df | |||
| 7298dec353 | |||
| 7574b01863 | |||
| 98e739a12b | |||
| 262ce03e46 | |||
| 1906691a35 | |||
| 4ca23b84a4 | |||
| b05eb8440b | |||
| f4de7748f5 | |||
| 93c26a839b | |||
| 878e7441f2 | |||
| 27d387e3e1 | |||
| 7b7e4f60bc | |||
| 8bac111acf | |||
| 7a46d3b497 | |||
| 357aa92e31 | |||
| fa17f16716 | |||
| ce99c8c706 | |||
| be828cf004 | |||
| 1057bb15d3 | |||
| ea5e8891a8 | |||
| 57d83f082a | |||
| 8a3f80ba82 | |||
| 374ebd6f7a | |||
| ab2ebefe2b | |||
| c34a9f6213 | |||
| f5df41c75f | |||
| 0c2dc06cb7 | |||
| 4552ccd7d7 | |||
| 525bcd8295 | |||
| ac2ad8d8b5 | |||
| 14ce495e9e | |||
| 4aed2cf652 | |||
| 244fb0ca28 | |||
| f6e123f1ed | |||
| e1e4e7f011 | |||
| 814b6ff489 | |||
| 3aa17ab1f4 | |||
| 4646ed8f4a | |||
| 3f5c8acb3c | |||
| 93b257314a | |||
| 19a1db8cb4 | |||
| c3275a4aa9 | |||
| 927185d95c | |||
| a3df6c6f0c | |||
| e1fa977b5b | |||
| ce95f316b9 | |||
| a162af5a75 | |||
| fb0ea4610d | |||
| 4f86a9978b | |||
| cc78b880ce | |||
| 4336fdb241 | |||
| 87db7cf1d4 | |||
| 09ff802c7e | |||
| 71b59b1950 | |||
| a3300ad9c6 | |||
| 8f40d4affa | |||
| 560f3fcccb | |||
| ecaadf63c5 | |||
| d490a6e0b4 | |||
| 53ced205b8 | |||
| 61c4983949 | |||
| fb862f466f | |||
| 8d51300386 | |||
| c4b413563d | |||
| 8f852971f9 | |||
| 3c54c89464 | |||
| d07aa13822 | |||
| c218f7d0e3 | |||
| 72ad872e90 | |||
| 854f244d22 | |||
| 9acbb223db | |||
| c59fc687db | |||
| 0791b917f1 | |||
| ba182ce8e4 | |||
| 663291c515 | |||
| 99253eecea | |||
| 916a02cb36 | |||
| 114c4abcd1 | |||
| e1e3d11932 | |||
| df46217c78 | |||
| d18cc92396 | |||
| 8f63b4b790 | |||
| ce7de93936 | |||
| ef2a24fe6f | |||
| 038cd0155b | |||
| fb430ac1ed | |||
| 53cfa3703b | |||
| 7ed9e8d145 | |||
| 93e0b0cbea | |||
| 0118bd3168 | |||
| 07d5245a5f | |||
| 6b844213ec | |||
| dabdeb8a80 | |||
| ecd56315e6 | |||
| 15b9e839e9 | |||
| d7f7dcd47b | |||
| b1981e09b8 | |||
| c9edd0a795 | |||
| d83e0716bb | |||
| aed38afff3 | |||
| 1df522e6ec | |||
| bd4ef2354e | |||
| 32a49a6969 | |||
| 21806687e6 | |||
| 7ade91a091 | |||
| 4e41f96d89 | |||
| 325c32b4c9 | |||
| 7d6606a8e5 | |||
| c887d520eb | |||
| e6050fc558 | |||
| cae4853334 | |||
| 9ef2bebfe9 | |||
| 53294af810 | |||
| 7d21c815a5 | |||
| e07719a86c | |||
| fa5bee6ca3 | |||
| 3a43ef2c3f | |||
| 55deab03cd | |||
| edda84fefd | |||
| 10b191f631 | |||
| d5a575c5e0 | |||
| 82d8c3e2d9 | |||
| bc0c8dbcf3 | |||
| de0245ff40 | |||
| 7f56ff0281 | |||
| 2cfbf5411c | |||
| f70da1c82a | |||
| 0b4db49f57 | |||
| fbffd54aa2 | |||
| 4d19a33af2 | |||
| 9d67a0e656 | |||
| 093005bfd9 | |||
| 514883f0bd | |||
| 27681aad98 | |||
| 0d367da776 | |||
| c499a8a13a | |||
| 64a6de751d | |||
| d39f0d6ff0 | |||
| bdbfbf7300 | |||
| ccac3d2c3c | |||
| f07278490f | |||
| f1880fc53c | |||
| e8fe4a2dea | |||
| 1d79507665 | |||
| f05dc67887 | |||
| ca5e57c3b7 | |||
| 6b1fabdd92 | |||
| 35f0581c17 | |||
| c79f02fe48 | |||
| ec028a1c0d | |||
| e3155dd13a | |||
| b61254dda6 | |||
| b4bdf97cfe | |||
| bf98984012 | |||
| 77258cbbc9 | |||
| 4150455c3e | |||
| 008aacb5c2 | |||
| d12d6a6a46 | |||
| ef7594b8f7 | |||
| 6a73217740 | |||
| 47c44fca89 | |||
| 6fa63a5c35 | |||
| 29533908c2 | |||
| 74a1e9dee2 | |||
| b3662e07c1 | |||
| 200e0c58c9 | |||
| e67286c51d | |||
| 603622e337 | |||
| 5c2a435a25 | |||
| 4afcd722ae | |||
| 21d6964230 | |||
| 4499ccc501 | |||
| abbca568d1 | |||
| 40e49d0ac9 | |||
| 8d9a0a91f0 | |||
| 15640dfdf0 | |||
| 2f5dde5d2f | |||
| a9a8a04bca | |||
| c08e5ce62c | |||
| ec353e179d | |||
| 496f253e69 | |||
| e436115a4a | |||
| 1c61f93b5f | |||
| 0494f7d1fd | |||
| 4aa66338fc | |||
| 8cda844ef1 | |||
| dde1ef92f0 | |||
| 999f8b481f | |||
| 294951f22a | |||
| 2619737f64 | |||
| 445a90defb | |||
| d3d64f636c | |||
| eef072693b | |||
| 43c5f8d340 | |||
| b344d65302 | |||
| ce967f35ca | |||
| 80718cc783 | |||
| 73f22332ec | |||
| e963b6b65d | |||
| 327e90e8ec | |||
| 9253b764fa | |||
| 393aed6270 | |||
| b240d6a8fc | |||
| 3cda0643fb | |||
| 1ba63675d5 | |||
| 32f52f2d95 | |||
| 9215edc0a0 | |||
| 25ae21dae3 | |||
| 94393bef4b | |||
| 4f29183685 | |||
| edecf56dd6 | |||
| 08754bc626 | |||
| 702542f408 | |||
| 6990b371a6 | |||
| 8020d9045b | |||
| 34bb52a051 | |||
| a2e0bc267c | |||
| d86a95b5e1 | |||
| 585ed46034 | |||
| 82a4f80cd2 | |||
| f47a3968b7 | |||
| 0b592dd5c6 | |||
| addf6ade9a | |||
| d7d0429169 | |||
| 4bf5dc6646 | |||
| 5b20027f2e | |||
| 4499f71dba | |||
| 81b734c151 | |||
| 0b4a6b5362 | |||
| 3a0636046e | |||
| 52eccb29e4 | |||
| 86e4a935e2 | |||
| 1fa952b930 | |||
| 78e11bc6be | |||
| 40cc20e06e | |||
| c223f970ec | |||
| daa80dc949 | |||
| 255a78a534 | |||
| 6faa469bf5 | |||
| eeefac4964 | |||
| eb4770085a | |||
| 1ed63ce4f5 | |||
| e85ad95bcf | |||
| bd73c79787 | |||
| b244d506bd | |||
| 44f03865c0 | |||
| 72dfb4f11d | |||
| 6b0a4f20bf | |||
| f052aacee1 | |||
| 13e2ff8035 | |||
| 199e61e01c | |||
| 1bf6abf338 | |||
| c7b1d72bf0 | |||
| 0b3bdcb219 | |||
| 6653307fda | |||
| 642ba72a1d | |||
| 71144db09c | |||
| 395bd62663 | |||
| d8fd6430ed | |||
| 1e243281c7 | |||
| de23494409 | |||
| b23d3a9f37 | |||
| de31b90080 | |||
| ccc585ca1f | |||
| 4dbe81cb66 | |||
| d139d1d1bd | |||
| a186544fad | |||
| 7de152539d | |||
| 1e486a16bd | |||
| 7f9c24eedb | |||
| 8a3396deba | |||
| 96d5b37196 | |||
| 7b2e332f6c | |||
| ab541c1653 | |||
| 5f058f2ceb | |||
| 360381dd9f | |||
| cef4096b39 | |||
| a9c96c1afa | |||
| a446deaff3 | |||
| e5dd987d03 | |||
| 51fd8ae08c | |||
| 884540e565 | |||
| ca1313e891 | |||
| 8df18746fa | |||
| 40f829ce54 | |||
| e16b3e35f4 | |||
| 2f15e26ffa | |||
| b4cdbc6534 | |||
| 0a93ffd76b | |||
| 98e9848c4a | |||
| 42bb5fccb8 | |||
| fb921788b9 | |||
| 847c6b9aa2 | |||
| cafbcd4097 | |||
| a22539a91e | |||
| 89378c3883 | |||
| f763e1dc42 | |||
| ea13e3cec4 | |||
| 3d3302e547 | |||
| 95d81d02a1 | |||
| 8401a674c3 | |||
| 3fcf1a7723 | |||
| 1a3ee78459 | |||
| b99c499d2e | |||
| 709b388dfc | |||
| 6e83240233 | |||
| 202c235c66 | |||
| 576d773fec | |||
| 58a930e456 | |||
| 52206ce9d0 | |||
| 1de9d5683b | |||
| b66242db98 | |||
| 004ac88516 | |||
| 3d1eac234a | |||
| a78e52812f | |||
| 5993e6c791 | |||
| 1ec8b7c324 | |||
| e75002a96d | |||
| a68364d4d7 | |||
| 2b5e836a33 | |||
| 9caa9c39e5 | |||
| 096bdaba7a | |||
| d22c1f57e5 | |||
| dac00be7b5 | |||
| 4e7cb9b23f | |||
| 5fdee2cb5e | |||
| 1db3f3deab | |||
| b2af55ac63 | |||
| 40cdc285b6 | |||
| 46f017e68b | |||
| 1769ac2c2d | |||
| 067bdcda2d | |||
| 3bc1934966 | |||
| 6ee9a3a64c | |||
| c0196c97a5 | |||
| 96105d6ba0 | |||
| 853ba6079c | |||
| b6104c64fe | |||
| e95678cf3a | |||
| e2c21051aa | |||
| 7c575e1b43 | |||
| c89c253f29 | |||
| f54685e216 | |||
| 8141bcb3b6 | |||
| db3d21db49 | |||
| ca85c0ecf3 | |||
| 702db46f20 | |||
| 327d0bf5c5 | |||
| 2375296e67 | |||
| b4fcd2da14 | |||
| e3c92cce77 | |||
| 1141d34f07 | |||
| 354fe12742 | |||
| 1b19c66603 | |||
| d7077fc7db | |||
| d6bd4a0d67 | |||
| 8628c87be4 | |||
| fef822145d | |||
| cfefa7d579 | |||
| def115fe0f | |||
| 01a7c6f4e0 | |||
| 0e86cdeb4a | |||
| a65f888a77 | |||
| 71059e3ea3 | |||
| ee3faaad11 | |||
| d2aad34935 | |||
| d8ed34beba | |||
| 3154d37e41 | |||
| 272f30af53 | |||
| 4c6717e234 | |||
| 39b6562d14 | |||
| 0d966fe197 | |||
| 82f94b010f | |||
| 4555c541a8 | |||
| a853a51a0b | |||
| 756694c029 | |||
| 669f3b3984 | |||
| e7eed2abbe | |||
| 98a13cf487 | |||
| 7f9aa214a4 | |||
| 347a536a4e | |||
| 5cf0abaf2b | |||
| 09c6f025a1 | |||
| f47208528b | |||
| 87d90a65ac | |||
| 02cff8c72c | |||
| a67993ac0b | |||
| e540d25665 | |||
| 52c4dc49cf | |||
| 44ad70320a | |||
| 2b8e1c0b11 | |||
| 85a2ce9065 | |||
| a24aa79dbf | |||
| 633de85180 | |||
| f32d03eb88 | |||
| e83d63c2aa | |||
| ff30ba62ba | |||
| ea98444dc0 | |||
| 328c3a7d51 | |||
| 4b465bedea | |||
| 461ef2c68e | |||
| 46efdacd6d | |||
| a063cc1a53 | |||
| 36f2f5d52e | |||
| ddc99104d4 | |||
| 8d277e9108 | |||
| f305d57ab7 | |||
| 877f2e477b | |||
| c8e2fc0e57 | |||
| 4cb9cbb5ae | |||
| 6f3be56223 | |||
| c638c108d2 | |||
| d8f5aa680d | |||
| f2b585e446 | |||
| 78f91e0037 | |||
| 3f3c156697 | |||
| 4627bf5d1f | |||
| 93a144984a | |||
| 2d0784440c | |||
| 05a083437f | |||
| d4b5f59f92 | |||
| ff53fb6db0 | |||
| 534b3908cc | |||
| 7ea2369810 | |||
| 4d96ec10c1 | |||
| a37ee3457a | |||
| 6969bb9a5c | |||
| 4567041456 | |||
| 49f09e46da | |||
| becd027563 | |||
| 6eb1605be6 | |||
| 5c16aec096 | |||
| 835e2dc1fb | |||
| e2378ab29a | |||
| 0f1d7eb391 | |||
| dd358b8b95 | |||
| 21cf5fcaf4 | |||
| 8c7509759b | |||
| dc9ac0d0ff | |||
| 93b401ca9f | |||
| b137d8270a | |||
| 6db7757451 | |||
| 43fd42b757 | |||
| 5d1c4d2b9f | |||
| 425acd8bf6 | |||
| 414160e0df | |||
| 117c6aefe8 | |||
| 13d584d39a | |||
| 9bc89c24f5 | |||
| 5a8c4d5a4e | |||
| 98da83a552 | |||
| 5344a30a3b | |||
| d5d6b3673e | |||
| d2a2fe88d1 | |||
| ced44c86ce | |||
| ee402ad444 | |||
| 3d2155eb66 | |||
| bd24b03548 | |||
| 5cb55a0f37 | |||
| 163242f80f | |||
| 266bfcb8bf | |||
| d82fa02669 | |||
| 3a5207a0d2 | |||
| e38f3bb884 | |||
| 751cdd9b3b | |||
| 8df6bdca89 | |||
| f9df7545ad | |||
| 9b7a90afef | |||
| 2ae83b3247 | |||
| 5d907f234e | |||
| 3a005c4f13 | |||
| 6c2f50470a | |||
| d233783942 | |||
| 3d9e4c80a2 | |||
| a7f49015c1 | |||
| 68ed2631dc | |||
| 2814fea67e | |||
| 3388be9db6 | |||
| 9b89319820 | |||
| ad34089df5 | |||
| 7a4c6bc326 | |||
| 6ab386ae35 | |||
| 0abbfde912 | |||
| 0521defb63 | |||
| ea4d96610f | |||
| 4bc56564b4 | |||
| 013f7f09da | |||
| f082de1f92 | |||
| ad39dc8c0c | |||
| 481a76319f | |||
| 27a43d5aea | |||
| df1d5a0680 | |||
| 722109faf0 | |||
| d4dca7736a | |||
| 4e90becd52 | |||
| 26890e94eb | |||
| 55c83f9a59 | |||
| b837d82878 | |||
| cbdcdb1e0f | |||
| 4fea85289e | |||
| ba5f9132c9 | |||
| 991681daa2 | |||
| 2faf53e16b | |||
| 5430ed99de | |||
| fef4dfd05e | |||
| 05943db3b5 | |||
| 682fabd1ef | |||
| ee8ccc6d7e | |||
| 8ee0bcac2a | |||
| 0c4ad74788 | |||
| 7d944e4f1f | |||
| 67b2394c16 | |||
| 269d08a35f | |||
| f626e83e48 | |||
| 8ba18cd587 | |||
| 54c12fea89 | |||
| afb5ce71ce | |||
| 6fef6acf10 | |||
| a27411401b | |||
| 3718fc319b | |||
| c6c9463f8c | |||
| b410685318 | |||
| b742b29259 | |||
| df5d27ab61 | |||
| 2870155c0b | |||
| 9ba1075f48 | |||
| 4ae5b2e050 | |||
| 4369bd8f5e | |||
| 321b24a7b9 | |||
| 22e0c1fb54 | |||
| ba65e3c020 | |||
| c19ab952e3 | |||
| ecc29087b5 | |||
| 87e056de64 | |||
| 761200529f | |||
| 770351916e | |||
| 4b7d957b47 | |||
| a1c03c18a5 | |||
| 5d376b054f | |||
| 277d13c2bb | |||
| 4a9389d80a | |||
| 33593c13ae | |||
| eef82c18c2 | |||
| 7363580095 | |||
| 23a46b0597 | |||
| 51c79f294c | |||
| ce1e0e1873 | |||
| c2775b3b63 | |||
| bcb224d1cb | |||
| 8bc5f22866 | |||
| c3f2ef02c4 | |||
| 08e2c90d9d | |||
| d2bb2ba454 | |||
| ba912318cd | |||
| 3d1f057373 | |||
| 0679807517 | |||
| 7f0e4803e2 | |||
| fa76029a17 | |||
| 376c68957f | |||
| 222ec8d4b9 | |||
| 90715c1944 | |||
| 801e1b6059 | |||
| 14bb659b75 | |||
| eab3c50288 | |||
| 74396e7240 | |||
| 6c2539e886 | |||
| aba25d8521 | |||
| f0c97756de | |||
| f14c57d4c2 | |||
| b6f60cc8ad | |||
| 170516d343 | |||
| c931e91bef | |||
| db9151ae36 | |||
| f2d86b6d69 | |||
| 534cdd091b | |||
| fdf4a48638 | |||
| 58a3c5634e | |||
| f0ddb6e89f | |||
| 2b408db7cb | |||
| 4e4bd23e2b | |||
| 62d0eb606e | |||
| 0e9beeae14 | |||
| 5e4dbdee24 | |||
| 9435dae6cd | |||
| 813210bf47 | |||
| c372dfc544 | |||
| ac77200683 | |||
| f5afbc90a4 | |||
| b24760c819 | |||
| 6732957e42 | |||
| 1fd0006859 | |||
| 5a205346e3 | |||
| e193004659 | |||
| fe3a9c33d0 | |||
| d83116b88f | |||
| 5f56098ae7 | |||
| 0f935f4372 | |||
| df5c5a8c01 | |||
| 57a0c3bad8 | |||
| 2a52326a45 | |||
| 87e4ee1eeb | |||
| 45833eea9a | |||
| baf10e9b15 | |||
| 463a947df1 | |||
| cceebb4fd9 | |||
| afd5465a61 | |||
| fe604733de | |||
| da22f9f39f | |||
| 284ebbf75d | |||
| 2c08ccad87 | |||
| fa4e234bab | |||
| 35c4179ca1 | |||
| c14ba7ce34 | |||
| 601ce3add3 | |||
| 08ff39fd0b | |||
| 82a6ee3875 | |||
| 3bf592f870 | |||
| 241e205717 | |||
| fb2720ea8d | |||
| 14853befda | |||
| e7f2803d11 | |||
| 320015dd8f | |||
| 471c124a98 | |||
| aa44a109c6 | |||
| 41e5ca8422 | |||
| 53af8b2bb6 | |||
| bd885b135f | |||
| 62a6dde81a | |||
| 14b72b344f | |||
| ac03668063 | |||
| 2512e176c9 | |||
| b613393866 | |||
| 607a6e95de | |||
| 871f574ff7 | |||
| 377e470b15 | |||
| e0a3f0cea5 | |||
| 66727b2736 | |||
| 8215ad977f | |||
| a611dffbdf | |||
| 39f378d8cf | |||
| f00da58153 | |||
| 09b038b16d | |||
| cf601d97ad | |||
| 80edad53d4 | |||
| a972b32769 | |||
| efcaae3e60 | |||
| e0ee591638 | |||
| 310a6d909f | |||
| cefd8fc50f | |||
| a4422593f2 | |||
| e2c5d22756 | |||
| 84385d9dc3 | |||
| 6cfa4c8ab7 | |||
| c76ccc53e1 | |||
| d48eafb1a2 | |||
| 302d9b4b91 | |||
| a091a7f68b | |||
| 53621f6de3 | |||
| 288b965db3 | |||
| 578c9dcfc6 | |||
| 64de17b248 | |||
| d699dff872 | |||
| 501843d624 | |||
| 508ce4283e | |||
| 4aa8e66549 | |||
| 917a6f70d8 | |||
| 2f5e53da55 | |||
| c3cdefc90a | |||
| 9c580e4989 | |||
| dbc75f7352 | |||
| db841a8578 | |||
| 50c902c4e4 | |||
| 84b6e0bde6 | |||
| abc29c3fba | |||
| 8356c534f5 | |||
| 78ff2687b4 | |||
| 9b6586fb36 | |||
| ea133219d0 | |||
| c9b5d81a3c | |||
| d895c63211 | |||
| 8c86da67ca | |||
| a9a230dfc3 | |||
| 6d53e52771 | |||
| 52e10b7cfe | |||
| addac85696 | |||
| 13fb949d68 | |||
| f1b593cfbd | |||
| f998e2dec9 | |||
| 2c0bd53a91 | |||
| 6dd7f6162f | |||
| 475b0bb2b3 | |||
| e6343935e3 | |||
| 946883fd4c | |||
| b1b7e3148b | |||
| 32203f6438 | |||
| f2d3c5d2ed | |||
| 7e2f0ed4bc | |||
| a877acf905 | |||
| ac756ddf7e | |||
| 36e2236e6b | |||
| 72df665520 | |||
| 07785528a5 | |||
| bb579dd581 | |||
| a7d3064340 | |||
| 1a60778248 | |||
| cda4bb5bda | |||
| 7543781046 | |||
| 82f3ffad06 | |||
| dc4d2a1731 | |||
| 2523925915 | |||
| d5bbd8475c | |||
| 54d195268a | |||
| a0deb9509f | |||
| b0b1433594 | |||
| 17a2c0a37c | |||
| fd68ea1cc2 | |||
| 784874c776 | |||
| f45c6d3466 | |||
| 98af70902f | |||
| 41a67b5c4c | |||
| 83ee903af4 | |||
| e591b0e485 | |||
| bcc9a34742 | |||
| 6b56c7de7f | |||
| 7b42719699 | |||
| 64b8343f67 | |||
| fcc5089fa6 | |||
| 334f782aa8 | |||
| 1776f2bf77 | |||
| e259412f79 | |||
| 999a02f110 | |||
| 604d0fba65 | |||
| 0e8f7b870b | |||
| 1eebc9a46e | |||
| 0a94c80c44 | |||
| c70bb2fbd8 | |||
| 12c06b384f | |||
| 82a58e7ac3 | |||
| dcb56ef433 | |||
| 8da91de514 | |||
| 4b20685562 | |||
| fff7c5cc3a | |||
| 3065742b73 | |||
| ffd69ad1f2 | |||
| 9004011479 | |||
| 1046bb4133 | |||
| 5578505213 | |||
| a61f56662a | |||
| f42e48be03 | |||
| f640e101ba | |||
| b7da276ae0 | |||
| bf7c0989f5 | |||
| 65fdbe7f55 | |||
| c9af209a93 | |||
| 48ab44a776 | |||
| b2e684af8f | |||
| b38a2c11a4 | |||
| c31acef47a | |||
| e9a573e18a | |||
| c07ddc77f7 | |||
| 303fc992d4 | |||
| 4b7797f61b | |||
| f775d41933 | |||
| 7722e7bdfa | |||
| 346c05f0de | |||
| 73c69923b3 | |||
| afbca4bdf5 | |||
| b61371942b | |||
| 61c07e8876 | |||
| d317741144 | |||
| 275739a778 | |||
| cb3a43ed8a | |||
| ec8d1d8fc9 | |||
| 868a3186dd | |||
| 17b546181c | |||
| 2d1aa15af8 | |||
| 63ee94ff89 | |||
| 7d9dc08a38 |
@@ -0,0 +1,4 @@
|
||||
docs/**/*
|
||||
_book/**/*
|
||||
node_modules/**/*
|
||||
test/**/*
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"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
|
||||
},
|
||||
"extends": "eslint:recommended"
|
||||
}
|
||||
+8
-4
@@ -22,10 +22,14 @@ 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
|
||||
|
||||
theme/javascript/vendors
|
||||
theme/vendors
|
||||
/node_modules
|
||||
|
||||
# vim swapfile
|
||||
*.swp
|
||||
|
||||
# Output of documentation
|
||||
_book
|
||||
|
||||
book.pdf
|
||||
book.epub
|
||||
book.mobi
|
||||
|
||||
+5
-3
@@ -1,7 +1,9 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
- "stable"
|
||||
- "4.1"
|
||||
before_install:
|
||||
- npm install svgexport -g
|
||||
- npm install svgexport -g
|
||||
after_success:
|
||||
- npm run lint
|
||||
|
||||
+39
-13
@@ -7,9 +7,10 @@ Names below are ordered by first contribution.
|
||||
Author
|
||||
------
|
||||
|
||||
- Samy Pessé <samypesse@gmail.com> (@SamyPesse)
|
||||
- Aaron O'Mullan <aaron.omullan@gmail.com> (@AaronO)
|
||||
|
||||
- Samy Pessé <samy@gitbook.com> (@SamyPesse)
|
||||
- Aaron O'Mullan <aaron@gitbook.com> (@AaronO)
|
||||
- Johan Preynat <johan@gitbook.com> (@jpreynat)
|
||||
- Nicolas Gaborit <nicolas@gitbook.com> (@soreine)
|
||||
|
||||
Contributors
|
||||
------------
|
||||
@@ -21,22 +22,47 @@ Translators
|
||||
------------
|
||||
|
||||
- German
|
||||
- Winnie (@winniehell)
|
||||
- Winnie (@winniehell)
|
||||
- Italian
|
||||
- Giulio Bonanome (@gbonanome)
|
||||
- Giulio Bonanome (@gbonanome)
|
||||
- Russian
|
||||
- Andrey Akinshin (@AndreyAkinshin)
|
||||
- Andrey Akinshin (@AndreyAkinshin)
|
||||
- Norwegian
|
||||
- Knut Melvær (@kmelve)
|
||||
- Knut Melvær (@kmelve)
|
||||
- Persian (Farsi)
|
||||
- Mohammad Hossein Mojtahedi (@mhm5000)
|
||||
- Mohammad Hossein Mojtahedi (@mhm5000)
|
||||
- Polish
|
||||
- Łukasz Szeremeta (@lszeremeta)
|
||||
- Portuguese
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Spanish
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Simplifiled Chinese
|
||||
- Hu Hao (@howiehu)
|
||||
- Hu Hao (@howiehu)
|
||||
- Traditional Chinese
|
||||
- Hu Hao (@howiehu)
|
||||
- Hu Hao (@howiehu)
|
||||
- French
|
||||
- Samy Pessé (@SamyPesse)
|
||||
- Samy Pessé (@SamyPesse)
|
||||
- Romanian
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Finnish
|
||||
- Tommi Savikko (@savikko)
|
||||
- Japanese
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Korean
|
||||
- Iancu Aurel (@awrelll)
|
||||
- SangYeob Yu (@deminoth)
|
||||
- Vietnamese
|
||||
- Hong Nguyen (@nghong)
|
||||
- Hebrew
|
||||
- @a-moses
|
||||
- Ukrainian
|
||||
- @Karnaukhov
|
||||
- Czech
|
||||
- @mjanda
|
||||
- Swedish
|
||||
- Jacob Burenstam (@buren)
|
||||
- Turkish
|
||||
- Turan Konan (@turankonan)
|
||||
- Catalan
|
||||
- Esaú García Sánchez-Torija (@egasato)
|
||||
|
||||
+173
@@ -2,6 +2,179 @@
|
||||
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)
|
||||
- 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
|
||||
- Headings are no longer annotated with glossary terms
|
||||
- Themes are now published as a plugin, with ability to extend it from the book source
|
||||
- `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
|
||||
- Multi-lingual books share assets folder
|
||||
- YAML front matter is parsed and can extend page's properties
|
||||
- Fix `uk` translation
|
||||
- Fix heading ID including dashes
|
||||
- Fix error in code highlighting for unknown languages
|
||||
- Fix data-uri images being handled as external images
|
||||
- Accept SSH url as plugin version
|
||||
- Add templating blocks `markdown`, `asciidoc` and `markup`
|
||||
- Better search experience
|
||||
- Better default theme, more responsive and cleaner
|
||||
|
||||
## 2.6.7
|
||||
- Fix bug with filenames including spaces
|
||||
- Add Turkish and Catalan translations
|
||||
|
||||
## 2.6.6
|
||||
- Fix custom CSS that are generated by plugins for PDF output
|
||||
|
||||
## 2.6.5
|
||||
- Fix support for plugins generating custom stylesheets (`styles-less` and `styles-sass`)
|
||||
- Fix glossary terms being replaced in script (ex: math)
|
||||
|
||||
## 2.6.4
|
||||
- Fix regression introduced by `2.6.3` of single HTML tags in markdown
|
||||
|
||||
## 2.6.3
|
||||
- Fix parsing bug with inline HTML in Markdown
|
||||
|
||||
## 2.6.2
|
||||
- Fix `gitbook.state.bookRoot`, `gitbook.state.root` now has a trailing slash
|
||||
- Update nunjucks to 2.2.0
|
||||
|
||||
## 2.6.1
|
||||
- Use CamelCase for `gitbook.state.innerLanguage`
|
||||
|
||||
## 2.6.0
|
||||
- Close sidebar after clicking a link on mobile
|
||||
- Add root for multilingual books: `gitbook.state.bookRoot`
|
||||
- Fix color of h6 headings
|
||||
- Fix bottom margin of lists and blockquotes
|
||||
- Add Swedish translation (`sv`)
|
||||
- Add Czech translation (`cs`)
|
||||
- Fix locale names for `zh-hans` and `jp`
|
||||
- Update plugin `search` to fix crashes
|
||||
- Aceept case-incensitive structure files (README, GLOSSARY, etc)
|
||||
|
||||
## 2.5.2
|
||||
- Fix custom stylesheets for ebook generation
|
||||
- Trigger event `start` before `page.change`
|
||||
|
||||
## 2.5.1
|
||||
- Fix calcul of `gitbook.state.root` when serving HTML index as `/`
|
||||
|
||||
## 2.5.0
|
||||
- Font settings, sharing and search are externalized as default plugins
|
||||
- Plugins can define a configuration schema in the manifest, this schema will be used to validate configuration during build
|
||||
- New Node.js API for plugin: `book.formatString(type, content)`
|
||||
- New client side API for website plugins: `gitbook.toolbar.createButton(opts)`
|
||||
- Better header/footer for PDF, CSS wil be inlined to easily style the header/footer
|
||||
- Cleaner table of contents for ebooks
|
||||
- Support for RTL in ebook's table of contents
|
||||
- Better colors for mobi (links and code blocks)
|
||||
- Fix installation of plugins when using a pre-release
|
||||
- Fix support for pre-releases
|
||||
- Update asciidoc parser to remove git dependency
|
||||
- Fix templating in imported content
|
||||
- Fix querystring in image urls
|
||||
- Normalize heading IDs like GitHub
|
||||
- Use Arial as default font for PDFs
|
||||
- Add `root`, `chapterTitle` and `filepath` to `gitbook.state` JS API
|
||||
|
||||
## 2.4.3
|
||||
- Add ukrainian translation (`uk`)
|
||||
- Add `book.json` configuration for maximum size of search index
|
||||
- Improve reliability of summary parser
|
||||
|
||||
## 2.4.2
|
||||
- Default plugins should not be installed by `gitbook install`
|
||||
- Limit search index size to avoid crash during generation
|
||||
- Fix code highlighting for html without language specified
|
||||
- Fix warning message for gitbook version when building a multilingual book
|
||||
|
||||
## 2.4.1
|
||||
- Fix disabling of default plugins, ex: `-highlight`
|
||||
|
||||
## 2.4.0
|
||||
- Fix page being updated when user wants to open a link in a new tab
|
||||
- Plugins can now replaced default code highlighter
|
||||
- Add semantic information for screen readers (web version)
|
||||
- Content references accept absolute paths, resolved to book folder
|
||||
- Improve overall reliability
|
||||
|
||||
## 2.3.3
|
||||
- Fix bug in SUMMARY parsing preventing multiple entries without filenames
|
||||
|
||||
## 2.3.2
|
||||
- Fix blocks (like maths) in Asciidoc
|
||||
- Fix error when checking gitbook version
|
||||
|
||||
## 2.3.1
|
||||
- Fix black font color for ebooks (mobi, pdf and epub)
|
||||
- Fix ISO code for korean language
|
||||
- Fix korean translation
|
||||
- Fix syntax highlighting for asciidoc
|
||||
- Fix inline html escaping in markdown
|
||||
- Add warning for file outside SUMMARY
|
||||
- Force SUMMARY entries to be unique by filename
|
||||
|
||||
## 2.3.0
|
||||
- Fix nunjucks issue with multiple `{% raw %}` blocks
|
||||
- Fix crash when git conref failed
|
||||
- Fix crash when failed to download remote image (better error message)
|
||||
- Fix flicking effect when changing page (big UX improvement)
|
||||
- Add Hebrew translation (`he`)
|
||||
- Add utility method `book.config.get` for plugins
|
||||
- Hooks `page:before` and `page` are no longer deprecated
|
||||
- Remove webfonts to make website lighter
|
||||
- Make glossary's order case insensitive
|
||||
|
||||
## 2.2.0
|
||||
- Fix direction in code blocks (always LTR)
|
||||
- Add options `chapterMark` and `pageBreaksBefore` for PDF
|
||||
- Update code highlighting library
|
||||
- `book.json` accessible as `config` in templating syntax
|
||||
- Add Vietnamese translation (`vi`)
|
||||
|
||||
## 2.1.0
|
||||
- Fix error in calcul of `levels` in table of contents, error introduced a few versions ago
|
||||
- Add optional `styles/print.css` to replace `print.css` used in ebook
|
||||
|
||||
## 2.0.4
|
||||
- Fix `{% raw %}`, got confused with "fake" variable declarations
|
||||
- Fix title of language chooser
|
||||
- Fix the X-UA-Compatible meta tag
|
||||
- Move style sheets to the <head> section
|
||||
|
||||
## 2.0.3
|
||||
- Fix `gitbook init` for SUMMARY with empty entries
|
||||
- Fix escaping of code blocks in markdown
|
||||
|
||||
## 2.0.2
|
||||
- Fix relative links in windows
|
||||
- Improve watcher in serve command (switch to chokidar)
|
||||
- Add Romanian translation (`ro`)
|
||||
- Add Finish translation (`fi`)
|
||||
- Add Japanese translation (`jp`)
|
||||
- Add Korean translation (`kr`)
|
||||
|
||||
## 2.0.1
|
||||
- Improve error logging (display file, line and column)
|
||||
- Add back support for `options.originalInput`
|
||||
- Don't process math in markdown parser (delegated to `mathjax` plugin)
|
||||
- Fix some cases of code blocks escaping
|
||||
- Fix i18n for introduction title in json format
|
||||
- Fix reload when book configuration is updated
|
||||
- Fix backslashes in url when building on windows
|
||||
|
||||
## 2.0.0
|
||||
- Fix page title of introduction
|
||||
- Ignore codeblocks when replacing glossary terms
|
||||
- Improve Unit Tests
|
||||
- Fix scrolling position in website when preparing page
|
||||
|
||||
## 2.0.0-beta.5
|
||||
- Fix progress order in json format
|
||||
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
module.exports = function (grunt) {
|
||||
var path = require("path");
|
||||
|
||||
// Load NPM tasks
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
grunt.loadNpmTasks('grunt-contrib-requirejs');
|
||||
grunt.loadNpmTasks("grunt-bower-install-simple");
|
||||
|
||||
// Init GRUNT configuraton
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
'bower-install-simple': {
|
||||
options: {
|
||||
color: true,
|
||||
production: false,
|
||||
directory: "theme/vendors"
|
||||
}
|
||||
},
|
||||
less: {
|
||||
development: {
|
||||
options: {
|
||||
compress: true,
|
||||
yuicompress: true,
|
||||
optimization: 2
|
||||
},
|
||||
files: {
|
||||
"theme/assets/style.css": "theme/stylesheets/website.less",
|
||||
"theme/assets/print.css": "theme/stylesheets/ebook.less"
|
||||
}
|
||||
}
|
||||
},
|
||||
requirejs: {
|
||||
compile: {
|
||||
options: {
|
||||
name: "gitbook",
|
||||
baseUrl: "theme/javascript/",
|
||||
out: "theme/assets/app.js",
|
||||
preserveLicenseComments: false,
|
||||
optimize: "uglify",
|
||||
include: ["requireLib"],
|
||||
paths: {
|
||||
"jQuery": '../vendors/jquery/dist/jquery',
|
||||
"lodash": '../vendors/lodash/dist/lodash',
|
||||
"requireLib": '../vendors/requirejs/require',
|
||||
"Mousetrap": '../vendors/mousetrap/mousetrap',
|
||||
"lunr": '../vendors/lunr.js/lunr',
|
||||
"URIjs": '../vendors/URIjs/src/',
|
||||
"ace": '../vendors/ace-builds/src-noconflict/'
|
||||
},
|
||||
shim: {
|
||||
'jQuery': {
|
||||
exports: '$'
|
||||
},
|
||||
'lodash': {
|
||||
exports: '_'
|
||||
},
|
||||
'Mousetrap': {
|
||||
exports: 'Mousetrap'
|
||||
},
|
||||
'lunr': {
|
||||
exports: 'lunr'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
copy: {
|
||||
vendors: {
|
||||
files: [
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'theme/vendors/fontawesome/fonts/',
|
||||
src: ['**'],
|
||||
dest: 'theme/assets/fonts/fontawesome/',
|
||||
filter: 'isFile'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask("bower-install", [ "bower-install-simple" ]);
|
||||
|
||||
// Build
|
||||
grunt.registerTask('build', [
|
||||
'bower-install',
|
||||
'less',
|
||||
'requirejs',
|
||||
'copy:vendors'
|
||||
]);
|
||||
|
||||
grunt.registerTask('default', [
|
||||
'build'
|
||||
]);
|
||||
};
|
||||
@@ -2,228 +2,50 @@ GitBook
|
||||
=======
|
||||
|
||||
[](http://badge.fury.io/js/gitbook)
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
[](https://ci.appveyor.com/project/SamyPesse/gitbook)
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
[](https://ci.appveyor.com/project/GitBook/gitbook)
|
||||
[](https://slack.gitbook.com)
|
||||
|
||||
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).
|
||||
|
||||
You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a web-editor is [also available](https://www.gitbook.com/editor).
|
||||
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).
|
||||
|
||||
Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
|
||||
Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
|
||||
|
||||
Complete documentation is available at [help.gitbook.com](http://help.gitbook.com/).
|
||||
Complete documentation is available at [toolchain.gitbook.com](http://toolchain.gitbook.com/).
|
||||
|
||||

|
||||
|
||||
## How to use it:
|
||||
## Getting started
|
||||
|
||||
GitBook can be installed from **NPM** using:
|
||||
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).
|
||||
|
||||
```
|
||||
$ npm install gitbook-cli -g
|
||||
```
|
||||
## Usage examples
|
||||
|
||||
Create the directories and files for a book from its [SUMMARY.md](https://github.com/GitbookIO/gitbook#book-format) file (if existing) using
|
||||
GitBook can be used to create book, public documentation, enterprise manual, thesis, research papers, etc.
|
||||
|
||||
```
|
||||
$ gitbook init
|
||||
```
|
||||
You can find a [list of real-world examples](docs/examples.md) in the documentation.
|
||||
|
||||
You can serve a repository as a book using:
|
||||
## Help and Support
|
||||
|
||||
```
|
||||
$ gitbook serve ./repository
|
||||
```
|
||||
|
||||
Or simply build the static website using:
|
||||
|
||||
```
|
||||
$ gitbook build ./repository ./outputFolder
|
||||
```
|
||||
We're always happy to help out with your books or any other questions you might have. You can ask a question on the following contact form at [gitbook.com/contact](https://www.gitbook.com/contact) or signal an issue on [GitHub](https://github.com/GitbookIO/gitbook).
|
||||
|
||||
## Features
|
||||
|
||||
* [Output as a website or ebook (pdf, epub, mobi)](http://help.gitbook.com/format/output.html)
|
||||
* [Multi-Languages](http://help.gitbook.com/format/languages.html)
|
||||
* [Glossary](http://help.gitbook.com/format/glossary.html)
|
||||
* [Cover](http://help.gitbook.com/format/cover.html)
|
||||
* [AsciiDoc Support](http://help.gitbook.com/format/asciidoc.html)
|
||||
* [Variables and Templating](http://help.gitbook.com/format/templating.html)
|
||||
* [Content References](http://help.gitbook.com/format/conrefs.html)
|
||||
* [Plugins](http://help.gitbook.com/plugins/index.html)
|
||||
* Interractive reader website:
|
||||
* Search
|
||||
* Font Settings (Serif, Sans Serif)
|
||||
* Themes: white, sepia, night
|
||||
|
||||
## Output Formats
|
||||
|
||||
GitBook can generate your book in the following formats:
|
||||
|
||||
* **Static Website**: This is the default format. It generates a complete interactive static website that can be, for example, hosted on GitHub Pages.
|
||||
* **eBook**: You need to have [ebook-convert](http://manual.calibre-ebook.com/cli/ebook-convert.html) installed. You can specify the eBook filename as the second argument, otherwise `book` will be used.
|
||||
* Generate a **PDF** using: `gitbook pdf ./myrepo ./mybook.pdf`
|
||||
* Generate a **ePub** using: `gitbook epub ./myrepo ./mybook.epub`
|
||||
* Generate a **MOBI** using: `gitbook mobi ./myrepo ./mybook.mobi`
|
||||
* **JSON**: This format is used for debugging or extracting metadata from a book. Generate this format using: ```gitbook build ./myrepo --format=json```.
|
||||
|
||||
## Book Format
|
||||
|
||||
A book is a Git repository containing at least 2 files: `README.md` and `SUMMARY.md`.
|
||||
|
||||
#### README.md
|
||||
|
||||
Typically, this should be the introduction for your book. It will be automatically added to the final summary.
|
||||
|
||||
#### SUMMARY.md
|
||||
|
||||
The `SUMMARY.md` defines your book's structure. It should contain a list of chapters, linking to their respective pages.
|
||||
|
||||
Example:
|
||||
|
||||
```markdown
|
||||
# Summary
|
||||
|
||||
This is the summary of my book.
|
||||
|
||||
* [section 1](section1/README.md)
|
||||
* [example 1](section1/example1.md)
|
||||
* [example 2](section1/example2.md)
|
||||
* [section 2](section2/README.md)
|
||||
* [example 1](section2/example1.md)
|
||||
```
|
||||
|
||||
Files that are not included in `SUMMARY.md` will not be processed by `gitbook`.
|
||||
|
||||
#### Multi-Languages
|
||||
|
||||
GitBook supports building books written in multiple languages. Each language should be a sub-directory following the normal GitBook format, and a file named `LANGS.md` should be present at the root of the repository with the following format:
|
||||
|
||||
```markdown
|
||||
* [English](en/)
|
||||
* [French](fr/)
|
||||
* [Español](es/)
|
||||
```
|
||||
|
||||
You can see a complete example with the [Learn Git](https://github.com/GitbookIO/git) book.
|
||||
|
||||
#### Glossary
|
||||
|
||||
Allows you to specify terms and their respective definitions to be displayed in the glossary. Based on those terms, `gitbook` will automatically build an index and highlight those terms in pages.
|
||||
|
||||
The `GLOSSARY.md` format is very simple :
|
||||
|
||||
```markdown
|
||||
# term
|
||||
Definition for this term
|
||||
|
||||
# Another term
|
||||
With it's definition, this can contain bold text and all other kinds of inline markup ...
|
||||
```
|
||||
|
||||
#### Variables and Templating
|
||||
|
||||
A set of variables can be defined in the `book.json`:
|
||||
|
||||
```js
|
||||
{
|
||||
"variables": {
|
||||
"host": "mybook.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
These variables can be used in the markdown files:
|
||||
|
||||
```
|
||||
The host is {{ book.host }}
|
||||
```
|
||||
|
||||
You can also use condition with these variables:
|
||||
|
||||
```
|
||||
{% if book.host == "mybook.com" %}
|
||||
|
||||
{% else %}
|
||||
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
Variables of `book.json` are available in the `book` namespace. You can also access informations about the `file` itself and the `gitbook` version:
|
||||
|
||||
```
|
||||
My file is {{ file.path }}
|
||||
Modified at {{ file.mtime }}
|
||||
Book built with GitBook {{ gitbook.version }}
|
||||
```
|
||||
|
||||
#### Content References
|
||||
|
||||
You can use "content references," or conrefs, when writing books or documentation using GitBook.
|
||||
|
||||
Include a file from the same book:
|
||||
|
||||
```
|
||||
{% include "./test.md" %}
|
||||
```
|
||||
|
||||
or from a git repository (with a specific revision):
|
||||
|
||||
```
|
||||
{% include "git+https://github.com/GitbookIO/documentation.git/README.md#1.0.1" %}
|
||||
```
|
||||
|
||||
Includes can be used with variables (see [Variables and Templating](#variables-and-templating)):
|
||||
|
||||
```
|
||||
{% include book.ref_doc_readme %}
|
||||
```
|
||||
|
||||
#### Ignoring files & folders
|
||||
|
||||
GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip. (The format inside those files follows the same convention as `.gitignore`).
|
||||
|
||||
Best practices for the `.gitignore` is to ignore build files from **node.js** (`node_modules`, ...) and build files from GitBook: `_book`, `*.epub`, `*.mobi` and `*.pdf` ([Download GitBook.gitignore](https://github.com/github/gitignore/blob/master/GitBook.gitignore)).
|
||||
|
||||
#### Cover
|
||||
|
||||
A cover image can be set by creating a file: **/cover.jpg**.
|
||||
The best resolution is **1800x2360**. The generation of the cover can be done automatically using the plugin [autocover](https://github.com/GitbookIO/plugin-autocover).
|
||||
|
||||
A small version of the cover can also be set by creating a file: **/cover_small.jpg**.
|
||||
|
||||
#### AsciiDoc
|
||||
|
||||
Since version 2.0.0, AsciiDoc can be used instead of Markdown, simply replace the `.md` by the `.adoc` extension. Chapters in the summary are detected from an ordered list in the `SUMMARY.adoc`.
|
||||
* Write using [Markdown](http://toolchain.gitbook.com/syntax/markdown.html) or [AsciiDoc](http://toolchain.gitbook.com/syntax/asciidoc.html)
|
||||
* Output as a website or [ebook (pdf, epub, mobi)](http://toolchain.gitbook.com/ebook.html)
|
||||
* [Multi-Languages](http://toolchain.gitbook.com/languages.html)
|
||||
* [Lexicon / Glossary](http://toolchain.gitbook.com/lexicon.html)
|
||||
* [Cover](http://toolchain.gitbook.com/ebook.html)
|
||||
* [Variables and Templating](http://toolchain.gitbook.com/templating/)
|
||||
* [Content References](http://toolchain.gitbook.com/templating/conrefs.html)
|
||||
* [Plugins](http://toolchain.gitbook.com/plugins/)
|
||||
* [Beautiful default theme](https://github.com/GitbookIO/theme-default)
|
||||
|
||||
## Publish your book
|
||||
|
||||
The platform [GitBook.com](https://www.gitbook.com/) is like an "Heroku for books": you can create a book on it (public, paid, or private) and update it using **git push**.
|
||||
The platform [GitBook.com](https://www.gitbook.com/) is like an "Heroku for books": you can create a book on it (public, or private) and update it using **git push**.
|
||||
|
||||
## Plugins
|
||||
## Licensing
|
||||
|
||||
Plugins can be used to extend your book's functionality. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more information about how to build a plugin for GitBook.
|
||||
|
||||
Plugins needed to build a book can be installed using: `gitbook install ./`. You can find plugins at [plugins.gitbook.com](http://plugins.gitbook.com).
|
||||
|
||||
|
||||
## Debugging
|
||||
|
||||
You can use the options `--log=debug` and `--debug` to get better error messages (with stack trace). For example:
|
||||
|
||||
```
|
||||
$ gitbook build ./ -- log=debug --debug
|
||||
```
|
||||
|
||||
#### How to use the latest commit from GitBook in gitbook-cli
|
||||
|
||||
To use the latest commit from `GitBook/gitbook` with `gitbook-cli`:
|
||||
|
||||
```
|
||||
$ git clone https://github.com/GitbookIO/gitbook.git ./gitbook
|
||||
$ gitbook versions:link 2.0.0 ./gitbook
|
||||
```
|
||||
|
||||
Now the version tag `2.0.0` wil be associated with the `./gitbook` folder.
|
||||
|
||||
You can uninstall it using: `gitbook versions:uninstall 2.0.0`.
|
||||
GitBook is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full license text.
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ init:
|
||||
# Test against these versions of Node.js.
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "0.10"
|
||||
- nodejs_version: "0.11"
|
||||
- nodejs_version: "5"
|
||||
- nodejs_version: "4"
|
||||
|
||||
# Install scripts. (runs after repo cloning)
|
||||
install:
|
||||
|
||||
+4
-3
@@ -1,7 +1,8 @@
|
||||
#! /usr/bin/env node
|
||||
/* eslint-disable no-console */
|
||||
|
||||
var color = require('bash-color');
|
||||
|
||||
console.log(color.red("You need to install 'gitbook-cli' to have access to the gitbook command anywhere on your system."));
|
||||
console.log(color.red("If you've installed this package globally, you need to uninstall it."));
|
||||
console.log(color.red(">> Run 'npm uninstall -g gitbook' then 'npm install -g gitbook-cli'"));
|
||||
console.log(color.red('You need to install "gitbook-cli" to have access to the gitbook command anywhere on your system.'));
|
||||
console.log(color.red('If you\'ve installed this package globally, you need to uninstall it.'));
|
||||
console.log(color.red('>> Run "npm uninstall -g gitbook" then "npm install -g gitbook-cli"'));
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
var pkg = require('./package.json');
|
||||
|
||||
module.exports = {
|
||||
// Documentation for GitBook is stored under "docs"
|
||||
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'],
|
||||
|
||||
variables: {
|
||||
version: pkg.version
|
||||
},
|
||||
|
||||
pluginsConfig: {
|
||||
sitemap: {
|
||||
hostname: 'https://toolchain.gitbook.com'
|
||||
}
|
||||
}
|
||||
};
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"name": "GitBook",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"lodash": "2.4.1",
|
||||
"requirejs": "2.1.11",
|
||||
"URIjs": "1.13.1",
|
||||
"mousetrap": "1.4.6",
|
||||
"lunr.js": "0.5.2",
|
||||
"fontawesome": "4.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# GitBook Toolchain Documentation
|
||||
|
||||
This document aims to be a comprehensive guide to GitBook. It contains the full documentation for version **{{ book.version }}**. Help for GitBook.com specific questions can be found at [help.gitbook.com](https://help.gitbook.com).
|
||||
|
||||
### 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 can output your content as a website ([customizable](themes/README.md) and [extensibles](plugins/README.md)) or as an ebook (PDF, ePub or Mobi).
|
||||
|
||||
[GitBook.com](https://www.gitbook.com) is the online platform to create and host books built using the GitBook format. It offers hosting, collaboration features and an [easy-to-use editor](https://www.gitbook.com/editor).
|
||||
|
||||
### Help and Support
|
||||
|
||||
We're always happy to help out with your books or any other questions you might have. You can ask a question on the following contact form at [gitbook.com/contact](https://www.gitbook.com/contact) or signal an issue on [GitHub](https://github.com/GitbookIO/gitbook).
|
||||
|
||||
Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
|
||||
|
||||
### FAQ
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Summary
|
||||
|
||||
### Getting Started
|
||||
|
||||
* [About this documentation](README.md)
|
||||
* [Installation and Setup](setup.md)
|
||||
|
||||
### Your Content
|
||||
|
||||
* [Directory structure](structure.md)
|
||||
* [Pages and Summary](pages.md)
|
||||
* [Configuration](config.md)
|
||||
* [Glossary](lexicon.md)
|
||||
* [Multi-Lingual](languages.md)
|
||||
* [Markdown](syntax/markdown.md)
|
||||
* [Headings](syntax/markdown.md#headings)
|
||||
* [Paragraphs](syntax/markdown.md#paragraphs)
|
||||
* [Lists](syntax/markdown.md#lists)
|
||||
* [Links](syntax/markdown.md#links)
|
||||
* [Images](syntax/markdown.md#images)
|
||||
* [Blockquotes](syntax/markdown.md#blockquotes)
|
||||
* [Tables](syntax/markdown.md#tables)
|
||||
* [Code](syntax/markdown.md#code)
|
||||
* [HTML](syntax/markdown.md#html)
|
||||
* [Footnotes](syntax/markdown.md#footnotes)
|
||||
* [AsciiDoc](syntax/asciidoc.md)
|
||||
* [eBook and PDF](ebook.md)
|
||||
|
||||
### Customization
|
||||
|
||||
* [Templating](templating/README.md)
|
||||
* [Content References](templating/conrefs.md)
|
||||
* [Variables](templating/variables.md)
|
||||
* [Builtin](templating/builtin.md)
|
||||
* [Plugins](plugins/README.md)
|
||||
* [Create a plugin](plugins/create.md)
|
||||
* [Hooks](plugins/hooks.md)
|
||||
* [Blocks](plugins/blocks.md)
|
||||
* [Filters](plugins/filters.md)
|
||||
* [API & Context](plugins/api.md)
|
||||
* [Test your plugin](plugins/testing.md)
|
||||
* [Theming](themes/README.md)
|
||||
|
||||
--
|
||||
|
||||
* [FAQ](faq.md)
|
||||
* [Examples](examples.md)
|
||||
* [Release notes](https://github.com/GitbookIO/gitbook/blob/master/CHANGES.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{% 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,48 @@
|
||||
# Configuration
|
||||
|
||||
GitBook allows you to customize your book using a flexible configuration. These options are specified in a `book.json` file. For authors unfamiliar with the JSON syntax, you can validate the syntax using tools such as [JSONlint](http://jsonlint.com).
|
||||
|
||||
### General Settings
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `root` | Path to the root folder containing all the book's files, except `book.json`|
|
||||
| `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. |
|
||||
| `isbn` | ISBN of the book |
|
||||
| `language` | [ISO code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) of the book's language, default value is `en` |
|
||||
| `direction` | Text's direction. Can be `rtl` or `ltr`, the default value depends on the value of `language` |
|
||||
| `gitbook` | Version of GitBook that should be used. Uses the [SemVer](http://semver.org) specification and accepts conditions like `">= 3.0.0"` |
|
||||
|
||||
### Plugins
|
||||
|
||||
Plugins and their configurations are specified in the `book.json`. See [the plugins section](plugins/README.md) for more details.
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `plugins` | List of plugins to load |
|
||||
| `pluginsConfig` |Configuration for plugins |
|
||||
|
||||
### Theme
|
||||
|
||||
Since version 3.0.0, GitBook can use themes. See [the theming section](themes/README.md) for more details.
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `theme` | The theme to use for the book |
|
||||
|
||||
### PDF Options
|
||||
|
||||
PDF Output can be customized using a set of options in the `book.json`:
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `pdf.pageNumbers` | Add page numbers to the bottom of every page (default is `true`) |
|
||||
| `pdf.fontSize` | Base font size (default is `12`) |
|
||||
| `pdf.fontFamily` | Base font family (default is `Arial`) |
|
||||
| `pdf.paperSize` | Paper size, options are `'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'legal', 'letter'` (default is `a4`) |
|
||||
| `pdf.margin.top` | Top margin (default is `56`) |
|
||||
| `pdf.margin.bottom` | Bottom margin (default is `56`) |
|
||||
| `pdf.margin.right` | Right margin (default is `62`) |
|
||||
| `pdf.margin.left` | Left margin (default is `62`) |
|
||||
@@ -0,0 +1,40 @@
|
||||
# Generating eBooks and PDFs
|
||||
|
||||
GitBook can generates a website, but can also output content as ebook (ePub, Mobi, PDF).
|
||||
|
||||
```
|
||||
# Generate a PDF file
|
||||
$ gitbook pdf ./ ./mybook.pdf
|
||||
|
||||
# Generate an ePub file
|
||||
$ gitbook epub ./ ./mybook.epub
|
||||
|
||||
# Generate a Mobi file
|
||||
$ gitbook mobi ./ ./mybook.mobi
|
||||
```
|
||||
|
||||
### Installing ebook-convert
|
||||
|
||||
`ebook-convert` is required to generate ebooks (epub, mobi, pdf).
|
||||
|
||||
##### 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:
|
||||
|
||||
```
|
||||
$ sudo ln -s ~/Applications/calibre.app/Contents/MacOS/ebook-convert /usr/bin
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
A good cover should respect the following guidelines:
|
||||
|
||||
* Size of 1800x2360 (pixels)
|
||||
* No border
|
||||
* Clearly visible book title
|
||||
* Any important text should be visible in the small version
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
description: Real world examples of content published using GitBook.
|
||||
---
|
||||
|
||||
# Examples
|
||||
|
||||
More than 50,000 books have been published on [GitBook.com](https://www.gitbook.com/explore).
|
||||
|
||||
### Books
|
||||
|
||||
- [Front-end Handbook](https://www.gitbook.com/book/frontendmasters/front-end-handbook/details) by [Cody Lindley](http://codylindley.com)
|
||||
- [How to make an Operating System](https://www.gitbook.com/book/samypesse/how-to-create-an-operating-system/details) by [@SamyPesse](https://github.com/SamyPesse)
|
||||
- [Building Web Apps with Go](https://www.gitbook.com/book/codegangsta/building-web-apps-with-go/details) by [@codegangsta](https://github.com/codegangsta)
|
||||
- [Django Girls Tutorial](http://tutorial.djangogirls.org/en/index.html) by [Django Girls](https://djangogirls.org)
|
||||
- [Linux Inside](https://www.gitbook.com/book/0xax/linux-insides/details) by [0xAX](https://twitter.com/0xAX)
|
||||
- [Learn Javascript](https://www.gitbook.com/book/gitbookio/javascript/details) by [GitBook](https://twitter.com/GitbookIO)
|
||||
|
||||
### Research Papers
|
||||
|
||||
- [TowCenter Collection](https://www.gitbook.com/@towcenter) by [Columbia Journalism School](http://www.journalism.columbia.edu/)
|
||||
- [Block Relaxation Algorithms in Statistics](https://www.gitbook.com/@jandeleeuw) by Jan de Leeuw
|
||||
|
||||
### Documentation
|
||||
|
||||
- [DuckDuckHack Documentation](http://docs.duckduckhack.com) by [DuckDuckGo](https://duckduckgo.com/about)
|
||||
- This documentation
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
# GitBook FAQ
|
||||
|
||||
This page gathers common questions and answers concerning the GitBook format and toolchain.
|
||||
|
||||
Questions about GitBook.com and the Editor are gather into the [help.gitbook.com's FAQ](http://help.gitbook.com/faq.html).
|
||||
|
||||
#### How can I host/publish my book?
|
||||
|
||||
Books can easily be published and hosted on [GitBook.com](https://www.gitbook.com). But GitBook output can be hosted on any static file hosting solution.
|
||||
|
||||
#### What can I use to edit my content?
|
||||
|
||||
Any text editor should work! But we advise using the [GitBook Editor](https://www.gitbook.com/editor). [GitBook.com](https://www.gitbook.com) also provides a web version of this editor.
|
||||
|
||||
---
|
||||
|
||||
#### Does GitBook supports RTL/bi-directional text ?
|
||||
|
||||
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
|
||||
{
|
||||
"language": "ar",
|
||||
"direction": "rtl"
|
||||
}
|
||||
```
|
||||
|
||||
With version 3.0 of GitBook, it's automatically detected according to the content.
|
||||
_Note that, while the output book will indeed respect RTL, the Editor doesn't support RTL writing yet_.
|
||||
|
||||
#### Should I use an `.html` or `.md` extensions in my links?
|
||||
|
||||
You should always use paths and the `.md` extensions when linking to your files, GitBook will automatically replace these paths by the appropriate link when the pointing file is referenced in the Table of Contents.
|
||||
|
||||
#### 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).
|
||||
|
||||
#### Does GitBook supports RTL languages?
|
||||
|
||||
Yes, GitBook automatically detect the direction in your pages (`rtl` or `ltr`) and adjust the layout accordingly. The direction can also be specified globally in the [book.json](config.md).
|
||||
|
||||
---
|
||||
|
||||
#### Does GitBook support Math equations?
|
||||
|
||||
GitBook supports math equations and TeX thanks to plugins. There are currently 2 official plugins to display math: [mathjax](https://plugins.gitbook.com/plugin/mathjax) and [katex](https://plugins.gitbook.com/plugin/katex).
|
||||
|
||||
#### Can I customize/theme the output?
|
||||
|
||||
Yes, both the website and ebook outputs can be customized using [themes](themes/README.md).
|
||||
|
||||
#### Can I add interactive content (videos, etc)?
|
||||
|
||||
GitBook is very [extensible](plugins/README.md). You can use [existing plugins](https://plugins.gitbook.com) or create your own!
|
||||
@@ -0,0 +1,17 @@
|
||||
# Multi-Languages
|
||||
|
||||
GitBook supports building books written in multiple languages. Each language should be a sub-directory following the normal GitBook format, and a file named `LANGS.md` should be present at the root of the repository with the following format:
|
||||
|
||||
```markdown
|
||||
# Languages
|
||||
|
||||
* [English](en/)
|
||||
* [French](fr/)
|
||||
* [Español](es/)
|
||||
```
|
||||
|
||||
### Configuration for each language
|
||||
|
||||
When a language book (ex: `en`) has a `book.json`, its configuration will extend the main configuration.
|
||||
|
||||
The only exception is plugins, plugins are specified globally, and language specific plugins cannot be specified.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Glossary
|
||||
|
||||
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 :
|
||||
|
||||
```markdown
|
||||
# Term
|
||||
Definition for this term
|
||||
|
||||
# Another term
|
||||
With it's definition, this can contain bold text
|
||||
and all other kinds of inline markup ...
|
||||
```
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
# Pages and Summary
|
||||
|
||||
### Summary
|
||||
|
||||
GitBook uses a `SUMMARY.md` file to define the structure of chapters and subchapters of the book. The `SUMMARY.md` file is used to generate the book's table of contents.
|
||||
|
||||
The format of `SUMMARY.md` is just a list of links. The link's title is used as the chapter's title, and the link's target is a path to that chapter's file.
|
||||
|
||||
Adding a nested list to a parent chapter will create subchapters.
|
||||
|
||||
##### Simple example
|
||||
|
||||
```markdown
|
||||
# Summary
|
||||
|
||||
* [Part I](part1/README.md)
|
||||
* [Writing is nice](part1/writing.md)
|
||||
* [GitBook is nice](part1/gitbook.md)
|
||||
* [Part II](part2/README.md)
|
||||
* [We love feedback](part2/feedback_please.md)
|
||||
* [Better tools for authors](part2/better_tools.md)
|
||||
```
|
||||
|
||||
Each chapter has a dedicated page (`part#/README.md`) and is split into subchapters.
|
||||
|
||||
##### Anchors
|
||||
|
||||
Chapters in the Table of Contents can be pointing to specific part of a file using anchor.
|
||||
|
||||
```markdown
|
||||
# Summary
|
||||
|
||||
### Part I
|
||||
|
||||
* [Part I](part1/README.md)
|
||||
* [Writing is nice](part1/README.md#writing)
|
||||
* [GitBook is nice](part1/README.md#gitbook)
|
||||
* [Part II](part2/README.md)
|
||||
* [We love feedback](part2/README.md#feedback)
|
||||
* [Better tools for authors](part2/README.md#tools)
|
||||
```
|
||||
|
||||
|
||||
##### Parts
|
||||
|
||||
The Table of Contents can be divided into parts separated by headings or horizontal lines:
|
||||
|
||||
```markdown
|
||||
# Summary
|
||||
|
||||
### Part I
|
||||
|
||||
* [Writing is nice](part1/writing.md)
|
||||
* [GitBook is nice](part1/gitbook.md)
|
||||
|
||||
### Part II
|
||||
|
||||
* [We love feedback](part2/feedback_please.md)
|
||||
* [Better tools for authors](part2/better_tools.md)
|
||||
|
||||
----
|
||||
|
||||
* [Last part without title](part3/title.md)
|
||||
```
|
||||
|
||||
Parts are just groups of chapters and do not have dedicated pages, but according to the theme, it will show in the navigation.
|
||||
|
||||
### Pages
|
||||
|
||||
#### Markdown syntax
|
||||
|
||||
Most of the files for GitBook use the Markdown syntax by default. GitBook infers your pages's structure from it. The syntax used is similar to the [GitHub Flavored Markdown syntax](https://guides.github.com/features/mastering-markdown/). One can also opt for the [AsciiDoc syntax](asciidoc.md).
|
||||
|
||||
##### Example of a chapter file
|
||||
|
||||
``` markdown
|
||||
# Title of the chapter
|
||||
|
||||
This is a great introduction.
|
||||
|
||||
## Section 1
|
||||
|
||||
Markdown will dictates _most_ of your **book's structure**
|
||||
|
||||
## Section 2
|
||||
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
#### Front Matter
|
||||
|
||||
Pages can contain an optional front matter. It can be used to define the page's description. The front matter must be the first thing in the file and must take the form of valid YAML set between triple-dashed lines. Here is a basic example:
|
||||
|
||||
```yaml
|
||||
---
|
||||
description: This is a short description of my page
|
||||
---
|
||||
|
||||
# The content of my page
|
||||
...
|
||||
```
|
||||
|
||||
The front matter can define variables of your own, they will be added to the [page variable](templating/variables.md) so you can use them in your templating.
|
||||
@@ -0,0 +1,28 @@
|
||||
# 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.
|
||||
|
||||
### How to find plugins?
|
||||
|
||||
Plugins can be easily searched on [plugins.gitbook.com](https://plugins.gitbook.com).
|
||||
|
||||
|
||||
### How to install a plugin?
|
||||
|
||||
Once you find a plugin that you want to install, you need to add it to your `book.json`:
|
||||
|
||||
```
|
||||
{
|
||||
"plugins": ["myPlugin", "anotherPlugin"]
|
||||
}
|
||||
```
|
||||
|
||||
You can also specify a specific version using: `"myPlugin@0.3.1"`. By default GitBook will resolve the latest version of the plugin compatbile with the current GitBook version.
|
||||
|
||||
### GitBook.com
|
||||
|
||||
Plugins are automatically installed on [GitBook.com](https://www.gitbook.com). Locally, run `gitbook install` to install and prepare all plugins for your books.
|
||||
|
||||
### Configuring plugins
|
||||
|
||||
Plugins specific configurations are stored in `pluginsConfig`. You have to refer to the documentation of the plugin itself for details about the available options.
|
||||
@@ -0,0 +1,97 @@
|
||||
# Context and APIs
|
||||
|
||||
GitBooks provides different 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` 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).
|
||||
|
||||
```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.write('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`.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Extend Blocks
|
||||
|
||||
Extending templating blocks is the best way to provide extra functionalities to authors.
|
||||
|
||||
The most common usage is to process the content within some tags at runtime. It's like [filters](./filters.md), but on steroids because you aren't confined to a single expression.
|
||||
|
||||
### Defining a new block
|
||||
|
||||
Blocks are defined by the plugin, blocks is a map of name associated with a block descriptor. The block descriptor needs to contain at least a `process` method.
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
blocks: {
|
||||
tag1: {
|
||||
process: function(block) {
|
||||
return "Hello "+block.body+", How are you?";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
The `process` should return the html content that will replace the tag. Refer to [Context and APIs](./api.md) to learn more about `this` and GitBook API.
|
||||
|
||||
### Handling block arguments
|
||||
|
||||
Arguments can be passed to blocks:
|
||||
|
||||
```
|
||||
{% tag1 "argument 1", "argument 2", name="Test" %}
|
||||
This is the body of the block.
|
||||
{% endtag1 %}
|
||||
```
|
||||
|
||||
And arguments are easily accessible in the `process` method:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
blocks: {
|
||||
tag1: {
|
||||
process: function(block) {
|
||||
// block.args equals ["argument 1", "argument 2"]
|
||||
// block.kwargs equals { "name": "Test" }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Handling sub-blocks
|
||||
|
||||
A defined block can be parsed into different sub-blocks, for example let's consider the source:
|
||||
|
||||
```
|
||||
{% myTag %}
|
||||
Main body
|
||||
{% subblock1 %}
|
||||
Body of sub-block 1
|
||||
{% subblock 2 %}
|
||||
Body of sub-block 1
|
||||
{% endmyTag %}
|
||||
```
|
||||
@@ -0,0 +1,74 @@
|
||||
# Create and publish a plugin
|
||||
|
||||
A GitBook plugin is a node package published on NPM that follow a defined convention.
|
||||
|
||||
## Structure
|
||||
|
||||
#### package.json
|
||||
|
||||
The `package.json` is a manifest format for describing **Node.js modules**. GitBook plugins are built on top of Node modules. It declares dependencies, version, ownership, and other information required to run a plugin in GitBook. This document describes the schema in detail.
|
||||
|
||||
A plugin manifest `package.json` can also contain details about the required configuration. The configuration schema is defined in the `gitbook` field of the `package.json` (This field follow the [JSON-Schema](http://json-schema.org) guidelines):
|
||||
|
||||
```js
|
||||
{
|
||||
"name": "gitbook-plugin-mytest",
|
||||
"version": "0.0.1",
|
||||
"description": "This is my first GitBook plugin",
|
||||
"engines": {
|
||||
"gitbook": ">1.x.x"
|
||||
},
|
||||
"gitbook": {
|
||||
"properties": {
|
||||
"myConfigKey": {
|
||||
"type": "string",
|
||||
"default": "it's the default value",
|
||||
"description": "It defines my awesome config!"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can learn more about `package.json` from the [NPM documentation](https://docs.npmjs.com/files/package.json).
|
||||
|
||||
The **package name** must begin with `gitbook-plugin-` and the **package engines** should contains `gitbook`.
|
||||
|
||||
#### index.js
|
||||
|
||||
The `index.js` is main entry point of your plugin runtime:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
// Map of hooks
|
||||
hooks: {},
|
||||
|
||||
// Map of new blocks
|
||||
blocks: {},
|
||||
|
||||
// Map of new filters
|
||||
filters: {}
|
||||
};
|
||||
```
|
||||
|
||||
## Publish your plugin
|
||||
|
||||
GitBook plugins can be published on [NPM](https://www.npmjs.com).
|
||||
|
||||
To publish a new plugin, you need to create an account on [npmjs.com](https://www.npmjs.com) then publish it from the command line:
|
||||
|
||||
```
|
||||
$ npm publish
|
||||
```
|
||||
|
||||
## Private plugins
|
||||
|
||||
Private plugins can be hosted on GitHub and included using `git` urls:
|
||||
|
||||
```
|
||||
{
|
||||
"plugins": [
|
||||
"myplugin@git+https://github.com/MyCompany/mygitbookplugin.git#1.0.0"
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,57 @@
|
||||
# Extend Filters
|
||||
|
||||
Filters are essentially functions that can be applied to variables. They are called with a pipe operator (`|`) and can take arguments.
|
||||
|
||||
```
|
||||
{{ foo | title }}
|
||||
{{ foo | join(",") }}
|
||||
{{ foo | replace("foo", "bar") | capitalize }}
|
||||
```
|
||||
|
||||
### Defining a new filter
|
||||
|
||||
Plugins can extend filters by defining custom functions in their entry point under the `filters` scope.
|
||||
|
||||
A filter function takes as first argument the content to filter, and should return the new content.
|
||||
Refer to [Context and APIs](./api.md) to learn more about `this` and GitBook API.
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
filters: {
|
||||
hello: function(name) {
|
||||
return 'Hello '+name;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
The filter `hello` can then be used in the book:
|
||||
|
||||
```
|
||||
{{ "Aaron"|hello }}, how are you?
|
||||
```
|
||||
|
||||
### Handling block arguments
|
||||
|
||||
Arguments can be passed to filters:
|
||||
|
||||
```
|
||||
Hello {{ "Samy"|fullName("Pesse", man=true}} }}
|
||||
```
|
||||
|
||||
Arguments are passed to the function, named-arguments are passed as a last argument (object).
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
filters: {
|
||||
fullName: function(firstName, lastName, kwargs) {
|
||||
var name = firstName + ' ' + lastName;
|
||||
|
||||
if (kwargs.man) name = "Mr" + name;
|
||||
else name = "Mrs" + name;
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,90 @@
|
||||
# Hooks
|
||||
|
||||
Hooks is a method of augmenting or altering the behavior of the process, with custom callbacks.
|
||||
|
||||
### List of hooks
|
||||
|
||||
### Relative to the global pipeline
|
||||
|
||||
| Name | Description | Arguments |
|
||||
| ---- | ----------- | --------- |
|
||||
| `init` | Called after parsing the book, before generating output and pages. | None |
|
||||
| `finish:before` | Called after generating the pages, before copying assets, cover, ... | None |
|
||||
| `finish` | Called after everything else. | None |
|
||||
|
||||
### Relative to the page pipeline
|
||||
|
||||
> It is recommended using [templating](./templating.md) to extend page parsing.
|
||||
|
||||
| Name | Description | Arguments |
|
||||
| ---- | ----------- | --------- |
|
||||
| `page:before` | Called before running the templating engine on the page | Page Object |
|
||||
| `page` | Called before outputting and indexing the page. | Page Object |
|
||||
|
||||
##### Page Object
|
||||
|
||||
```js
|
||||
{
|
||||
// Parser named
|
||||
"type": "markdown",
|
||||
|
||||
// File Path relative to book root
|
||||
"path": "page.md",
|
||||
|
||||
// Absolute file path
|
||||
"rawpath": "/usr/...",
|
||||
|
||||
// Title of the page in the SUMMARY
|
||||
"title": "",
|
||||
|
||||
// Content of the page
|
||||
// Markdown/Asciidoc in "page:before"
|
||||
// HTML in "page"
|
||||
"content": "# Hello"
|
||||
}
|
||||
```
|
||||
|
||||
##### Example to add a title
|
||||
|
||||
In the `page:before` hook, `page.content` is the markdown/asciidoc content.
|
||||
|
||||
```js
|
||||
{
|
||||
"page:before": function(page) {
|
||||
page.content = "# Title\n" +page.content;
|
||||
return page;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Example to replace some html
|
||||
|
||||
In the `page` hook, `page.content` is the HTML generated from the markdown/asciidoc conversion.
|
||||
|
||||
```js
|
||||
{
|
||||
"page": function(page) {
|
||||
page.content = page.content.replace("<b>", "<strong>")
|
||||
.replace("</b>", "</strong>");
|
||||
return page;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Asynchronous Operations
|
||||
|
||||
Hooks callbacks can be asynchronous and return promises.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
{
|
||||
"init": function() {
|
||||
return writeSomeFile()
|
||||
.then(function() {
|
||||
return writeAnotherFile();
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,22 @@
|
||||
# Testing your plugin
|
||||
|
||||
### Testing your plugin locally
|
||||
|
||||
Testing your plugin on your book before plushing it is possible using [npm link](https://docs.npmjs.com/cli/link).
|
||||
|
||||
In the plugin's folder, run:
|
||||
|
||||
```
|
||||
$ npm link
|
||||
```
|
||||
|
||||
The nin your book's folder:
|
||||
|
||||
```
|
||||
$ 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.
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Setup and Installation of GitBook
|
||||
|
||||
Getting GitBook installed and ready-to-go should only take a few minutes.
|
||||
|
||||
### GitBook.com
|
||||
|
||||
[GitBook.com](https://www.gitbook.com) is an easy to use solution to write, publish and host books. It is the easiest solution for publishing your content and collaborating on it.
|
||||
|
||||
It integrates well with the [GitBook Editor](https://www.gitbook.com/editor).
|
||||
|
||||
### Local Installation
|
||||
|
||||
##### Requirements
|
||||
|
||||
Installing GitBook is easy and straightforward. Your system just needs to meet these two requirements:
|
||||
|
||||
* NodeJS (v4.0.0 and above are adviced)
|
||||
* Windows, Linux, Unix, or Mac OS X
|
||||
|
||||
##### Install with NPM
|
||||
|
||||
The best way to install GitBook is via **NPM**. At the terminal prompt, simply run the following command to install GitBook:
|
||||
|
||||
```
|
||||
$ npm install gitbook-cli -g
|
||||
```
|
||||
|
||||
`gitbook-cli` is an utility to install and use multiple versions of GitBook on the same system. It will automatically install the required version of GitBook to build a book.
|
||||
|
||||
##### Create a book
|
||||
|
||||
GitBook can setup a boilerplate book:
|
||||
|
||||
```
|
||||
$ gitbook init
|
||||
```
|
||||
|
||||
If you wish to create the book into a new directory, you can do so by running `gitbook init ./directory`
|
||||
|
||||
Preview and serve your book using:
|
||||
|
||||
```
|
||||
$ gitbook serve
|
||||
```
|
||||
|
||||
Or build the static website using:
|
||||
|
||||
```
|
||||
$ gitbook build
|
||||
```
|
||||
|
||||
##### Install pre-releases
|
||||
|
||||
`gitbook-cli` makes it easy to download and install other versions of GitBook to test with your book:
|
||||
|
||||
```
|
||||
$ gitbook fetch beta
|
||||
```
|
||||
|
||||
Use `gitbook ls-remote` to list remote versions available for install.
|
||||
|
||||
##### Debugging
|
||||
|
||||
You can use the options `--log=debug` and `--debug` to get better error messages (with stack trace). For example:
|
||||
|
||||
```
|
||||
$ gitbook build ./ --log=debug --debug
|
||||
```
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# Directory Structure
|
||||
|
||||
GitBook uses a simple directory structure. All Markdown/Asciidoc files listed in the [SUMMARY](pages.md) will be transformed as HTML. Multi-Lingual books have a slightly [different structure](languages.md).
|
||||
|
||||
A basic GitBook usually looks something like this:
|
||||
|
||||
```
|
||||
.
|
||||
├── book.json
|
||||
├── README.md
|
||||
├── SUMMARY.md
|
||||
├── chapter-1/
|
||||
| ├── README.md
|
||||
| └── something.md
|
||||
└── chapter-2/
|
||||
├── README.md
|
||||
└── something.md
|
||||
```
|
||||
|
||||
An overview of what each of these does:
|
||||
|
||||
| File | Description |
|
||||
| -------- | ----------- |
|
||||
| `book.json` | Stores [configuration](config.md) data (__optional__) |
|
||||
| `README.md` | Preface / Introduction for your book (**required**) |
|
||||
| `SUMMARY.md` | Table of Contents (See [Pages](pages.md)) (__optional__) |
|
||||
| `GLOSSARY.md` | Lexicon / List of terms to annotate (See [Glossary](lexicon.md)) (__optional__) |
|
||||
|
||||
### Static files and Images
|
||||
|
||||
A static file is a file that is not listed in the `SUMMARY.md`. All static files, unless [ignored](#ignore), are copied to the output.
|
||||
|
||||
### Ignoring files & folders {#ignore}
|
||||
|
||||
GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip.
|
||||
The format inside those files, follows the same convention as `.gitignore`:
|
||||
|
||||
```
|
||||
# This is a comment
|
||||
|
||||
# Ignore the file test.md
|
||||
test.md
|
||||
|
||||
# Ignore everything in the directory "bin"
|
||||
bin/*
|
||||
```
|
||||
|
||||
### Project integration with subdirectory {#subdirectory}
|
||||
|
||||
For software projects, you can use a subdirectory (like `docs/`) to store the book for the project's documentation. You can configure the [`root` option](config.md) to indicate the folder where GitBook can find the book's files:
|
||||
|
||||
```
|
||||
.
|
||||
├── book.json
|
||||
└── docs/
|
||||
├── README.md
|
||||
└── SUMMARY.md
|
||||
```
|
||||
|
||||
With `book.json` containing:
|
||||
|
||||
```
|
||||
{
|
||||
"root": "./docs"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
# AsciiDoc
|
||||
|
||||
Since version `2.0.0`, GitBook can also accept AsciiDoc as an input format.
|
||||
|
||||
Please refer to the [AsciiDoc Syntax Quick Reference](http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/) for more informations about the format.
|
||||
|
||||
Just like for markdown, GitBook is using some special files to extract structures: `README.adoc`, `SUMMARY.adoc`, `LANGS.adoc` and `GLOSSARY.adoc`.
|
||||
|
||||
### README.adoc
|
||||
|
||||
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.
|
||||
|
||||
Subchapters are defined simply by adding a nested list to a parent chapter.
|
||||
|
||||
```asciidoc
|
||||
= Summary
|
||||
|
||||
. link:chapter-1/README.adoc[Chapter 1]
|
||||
.. link:chapter-1/ARTICLE1.adoc[Article 1]
|
||||
.. link:chapter-1/ARTICLE2.adoc[Article 2]
|
||||
... link:chapter-1/ARTICLE-1-2-1.adoc[Article 1.2.1]
|
||||
. link:chapter-2/README.adoc[Chapter 2]
|
||||
. link:chapter-3/README.adoc[Chapter 3]
|
||||
. link:chapter-4/README.adoc[Chapter 4]
|
||||
.. Unfinished article
|
||||
. Unfinished Chapter
|
||||
```
|
||||
|
||||
### LANGS.adoc
|
||||
|
||||
For [Multi-Languages](./languages.md) books, this file is used to define the different supported languages and translations.
|
||||
|
||||
This file is following the same syntax as the `SUMMARY.adoc`:
|
||||
|
||||
```asciidoc
|
||||
= Languages
|
||||
|
||||
. link:en/[English]
|
||||
. link:fr/[French]
|
||||
```
|
||||
|
||||
### GLOSSARY.adoc
|
||||
|
||||
This file is used to define terms. [See the glossary section](./lexicon.md).
|
||||
|
||||
```asciidoc
|
||||
= Glossary
|
||||
|
||||
== Magic
|
||||
|
||||
Sufficiently advanced technology, beyond the understanding of the
|
||||
observer producing a sense of wonder.
|
||||
|
||||
== PHP
|
||||
|
||||
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"). ```
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
# Markdown
|
||||
|
||||
Most of the examples from this documentation are in Markdown. Markdown is default parser for GitBook, but one can also opt for the [AsciiDoc syntax](asciidoc.md).
|
||||
|
||||
Here’s an overview of Markdown syntax that you can use with GitBook (same as GitHub with some additions).
|
||||
|
||||
### Headings
|
||||
|
||||
To create a heading, add one to six `#` symbols before your heading text. The number of # you use will determine the size of the heading.
|
||||
|
||||
```markdown
|
||||
# This is an <h1> tag
|
||||
## This is an <h2> tag
|
||||
###### This is an <h6> tag
|
||||
```
|
||||
|
||||
GitBook supports a nice way for explicitly setting the header ID. If you follow the header text with an opening curly bracket (separated from the text with a least one space), a hash, the ID and a closing curly bracket, the ID is set on the header. If you use the trailing hash feature of atx style headers, the header ID has to go after the trailing hashes. For example:
|
||||
|
||||
```markdown
|
||||
Hello {#id}
|
||||
-----
|
||||
|
||||
# Hello {#id}
|
||||
|
||||
# Hello # {#id}
|
||||
```
|
||||
|
||||
### Paragraphs and Line Breaks {#paragraphs}
|
||||
|
||||
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line — a line containing nothing but spaces or tabs is considered blank.) Normal paragraphs should not be indented with spaces or tabs.
|
||||
|
||||
```
|
||||
Here's a line for us to start with.
|
||||
|
||||
This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
|
||||
```
|
||||
|
||||
### Emphasis {#emphasis}
|
||||
|
||||
```markdown
|
||||
*This text will be italic*
|
||||
_This will also be italic_
|
||||
|
||||
**This text will be bold**
|
||||
__This will also be bold__
|
||||
|
||||
~~This text will be crossed out.~~
|
||||
|
||||
_You **can** combine them_
|
||||
```
|
||||
|
||||
### Lists {#lists}
|
||||
|
||||
Markdown supports ordered (numbered) and unordered (bulleted) lists.
|
||||
|
||||
##### Unordered
|
||||
|
||||
Unordered lists use asterisks, pluses, and hyphens — interchangably — as list markers:
|
||||
|
||||
```markdown
|
||||
* Item 1
|
||||
* Item 2
|
||||
* Item 2a
|
||||
* Item 2b
|
||||
```
|
||||
|
||||
##### Ordered
|
||||
|
||||
Ordered lists use numbers followed by periods:
|
||||
|
||||
```markdown
|
||||
1. Item 1
|
||||
2. Item 2
|
||||
3. Item 3
|
||||
* Item 3a
|
||||
* Item 3b
|
||||
```
|
||||
|
||||
### Links {#links}
|
||||
|
||||
Markdown supports two style of links: inline and reference.
|
||||
|
||||
A simple link can be created by surrounding the text with square brackets and the link URL with parentheses:
|
||||
|
||||
```markdown
|
||||
This is [an example](http://example.com/ "Title") inline link with a title.
|
||||
|
||||
[This link](http://example.net/) has no title attribute.
|
||||
```
|
||||
|
||||
Links can point to relative paths, anchors or absolute urls.
|
||||
|
||||
|
||||
### References
|
||||
|
||||
There is another way to create links which does not interrupt the text flow. The URL and title are defined using a reference name and this reference name is then used in square brackets instead of the link URL:
|
||||
|
||||
```markdown
|
||||
This is [an example][id] reference-style link.
|
||||
```
|
||||
|
||||
Then, anywhere in the document, you define your link label like this, on a line by itself:
|
||||
|
||||
```markdown
|
||||
[id]: http://example.com/ "Optional Title Here"
|
||||
```
|
||||
|
||||
### Images {#images}
|
||||
|
||||
Images can be created in a similar way than links: just use an exclamation mark before the square brackets. The link text will become the alternative text of the image and the link URL specifies the image source:
|
||||
|
||||
```markdown
|
||||
An image: 
|
||||
```
|
||||
|
||||
### Blockquotes {#blockquotes}
|
||||
|
||||
A blockquote is started using the `>` marker followed by an optional space; all following lines that are also started with the blockquote marker belong to the blockquote. You can use any block-level elements inside a blockquote:
|
||||
|
||||
```markdown
|
||||
As Kanye West said:
|
||||
|
||||
> We're living the future so
|
||||
> the present is our past.
|
||||
```
|
||||
|
||||
### Tables {#tables}
|
||||
|
||||
You can create tables by assembling a list of words and dividing them with hyphens `-` (for the first row), and then separating each column with a pipe `|`:
|
||||
|
||||
```markdown
|
||||
| First Header | Second Header |
|
||||
| ------------- | ------------- |
|
||||
| Content Cell | Content Cell |
|
||||
| Content Cell | Content Cell |
|
||||
```
|
||||
|
||||
The pipes on either end of the table are optional. Cells can vary in width and do not need to be perfectly aligned within columns. There must be at least three hyphens in each column of the header row.
|
||||
|
||||
### Code {#code}
|
||||
|
||||
Markdown supports two different code block styles. One uses lines indented with either four spaces or one tab whereas the other uses lines with tilde characters as delimiters – therefore the content does not need to be indented:
|
||||
|
||||
```markdown
|
||||
This is a sample code block.
|
||||
|
||||
Continued here.
|
||||
|
||||
```
|
||||
|
||||
##### Fenced code blocks
|
||||
|
||||
You can create fenced code blocks by placing triple backticks ` ``` ` before and after the code block. We recommend placing a blank line before and after code blocks to make the raw formatting easier to read.
|
||||
|
||||
```
|
||||
function test() {
|
||||
console.log("notice the blank line before this function?");
|
||||
}
|
||||
```
|
||||
|
||||
##### Syntax highlighting
|
||||
|
||||
You can add an optional language identifier to enable syntax highlighting in your fenced code block.
|
||||
|
||||
For example, to syntax highlight Ruby code:
|
||||
|
||||
```ruby
|
||||
require 'redcarpet'
|
||||
markdown = Redcarpet.new("Hello World!")
|
||||
puts markdown.to_html
|
||||
```
|
||||
|
||||
##### Inline code
|
||||
|
||||
Text phrases can be marked up as code by surrounding them with backticks:
|
||||
|
||||
Use `gitbook` to convert the `text` in markdown
|
||||
syntax to HTML.
|
||||
|
||||
### Footnotes
|
||||
|
||||
GitBook supports a simple syntax for such footnotes. Footnotes are relative to each pages.
|
||||
|
||||
```markdown
|
||||
Text prior to footnote reference.[^2]
|
||||
|
||||
[^2]: Comment to include in footnote.
|
||||
```
|
||||
|
||||
### HTML
|
||||
|
||||
GitBook supports use of raw HTML in your text, Markdown syntax in HTML is not processed:
|
||||
|
||||
```
|
||||
<div>
|
||||
Markdown here will not be **parsed**
|
||||
</div>
|
||||
```
|
||||
|
||||
### Horizontal Rule
|
||||
|
||||
Horizontal Rules can be inserted using three or more asterisks, dashes or underscores, optionally separated by spaces or tabs, on an otherwise blank line:
|
||||
|
||||
```markdown
|
||||
Three or more...
|
||||
|
||||
---
|
||||
|
||||
Hyphens
|
||||
|
||||
***
|
||||
|
||||
Asterisks
|
||||
|
||||
```
|
||||
|
||||
### Ignoring Markdown formatting
|
||||
|
||||
You can tell GitBook to ignore (or escape) Markdown formatting by using `\` before the Markdown character.
|
||||
|
||||
```
|
||||
Let's rename \*our-new-project\* to \*our-old-project\*.
|
||||
```
|
||||
@@ -0,0 +1,99 @@
|
||||
# Templating
|
||||
|
||||
GitBook uses the [Nunjucks templating language](https://mozilla.github.io/nunjucks/) to process pages and theme's templates.
|
||||
|
||||
The Nunjucks syntax is very similar to **Jinja2** or **Liquid**. Its syntax uses surrounding braces `{ }` to mark content that needs to be processed.
|
||||
|
||||
### Variables
|
||||
|
||||
A variable looks up a value from the template context. If you wanted to simply display a variable, you would use the `{{ variable }}` syntax. For example :
|
||||
|
||||
```twig
|
||||
My name is {{ name }}, nice to meet you
|
||||
```
|
||||
|
||||
This looks up username from the context and displays it. Variable names can have dots in them which lookup properties, just like JavaScript. You can also use the square bracket syntax.
|
||||
|
||||
```twig
|
||||
{{ foo.bar }}
|
||||
{{ foo["bar"] }}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Filters
|
||||
|
||||
Filters are essentially functions that can be applied to variables. They are called with a pipe operator (`|`) and can take arguments.
|
||||
|
||||
```twig
|
||||
{{ foo | title }}
|
||||
{{ foo | join(",") }}
|
||||
{{ foo | replace("foo", "bar") | capitalize }}
|
||||
```
|
||||
|
||||
The third example shows how you can chain filters. It would display "Bar", by first replacing "foo" with "bar" and then capitalizing it.
|
||||
|
||||
### Tags
|
||||
|
||||
##### if
|
||||
|
||||
`if` tests a condition and lets you selectively display content. It behaves exactly as JavaScript's `if` behaves.
|
||||
|
||||
```twig
|
||||
{% if variable %}
|
||||
It is true
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
If variable is defined and evaluates to true, "It is true" will be displayed. Otherwise, nothing will be.
|
||||
|
||||
You can specify alternate conditions with `elif` and `else`:
|
||||
|
||||
```twig
|
||||
{% if hungry %}
|
||||
I am hungry
|
||||
{% elif tired %}
|
||||
I am tired
|
||||
{% else %}
|
||||
I am good!
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
##### for
|
||||
|
||||
`for` iterates over arrays and dictionaries.
|
||||
|
||||
```twig
|
||||
# Chapters about GitBook
|
||||
|
||||
{% for article in glossary.terms['gitbook'].articles %}
|
||||
* [{{ article.title }}]({{ article.path }})
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
##### set
|
||||
|
||||
`set` lets you create/modify a variable.
|
||||
|
||||
```twig
|
||||
{% set softwareVersion = "1.0.0" %}
|
||||
|
||||
Current version is {{ softwareVersion }}.
|
||||
[Download it](website.com/download/{{ softwareVersion }})
|
||||
```
|
||||
|
||||
##### include and block
|
||||
|
||||
Inclusion and inheritance is detailled in the [Content References](conrefs.md) section.
|
||||
|
||||
### Escaping
|
||||
|
||||
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
|
||||
{% raw %}
|
||||
this will {{ not be processed }}
|
||||
{% endraw %}
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
# Builtin Templating Helpers
|
||||
|
||||
GitBook provides a serie of builtin filters and blocks to help you write templates.
|
||||
|
||||
### 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)
|
||||
|
||||
`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.
|
||||
|
||||
### Blocks
|
||||
|
||||
`{% markdown %}Markdown string{% endmarkdown %}`
|
||||
Render inline markdown
|
||||
|
||||
`{% asciidoc %}AsciiDoc string{% endasciidoc %}`
|
||||
Render inline asciidoc
|
||||
@@ -0,0 +1,55 @@
|
||||
# Content References
|
||||
|
||||
Content referencing (conref) is a convenient mechanism to reuse content from other files or books.
|
||||
|
||||
### Importing local files
|
||||
|
||||
Importing an other file's content is easy using the `include` tag:
|
||||
|
||||
```
|
||||
{% include "./test.md" %}
|
||||
```
|
||||
|
||||
### Importing file from another book
|
||||
|
||||
GitBook can also resolve the include path by using git:
|
||||
|
||||
```
|
||||
{% include "git+https://github.com/GitbookIO/documentation.git/README.md#0.0.1" %}
|
||||
```
|
||||
|
||||
The format of git url is:
|
||||
|
||||
```
|
||||
git+https://user@hostname/owner/project.git/file#commit-ish
|
||||
```
|
||||
|
||||
The real git url part should finish with `.git`, the filename to import is extracted after the `.git` till the fragment of the url.
|
||||
|
||||
The `commit-ish` can be any tag, sha, or branch which can be supplied as an argument to `git checkout`. The default is `master`.
|
||||
|
||||
### Inheritance
|
||||
|
||||
Template inheritance is a way to make it easy to reuse templates. When writing a template, you can define "blocks" that child templates can override. The inheritance chain can be as long as you like.
|
||||
|
||||
`block` defines a section on the template and identifies it with a name. Base templates can specify blocks and child templates can override them with new content.
|
||||
|
||||
```
|
||||
{% extends "./mypage.md" %}
|
||||
|
||||
{% block pageContent %}
|
||||
# This is my page content
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
In the file `mypage.md`, you should specify the blocks that can be extended:
|
||||
|
||||
```
|
||||
{% block pageContent %}
|
||||
This is the default content
|
||||
{% endblock %}
|
||||
|
||||
# License
|
||||
|
||||
{% import "./LICENSE" %}
|
||||
```
|
||||
@@ -0,0 +1,86 @@
|
||||
# Variables
|
||||
|
||||
The following is a reference of the available data during book's parsing and theme generation.
|
||||
|
||||
### Global Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `book` | Book-wide information + configuration settings from `book.json`. See below for details. |
|
||||
| `gitbook` | GitBook specific information |
|
||||
| `page` | Current page specific information |
|
||||
| `file` | File associated with the current page specific information |
|
||||
| `readme` | Information about the Readme |
|
||||
| `glossary` | Information about the Glossary |
|
||||
| `summary` | Information about the table of contents |
|
||||
| `languages` | List of languages for multi-lingual books |
|
||||
| `output` | Information about the output generator |
|
||||
| `config` | Dump of the `book.json` |
|
||||
|
||||
### Book Variables
|
||||
|
||||
| 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 |
|
||||
|
||||
### GitBook Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `gitbook.time` | The current time (when you run the `gitbook` command) . |
|
||||
| `gitbook.version` | Version of GitBook used to generate the book |
|
||||
|
||||
### File Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `file.path` | The path to the raw page |
|
||||
| `file.mtime` | Modified Time. Last time the file was modified |
|
||||
| `file.type` | The name of the parser used to compile this file (ex: `markdown`, `asciidoc`, etc) |
|
||||
|
||||
#### Page Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `page.title` | Title of the page |
|
||||
| `page.previous` | Previous page in the Table of Contents (can be `null`) |
|
||||
| `page.next` | Next page in the Table of Contents (can be `null`) |
|
||||
| `page.dir` | Text direction, based on configuration or detected from content (`rtl` or `ltr`) |
|
||||
|
||||
#### Table of Contents Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `summary.parts` | List of sections in the Table of Contents |
|
||||
|
||||
The whole table of contents (`SUMMARY.md`) can be accessed:
|
||||
|
||||
`summary.parts[0].articles[0].title` will return the title of the first article.
|
||||
|
||||
#### Multi-lingual book Variable
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `languages.list` | List of languages for this book |
|
||||
|
||||
Languages are defined by `{ id: 'en', title: 'English' }`.
|
||||
|
||||
### Output Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `output.name` | Name of the output generator, possible values are `website`, `json`, `ebook` |
|
||||
| `output.format` | When `output.name == "ebook"`, `format` defines the ebook format that will be generated, possible values are `pdf`, `epub` or `mobi` |
|
||||
|
||||
### Readme Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `readme.path` | Path to the Readme in the book |
|
||||
|
||||
### Glossary Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `glossary.path` | Path to the Glossary in the book |
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
# Theming
|
||||
|
||||
Since version 3.0.0, GitBook can be easily themed. Books use the [theme-default](https://github.com/GitbookIO/theme-default) theme by default.
|
||||
|
||||
> **Caution**: Custom theming can block some plugins from working correctly.
|
||||
|
||||
### Structure of a theme
|
||||
|
||||
A theme is a plugin containing templates and assets. Overriding any individual template is optional, since themes always extend the default theme.
|
||||
|
||||
| Folder | Description |
|
||||
| -------- | ----------- |
|
||||
| `_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) |
|
||||
|
||||
|
||||
### 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
|
||||
|
||||
### Publish a theme
|
||||
|
||||
Themes are published as plugins ([see related docs](../plugins/README.md)) with a `theme-` prefix. For example the theme `awesome` will be loaded from the `theme-awesome` plugin, and then from the `gitbook-plugin-theme-awesome` NPM package.
|
||||
@@ -0,0 +1,25 @@
|
||||
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,9 @@
|
||||
var gitbook = require('../gitbook');
|
||||
|
||||
describe('satisfies', function() {
|
||||
|
||||
it('should return true for *', function() {
|
||||
expect(gitbook.satisfies('*')).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
describe('GitBook', function() {
|
||||
it('should correctly export', function() {
|
||||
require('../');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
var Config = require('../models/config');
|
||||
|
||||
/**
|
||||
Decode changes from a JS API to a config object
|
||||
|
||||
@param {Config} config
|
||||
@param {Object} result: result from API
|
||||
@return {Config}
|
||||
*/
|
||||
function decodeGlobal(config, result) {
|
||||
var values = result.values;
|
||||
|
||||
delete values.generator;
|
||||
delete values.output;
|
||||
|
||||
return Config.updateValues(config, values);
|
||||
}
|
||||
|
||||
module.exports = decodeGlobal;
|
||||
@@ -0,0 +1,22 @@
|
||||
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;
|
||||
@@ -0,0 +1,44 @@
|
||||
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;
|
||||
@@ -0,0 +1,122 @@
|
||||
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
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
var objectPath = require('object-path');
|
||||
var deprecate = require('./deprecate');
|
||||
|
||||
/**
|
||||
Encode a config object into a JS config api
|
||||
|
||||
@param {Output} output
|
||||
@param {Config} config
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeConfig(output, config) {
|
||||
var result = {
|
||||
values: config.getValues().toJS(),
|
||||
|
||||
get: function(key, defaultValue) {
|
||||
return objectPath.get(result.values, key, defaultValue);
|
||||
},
|
||||
|
||||
set: function(key, value) {
|
||||
return objectPath.set(result.values, key, value);
|
||||
}
|
||||
};
|
||||
|
||||
deprecate.field(output, 'config.options', result, 'options',
|
||||
result.values, '"config.options" property is deprecated, use "config.get(key)" instead');
|
||||
|
||||
deprecate.field(output, 'config.options.generator', result.values, 'generator',
|
||||
output.getGenerator(), '"options.generator" property is deprecated, use "output.name" instead');
|
||||
|
||||
deprecate.field(output, 'config.options.generator', result.values, 'output',
|
||||
output.getRoot(), '"options.output" property is deprecated, use "output.root()" instead');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = encodeConfig;
|
||||
@@ -0,0 +1,178 @@
|
||||
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 fileToURL = require('../output/helper/fileToURL');
|
||||
var defaultBlocks = require('../constants/defaultBlocks');
|
||||
var gitbook = require('../gitbook');
|
||||
|
||||
var encodeConfig = require('./encodeConfig');
|
||||
var encodeSummary = require('./encodeSummary');
|
||||
var encodeNavigation = require('./encodeNavigation');
|
||||
|
||||
/**
|
||||
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()),
|
||||
summary: encodeSummary(output, book.getSummary()),
|
||||
|
||||
/**
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
Resolve a file from the output root
|
||||
|
||||
@param {String} fileName
|
||||
@return {String}
|
||||
*/
|
||||
resolve: function(fileName) {
|
||||
return path.resolve(outputFolder, fileName);
|
||||
},
|
||||
|
||||
/**
|
||||
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;
|
||||
@@ -0,0 +1,64 @@
|
||||
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;
|
||||
@@ -0,0 +1,39 @@
|
||||
var JSONUtils = require('../json');
|
||||
var deprecate = require('./deprecate');
|
||||
var encodeProgress = require('./encodeProgress');
|
||||
|
||||
/**
|
||||
Encode a page in a context to a JS API
|
||||
|
||||
@param {Output} output
|
||||
@param {Page} page
|
||||
@return {Object}
|
||||
*/
|
||||
function encodePage(output, page) {
|
||||
var book = output.getBook();
|
||||
var summary = book.getSummary();
|
||||
var fs = book.getContentFS();
|
||||
var file = page.getFile();
|
||||
|
||||
// JS Page is based on the JSON output
|
||||
var result = JSONUtils.encodePage(page, summary);
|
||||
|
||||
result.type = file.getType();
|
||||
result.path = file.getPath();
|
||||
result.rawPath = fs.resolve(result.path);
|
||||
|
||||
deprecate.field(output, 'page.progress', result, 'progress', function() {
|
||||
return encodeProgress(output, page);
|
||||
}, '"page.progress" property is deprecated');
|
||||
|
||||
deprecate.field(output, 'page.sections', result, 'sections', [
|
||||
{
|
||||
content: result.content,
|
||||
type: 'normal'
|
||||
}
|
||||
], '"sections" property is deprecated, use page.content instead');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = encodePage;
|
||||
@@ -0,0 +1,63 @@
|
||||
var Immutable = require('immutable');
|
||||
var encodeNavigation = require('./encodeNavigation');
|
||||
|
||||
/**
|
||||
page.progress is a deprecated property from GitBook v2
|
||||
|
||||
@param {Output}
|
||||
@param {Page}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeProgress(output, page) {
|
||||
var current = page.getPath();
|
||||
var navigation = encodeNavigation(output);
|
||||
navigation = Immutable.Map(navigation);
|
||||
|
||||
var n = navigation.size;
|
||||
var percent = 0, prevPercent = 0, currentChapter = null;
|
||||
var done = true;
|
||||
|
||||
var chapters = navigation
|
||||
.map(function(nav, chapterPath) {
|
||||
nav.path = chapterPath;
|
||||
return nav;
|
||||
})
|
||||
.valueSeq()
|
||||
.sortBy(function(nav) {
|
||||
return nav.index;
|
||||
})
|
||||
.map(function(nav, i) {
|
||||
// Calcul percent
|
||||
nav.percent = (i * 100) / Math.max((n - 1), 1);
|
||||
|
||||
// Is it done
|
||||
nav.done = done;
|
||||
if (nav.path == current) {
|
||||
currentChapter = nav;
|
||||
percent = nav.percent;
|
||||
done = false;
|
||||
} else if (done) {
|
||||
prevPercent = nav.percent;
|
||||
}
|
||||
|
||||
return nav;
|
||||
})
|
||||
.toJS();
|
||||
|
||||
return {
|
||||
// Previous percent
|
||||
prevPercent: prevPercent,
|
||||
|
||||
// Current percent
|
||||
percent: percent,
|
||||
|
||||
// List of chapter with progress
|
||||
chapters: chapters,
|
||||
|
||||
// Current chapter
|
||||
current: currentChapter
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = encodeProgress;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
var encodeSummaryArticle = require('../json/encodeSummaryArticle');
|
||||
|
||||
/**
|
||||
Encode summary to provide an API to plugin
|
||||
|
||||
@param {Output} output
|
||||
@param {Config} config
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeSummary(output, summary) {
|
||||
var result = {
|
||||
/**
|
||||
Iterate over the summary, it stops when the "iter" returns false
|
||||
|
||||
@param {Function} iter
|
||||
*/
|
||||
walk: function (iter) {
|
||||
summary.getArticle(function(article) {
|
||||
var jsonArticle = encodeSummaryArticle(article, false);
|
||||
|
||||
return iter(jsonArticle);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Get an article by its level
|
||||
|
||||
@param {String} level
|
||||
@return {Object}
|
||||
*/
|
||||
getArticleByLevel: function(level) {
|
||||
var article = summary.getByLevel(level);
|
||||
return (article? encodeSummaryArticle(article) : undefined);
|
||||
},
|
||||
|
||||
/**
|
||||
Get an article by its path
|
||||
|
||||
@param {String} level
|
||||
@return {Object}
|
||||
*/
|
||||
getArticleByPath: function(level) {
|
||||
var article = summary.getByPath(level);
|
||||
return (article? encodeSummaryArticle(article) : undefined);
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = encodeSummary;
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
module.exports = {
|
||||
encodePage: require('./encodePage'),
|
||||
decodePage: require('./decodePage'),
|
||||
|
||||
encodeGlobal: require('./encodeGlobal'),
|
||||
decodeGlobal: require('./decodeGlobal')
|
||||
};
|
||||
-791
@@ -1,791 +0,0 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var util = require("util");
|
||||
var lunr = require('lunr');
|
||||
var parsers = require("gitbook-parsers");
|
||||
var color = require('bash-color');
|
||||
|
||||
var fs = require("./utils/fs");
|
||||
var parseNavigation = require("./utils/navigation");
|
||||
var parseProgress = require("./utils/progress");
|
||||
var pageUtil = require("./utils/page");
|
||||
var batch = require("./utils/batch");
|
||||
var links = require("./utils/links");
|
||||
var logger = require("./utils/logger");
|
||||
|
||||
var Configuration = require("./configuration");
|
||||
var TemplateEngine = require("./template");
|
||||
var Plugin = require("./plugin");
|
||||
var PluginsList = require("./pluginslist");
|
||||
|
||||
var generators = require("./generators");
|
||||
|
||||
var Book = function(root, context, parent) {
|
||||
var that = this;
|
||||
|
||||
this.context = _.defaults(context || {}, {
|
||||
// Extend book configuration
|
||||
config: {},
|
||||
|
||||
// Log function
|
||||
log: function(msg) {
|
||||
process.stdout.write(msg);
|
||||
},
|
||||
|
||||
// Log level
|
||||
logLevel: "info"
|
||||
});
|
||||
|
||||
// Log
|
||||
this.log = logger(this.context.log, this.context.logLevel);
|
||||
|
||||
// Root folder of the book
|
||||
this.root = path.resolve(root);
|
||||
|
||||
// Parent book
|
||||
this.parent = parent;
|
||||
|
||||
// Configuration
|
||||
this.config = new Configuration(this, this.context.config);
|
||||
Object.defineProperty(this, "options", {
|
||||
get: function () {
|
||||
return this.config.options;
|
||||
}
|
||||
});
|
||||
|
||||
// Template
|
||||
this.template = new TemplateEngine(this);
|
||||
|
||||
// Summary
|
||||
this.summary = {};
|
||||
this.navigation = [];
|
||||
|
||||
// Glossary
|
||||
this.glossary = [];
|
||||
|
||||
// Langs
|
||||
this.langs = [];
|
||||
|
||||
// Sub-books
|
||||
this.books = [];
|
||||
|
||||
// Files in the book
|
||||
this.files = [];
|
||||
|
||||
// List of plugins
|
||||
this.plugins = new PluginsList(this);
|
||||
|
||||
// Structure files
|
||||
this.summaryFile = null;
|
||||
this.glossaryFile = null;
|
||||
this.readmeFile = null;
|
||||
this.langsFile = null;
|
||||
|
||||
// Search Index
|
||||
this.searchIndex = lunr(function () {
|
||||
this.ref('url');
|
||||
|
||||
this.field('title', { boost: 10 });
|
||||
this.field('body');
|
||||
});
|
||||
|
||||
// Bind methods
|
||||
_.bindAll(this);
|
||||
};
|
||||
|
||||
// Initialize and parse the book: config, summary, glossary
|
||||
Book.prototype.parse = function() {
|
||||
var that = this;
|
||||
var multilingual = false;
|
||||
|
||||
return this.parseConfig()
|
||||
|
||||
.then(function() {
|
||||
return that.parsePlugins();
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
return that.parseLangs()
|
||||
.then(function() {
|
||||
multilingual = that.langs.length > 0;
|
||||
if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "lanuages");
|
||||
|
||||
// Sub-books that inherit from the current book configuration
|
||||
that.books = _.map(that.langs, function(lang) {
|
||||
that.log.info.ln("Preparing language book", lang.lang);
|
||||
return new Book(
|
||||
path.join(that.root, lang.path),
|
||||
_.merge({}, that.context, {
|
||||
config: _.extend({}, that.options, {
|
||||
'output': path.join(that.options.output, lang.lang),
|
||||
'language': lang.lang
|
||||
})
|
||||
}),
|
||||
that
|
||||
)
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
if (multilingual) return;
|
||||
return that.listAllFiles();
|
||||
})
|
||||
.then(function() {
|
||||
if (multilingual) return;
|
||||
return that.parseReadme();
|
||||
})
|
||||
.then(function() {
|
||||
if (multilingual) return;
|
||||
return that.parseSummary();
|
||||
})
|
||||
.then(function() {
|
||||
if (multilingual) return;
|
||||
return that.parseGlossary();
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
// Init sub-books
|
||||
return _.reduce(that.books, function(prev, book) {
|
||||
return prev.then(function() {
|
||||
return book.parse();
|
||||
});
|
||||
}, Q());
|
||||
})
|
||||
|
||||
.thenResolve(this);
|
||||
};
|
||||
|
||||
// Generate the output
|
||||
Book.prototype.generate = function(generator) {
|
||||
var that = this;
|
||||
that.options.generator = generator || that.options.generator;
|
||||
|
||||
that.log.info.ln("start generation with", that.options.generator, "generator");
|
||||
return Q()
|
||||
|
||||
// Clean output folder
|
||||
.then(function() {
|
||||
that.log.info("clean", that.options.generator, "generator");
|
||||
return fs.clean(that.options.output)
|
||||
.progress(function(p) {
|
||||
that.log.debug.ln("remove", p.file, "("+p.i+"/"+p.count+")");
|
||||
})
|
||||
.then(function() {
|
||||
that.log.info.ok();
|
||||
});
|
||||
})
|
||||
|
||||
// Create generator
|
||||
.then(function() {
|
||||
var Generator = generators[generator];
|
||||
if (!Generator) throw "Generator '"+that.options.generator+"' doesn't exist";
|
||||
generator = new Generator(that);
|
||||
|
||||
return generator.prepare();
|
||||
})
|
||||
|
||||
// Generate content
|
||||
.then(function() {
|
||||
if (that.isMultilingual()) {
|
||||
return that.generateMultiLingual(generator);
|
||||
} else {
|
||||
// Separate list of files into the different operations needed
|
||||
var ops = _.groupBy(that.files, function(file) {
|
||||
if (file[file.length -1] == "/") {
|
||||
return "directories";
|
||||
} else if (_.contains(parsers.extensions, path.extname(file)) && that.navigation[file]) {
|
||||
return "content";
|
||||
} else {
|
||||
return "files";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return Q()
|
||||
|
||||
// First, let's create folder
|
||||
.then(function() {
|
||||
return _.reduce(ops["directories"] || [], function(prev, folder) {
|
||||
return prev.then(function() {
|
||||
that.log.debug.ln("transferring folder", folder);
|
||||
return Q(generator.transferFolder(folder));
|
||||
});
|
||||
}, Q());
|
||||
})
|
||||
|
||||
// Then, let's copy other files
|
||||
.then(function() {
|
||||
return Q.all(_.map(ops["files"] || [], function(file) {
|
||||
that.log.debug.ln("transferring file", file);
|
||||
return Q(generator.transferFile(file));
|
||||
}));
|
||||
})
|
||||
|
||||
// Finally let's generate content
|
||||
.then(function() {
|
||||
var nFiles = (ops["content"] || []).length;
|
||||
return _.reduce(ops["content"] || [], function(prev, file, i) {
|
||||
return prev.then(function() {
|
||||
var p = ((i*100)/nFiles).toFixed(0)+"%";
|
||||
that.log.info.ln("processing", file, p);
|
||||
return Q(generator.convertFile(file));
|
||||
});
|
||||
}, Q());
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
// Finish generation
|
||||
.then(function() {
|
||||
return generator.callHook("finish:before");
|
||||
})
|
||||
.then(function() {
|
||||
return generator.finish();
|
||||
})
|
||||
.then(function() {
|
||||
return generator.callHook("finish");
|
||||
})
|
||||
.then(function() {
|
||||
that.log.info.ln("generation is finished");
|
||||
});
|
||||
};
|
||||
|
||||
// Generate the output for a multilingual book
|
||||
Book.prototype.generateMultiLingual = function(generator) {
|
||||
var that = this;
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
// Generate sub-books
|
||||
return _.reduce(that.books, function(prev, book) {
|
||||
return prev.then(function() {
|
||||
return book.generate(that.options.generator);
|
||||
});
|
||||
}, Q());
|
||||
});
|
||||
};
|
||||
|
||||
// Extract files from ebook generated
|
||||
Book.prototype.generateFile = function(output, options) {
|
||||
var book = this;
|
||||
|
||||
options = _.defaults(options || {}, {
|
||||
ebookFormat: path.extname(output).slice(1)
|
||||
});
|
||||
output = output || path.resolve(book.root, "book."+options.ebookFormat);
|
||||
|
||||
return fs.tmp.dir()
|
||||
.then(function(tmpDir) {
|
||||
book.setOutput(tmpDir);
|
||||
|
||||
return book.generate(options.ebookFormat)
|
||||
.then(function(_options) {
|
||||
var copyFile = function(lang) {
|
||||
var _outputFile = output;
|
||||
var _tmpDir = tmpDir;
|
||||
|
||||
if (lang) {
|
||||
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
|
||||
_tmpDir = path.join(_tmpDir, lang);
|
||||
}
|
||||
|
||||
book.log.debug.ln("copy ebook to", _outputFile);
|
||||
return fs.copy(
|
||||
path.join(_tmpDir, "index."+options.ebookFormat),
|
||||
_outputFile
|
||||
);
|
||||
};
|
||||
|
||||
// Multi-langs book
|
||||
return Q()
|
||||
.then(function() {
|
||||
if (book.isMultilingual()) {
|
||||
return Q.all(
|
||||
_.map(book.langs, function(lang) {
|
||||
return copyFile(lang.lang);
|
||||
})
|
||||
)
|
||||
.thenResolve(book.langs.length);
|
||||
} else {
|
||||
return copyFile().thenResolve(1);
|
||||
}
|
||||
})
|
||||
.then(function(n) {
|
||||
book.log.info.ok(n+" file(s) generated");
|
||||
|
||||
return fs.remove(tmpDir);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Parse configuration
|
||||
Book.prototype.parseConfig = function() {
|
||||
var that = this;
|
||||
|
||||
that.log.info("loading book configuration....")
|
||||
return that.config.load()
|
||||
.then(function() {
|
||||
that.log.info.ok();
|
||||
});
|
||||
};
|
||||
|
||||
// Parse list of plugins
|
||||
Book.prototype.parsePlugins = function() {
|
||||
var that = this;
|
||||
var failed = [];
|
||||
|
||||
// Load plugins
|
||||
return that.plugins.load(that.options.plugins)
|
||||
.then(function() {
|
||||
if (_.size(that.plugins.failed) > 0) return Q.reject(new Error("Error loading plugins: "+that.plugins.failed.join(",")+". Run 'gitbook install' to install plugins from NPM."));
|
||||
|
||||
that.log.info.ok(that.plugins.count()+" plugins loaded");
|
||||
that.log.debug.ln("normalize plugins list");
|
||||
});
|
||||
};
|
||||
|
||||
// Parse readme to extract defaults title and description
|
||||
Book.prototype.parseReadme = function() {
|
||||
var that = this;
|
||||
var structure = that.config.getStructure("readme");
|
||||
that.log.debug.ln("start parsing readme:", structure);
|
||||
|
||||
return that.findFile(structure)
|
||||
.then(function(readme) {
|
||||
if (!readme) throw "No README file";
|
||||
if (!_.contains(that.files, readme.path)) throw "README file is ignored";
|
||||
|
||||
that.readmeFile = readme.path;
|
||||
that._defaultsStructure(that.readmeFile);
|
||||
|
||||
that.log.debug.ln("readme located at", that.readmeFile);
|
||||
return that.template.renderFile(that.readmeFile)
|
||||
.then(function(content) {
|
||||
return readme.parser.readme(content);
|
||||
});
|
||||
})
|
||||
.then(function(readme) {
|
||||
that.options.title = that.options.title || readme.title;
|
||||
that.options.description = that.options.description || readme.description;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Parse langs to extract list of sub-books
|
||||
Book.prototype.parseLangs = function() {
|
||||
var that = this;
|
||||
|
||||
var structure = that.config.getStructure("langs");
|
||||
that.log.debug.ln("start parsing languages index:", structure);
|
||||
|
||||
return that.findFile(structure)
|
||||
.then(function(langs) {
|
||||
if (!langs) return [];
|
||||
|
||||
that.langsFile = langs.path;
|
||||
that._defaultsStructure(that.langsFile);
|
||||
|
||||
that.log.debug.ln("languages index located at", that.langsFile);
|
||||
return that.template.renderFile(that.langsFile)
|
||||
.then(function(content) {
|
||||
return langs.parser.langs(content);
|
||||
});
|
||||
})
|
||||
.then(function(langs) {
|
||||
that.langs = langs;
|
||||
});
|
||||
};
|
||||
|
||||
// Parse summary to extract list of chapters
|
||||
Book.prototype.parseSummary = function() {
|
||||
var that = this;
|
||||
|
||||
var structure = that.config.getStructure("summary");
|
||||
that.log.debug.ln("start parsing summary:", structure);
|
||||
|
||||
return that.findFile(structure)
|
||||
.then(function(summary) {
|
||||
if (!summary) throw "No SUMMARY file";
|
||||
|
||||
// Remove the summary from the list of files to parse
|
||||
that.summaryFile = summary.path;
|
||||
that._defaultsStructure(that.summaryFile);
|
||||
that.files = _.without(that.files, that.summaryFile);
|
||||
|
||||
that.log.debug.ln("summary located at", that.summaryFile);
|
||||
return that.template.renderFile(that.summaryFile)
|
||||
.then(function(content) {
|
||||
return summary.parser.summary(content, {
|
||||
entryPoint: that.readmeFile,
|
||||
entryPointTitle: "",
|
||||
files: that.files
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function(summary) {
|
||||
that.summary = summary;
|
||||
that.navigation = parseNavigation(that.summary, that.files);
|
||||
});
|
||||
};
|
||||
|
||||
// Parse glossary to extract terms
|
||||
Book.prototype.parseGlossary = function() {
|
||||
var that = this;
|
||||
|
||||
var structure = that.config.getStructure("glossary");
|
||||
that.log.debug.ln("start parsing glossary: ", structure);
|
||||
|
||||
return that.findFile(structure)
|
||||
.then(function(glossary) {
|
||||
if (!glossary) return [];
|
||||
|
||||
// Remove the glossary from the list of files to parse
|
||||
that.glossaryFile = glossary.path;
|
||||
that._defaultsStructure(that.glossaryFile);
|
||||
that.files = _.without(that.files, that.glossaryFile);
|
||||
|
||||
that.log.debug.ln("glossary located at", that.glossaryFile);
|
||||
return that.template.renderFile(that.glossaryFile)
|
||||
.then(function(content) {
|
||||
return glossary.parser.glossary(content);
|
||||
});
|
||||
})
|
||||
.then(function(glossary) {
|
||||
that.glossary = glossary;
|
||||
});
|
||||
};
|
||||
|
||||
// Parse a page
|
||||
Book.prototype.parsePage = function(filename, options) {
|
||||
var that = this, page = {};
|
||||
options = _.defaults(options || {}, {
|
||||
// Transform svg images
|
||||
convertImages: false,
|
||||
|
||||
// Interpolate before templating
|
||||
interpolateTemplate: _.identity,
|
||||
|
||||
// Interpolate after templating
|
||||
interpolateContent: _.identity
|
||||
});
|
||||
|
||||
var interpolate = function(fn) {
|
||||
return Q(fn(page))
|
||||
.then(function(_page) {
|
||||
page = _page;
|
||||
});
|
||||
};
|
||||
|
||||
that.log.debug.ln("start parsing file", filename);
|
||||
|
||||
var extension = path.extname(filename);
|
||||
var filetype = parsers.get(extension);
|
||||
|
||||
if (!filetype) return Q.reject(new Error("Can't parse file: "+filename));
|
||||
|
||||
// Type of parser used
|
||||
page.type = filetype.name;
|
||||
|
||||
// Path relative to book
|
||||
page.path = filename;
|
||||
|
||||
// Path absolute in the system
|
||||
page.rawPath = path.resolve(that.root, filename);
|
||||
|
||||
// Progress in the book
|
||||
page.progress = parseProgress(that.navigation, filename);
|
||||
|
||||
that.log.debug.ln("render template", filename);
|
||||
|
||||
// Read file content
|
||||
return that.readFile(page.path)
|
||||
.then(function(content) {
|
||||
page.content = content;
|
||||
|
||||
return interpolate(options.interpolateTemplate);
|
||||
})
|
||||
|
||||
// Prepare page markup
|
||||
.then(function() {
|
||||
return filetype.page.prepare(page.content)
|
||||
.then(function(content) {
|
||||
page.content = content;
|
||||
});
|
||||
})
|
||||
|
||||
// Generate template
|
||||
.then(function() {
|
||||
return that.template.renderPage(page);
|
||||
})
|
||||
|
||||
// Prepare and Parse markup
|
||||
.then(function(content) {
|
||||
page.content = content;
|
||||
|
||||
that.log.debug.ln("use file parser", filetype.name, "for", filename);
|
||||
return filetype.page(page.content);
|
||||
})
|
||||
|
||||
// Post process sections
|
||||
.then(function(_page) {
|
||||
return _.reduce(_page.sections, function(prev, section) {
|
||||
return prev.then(function(_sections) {
|
||||
return that.template.postProcess(section.content || "")
|
||||
.then(function(content) {
|
||||
section.content = content;
|
||||
return _sections.concat([section]);
|
||||
})
|
||||
});
|
||||
}, Q([]));
|
||||
})
|
||||
|
||||
// Prepare html
|
||||
.then(function(_sections) {
|
||||
return pageUtil.normalize(_sections, {
|
||||
book: that,
|
||||
convertImages: options.convertImages,
|
||||
input: filename,
|
||||
navigation: that.navigation,
|
||||
base: path.dirname(filename) || './',
|
||||
output: path.dirname(filename) || './',
|
||||
glossary: that.glossary
|
||||
});
|
||||
})
|
||||
|
||||
// Interpolate output
|
||||
.then(function(_sections) {
|
||||
page.sections = _sections;
|
||||
return interpolate(options.interpolateContent);
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
that.indexPage(page);
|
||||
return page;
|
||||
});
|
||||
};
|
||||
|
||||
// Find file that can be parsed with a specific filename
|
||||
Book.prototype.findFile = function(filename) {
|
||||
var that = this;
|
||||
|
||||
return _.reduce(parsers.extensions, function(prev, ext) {
|
||||
return prev.then(function(output) {
|
||||
// Stop if already find a parser
|
||||
if (output) return output;
|
||||
|
||||
var filepath = filename+ext;
|
||||
|
||||
return that.fileExists(filepath)
|
||||
.then(function(exists) {
|
||||
if (!exists) return null;
|
||||
return {
|
||||
parser: parsers.get(ext),
|
||||
path: filepath
|
||||
};
|
||||
})
|
||||
});
|
||||
}, Q(null));
|
||||
};
|
||||
|
||||
// Check if a file exists in the book
|
||||
Book.prototype.fileExists = function(filename) {
|
||||
return fs.exists(
|
||||
path.join(this.root, filename)
|
||||
);
|
||||
};
|
||||
|
||||
// Read a file
|
||||
Book.prototype.readFile = function(filename) {
|
||||
return fs.readFile(
|
||||
path.join(this.root, filename),
|
||||
{ encoding: "utf8" }
|
||||
);
|
||||
};
|
||||
|
||||
// Return stat for a file
|
||||
Book.prototype.statFile = function(filename) {
|
||||
return fs.stat(path.join(this.root, filename));
|
||||
};
|
||||
|
||||
// List all files in the book
|
||||
Book.prototype.listAllFiles = function() {
|
||||
var that = this;
|
||||
|
||||
return fs.list(this.root, {
|
||||
ignoreFiles: ['.ignore', '.gitignore', '.bookignore'],
|
||||
ignoreRules: [
|
||||
// Skip Git stuff
|
||||
'.git/',
|
||||
'.gitignore',
|
||||
|
||||
// Skip OS X meta data
|
||||
'.DS_Store',
|
||||
|
||||
// Skip stuff installed by plugins
|
||||
'node_modules',
|
||||
|
||||
// Skip book outputs
|
||||
'_book',
|
||||
'*.pdf',
|
||||
'*.epub',
|
||||
'*.mobi',
|
||||
|
||||
// Skip config files
|
||||
'.ignore',
|
||||
'.bookignore',
|
||||
'book.json',
|
||||
]
|
||||
})
|
||||
.then(function(_files) {
|
||||
that.files = _files;
|
||||
});
|
||||
};
|
||||
|
||||
// Return true if the book is a multilingual book
|
||||
Book.prototype.isMultilingual = function(filename) {
|
||||
return this.books.length > 0;
|
||||
};
|
||||
|
||||
// Return root of the parent
|
||||
Book.prototype.parentRoot = function() {
|
||||
if (this.parent) return this.parent.parentRoot();
|
||||
return this.root;
|
||||
};
|
||||
|
||||
// Return true if it's a sub-book
|
||||
Book.prototype.isSubBook = function() {
|
||||
return !!this.parent;
|
||||
};
|
||||
|
||||
// Test if the file is the entry point
|
||||
Book.prototype.isEntryPoint = function(fp) {
|
||||
return fp == this.readmeFile;
|
||||
};
|
||||
|
||||
// Resolve a path in book
|
||||
Book.prototype.resolve = function(p) {
|
||||
return path.resolve(this.root, p);
|
||||
};
|
||||
|
||||
// Normalize a link to .html and convert README -> index
|
||||
Book.prototype.contentLink = function(link) {
|
||||
if (
|
||||
path.basename(link, path.extname(link)) == "README"
|
||||
|| link == this.readmeFile
|
||||
) {
|
||||
link = path.join(path.dirname(link), "index"+path.extname(link));
|
||||
}
|
||||
|
||||
link = links.changeExtension(link, ".html");
|
||||
return link;
|
||||
}
|
||||
|
||||
// Index a page into the search index
|
||||
Book.prototype.indexPage = function(page) {
|
||||
var nav = this.navigation[page.path];
|
||||
if (!nav) return;
|
||||
|
||||
this.log.debug.ln("index page", page.path);
|
||||
this.searchIndex.add({
|
||||
url: this.contentLink(page.path),
|
||||
title: nav.title,
|
||||
body: pageUtil.extractText(page.sections),
|
||||
});
|
||||
};
|
||||
|
||||
// Default structure paths to an extension
|
||||
Book.prototype._defaultsStructure = function(filename) {
|
||||
var that = this;
|
||||
var extension = path.extname(filename);
|
||||
|
||||
that.readmeFile = that.readmeFile || that.config.getStructure("readme")+extension;
|
||||
that.summaryFile = that.summaryFile || that.config.getStructure("summary")+extension;
|
||||
that.glossaryFile = that.glossaryFile || that.config.getStructure("glossary")+extension;
|
||||
that.langsFile = that.langsFile || that.config.getStructure("langs")+extension;
|
||||
}
|
||||
|
||||
// Change output path
|
||||
Book.prototype.setOutput = function(p) {
|
||||
var that = this;
|
||||
this.options.output = path.resolve(p);
|
||||
|
||||
_.each(this.books, function(book) {
|
||||
book.setOutput(path.join(that.options.output, book.options.language));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Init and return a book
|
||||
Book.init = function(root) {
|
||||
var book = new Book(root);
|
||||
var extensionToUse = ".md";
|
||||
|
||||
var chaptersPaths = function(chapters) {
|
||||
return _.reduce(chapters || [], function(accu, chapter) {
|
||||
if (!chapter.path) return accu;
|
||||
return accu.concat(
|
||||
_.filter([
|
||||
{
|
||||
title: chapter.title,
|
||||
path: chapter.path
|
||||
}
|
||||
].concat(chaptersPaths(chapter.articles)))
|
||||
);
|
||||
}, []);
|
||||
};
|
||||
|
||||
book.log.info.ln("init book at", root);
|
||||
return fs.mkdirp(root)
|
||||
.then(function() {
|
||||
book.log.info.ln("detect structure from SUMMARY (if it exists)");
|
||||
return book.parseSummary();
|
||||
})
|
||||
.fail(function() {
|
||||
return Q();
|
||||
})
|
||||
.then(function() {
|
||||
var summary = book.summaryFile || "SUMMARY.md";
|
||||
var chapters = book.summary.chapters || [];
|
||||
extensionToUse = path.extname(summary);
|
||||
|
||||
if (chapters.length == 0) {
|
||||
chapters = [
|
||||
{
|
||||
title: "Summary",
|
||||
path: "SUMMARY"+extensionToUse
|
||||
},
|
||||
{
|
||||
title: "Introduction",
|
||||
path: "README"+extensionToUse
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
return Q(chaptersPaths(chapters));
|
||||
})
|
||||
.then(function(chapters) {
|
||||
// Create files that don't exist
|
||||
return Q.all(_.map(chapters, function(chapter) {
|
||||
var absolutePath = path.resolve(book.root, chapter.path);
|
||||
|
||||
return fs.exists(absolutePath)
|
||||
.then(function(exists) {
|
||||
book.log.info.ln("create", chapter.path);
|
||||
if(exists) return;
|
||||
|
||||
return fs.mkdirp(path.dirname(absolutePath))
|
||||
.then(function() {
|
||||
return fs.writeFile(absolutePath, '# '+chapter.title+'\n');
|
||||
});
|
||||
});
|
||||
}));
|
||||
})
|
||||
.then(function() {
|
||||
book.log.info.ln("initialization is finished");
|
||||
});
|
||||
};
|
||||
|
||||
module.exports= Book;
|
||||
@@ -0,0 +1,15 @@
|
||||
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
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
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());
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
var path = require('path');
|
||||
var tmp = require('tmp');
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
var fs = require('../utils/fs');
|
||||
var Parse = require('../parse');
|
||||
var Output = require('../output');
|
||||
|
||||
var options = require('./options');
|
||||
var getBook = require('./getBook');
|
||||
|
||||
|
||||
module.exports = function(format) {
|
||||
return {
|
||||
name: (format + ' [book] [output]'),
|
||||
description: 'build a book into an ebook file',
|
||||
options: [
|
||||
options.log
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
// Output file will be stored in
|
||||
var outputFile = args[1] || ('book.' + format);
|
||||
|
||||
// Create temporary directory
|
||||
var outputFolder = tmp.dirSync().name;
|
||||
|
||||
var book = getBook(args, kwargs);
|
||||
var logger = book.getLogger();
|
||||
var Generator = Output.getGenerator('ebook');
|
||||
|
||||
return Parse.parseBook(book)
|
||||
.then(function(resultBook) {
|
||||
return Output.generate(Generator, resultBook, {
|
||||
root: outputFolder,
|
||||
format: format
|
||||
});
|
||||
})
|
||||
|
||||
// Extract ebook file
|
||||
.then(function(output) {
|
||||
var book = output.getBook();
|
||||
var languages = book.getLanguages();
|
||||
|
||||
if (book.isMultilingual()) {
|
||||
return Promise.ForEach(languages, function(lang) {
|
||||
var langID = lang.getID();
|
||||
|
||||
var langOutputFile = path.join(
|
||||
path.dirname(outputFile),
|
||||
path.basename(outputFile, format) + '_' + langID + '.' + format
|
||||
);
|
||||
|
||||
return fs.copy(
|
||||
path.resolve(outputFolder, langID, 'index.' + format),
|
||||
langOutputFile
|
||||
);
|
||||
})
|
||||
.thenResolve(languages.getCount());
|
||||
} else {
|
||||
return fs.copy(
|
||||
path.resolve(outputFolder, 'index.' + format),
|
||||
outputFile
|
||||
).thenResolve(1);
|
||||
}
|
||||
})
|
||||
|
||||
// Log end
|
||||
.then(function(count) {
|
||||
logger.info.ok(count + ' file(s) generated');
|
||||
|
||||
logger.debug('cleaning up... ');
|
||||
return logger.debug.promise(fs.rmDir(outputFolder));
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
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;
|
||||
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
@@ -0,0 +1,12 @@
|
||||
var buildEbook = require('./buildEbook');
|
||||
|
||||
module.exports = [
|
||||
require('./build'),
|
||||
require('./serve'),
|
||||
require('./install'),
|
||||
require('./parse'),
|
||||
require('./init'),
|
||||
buildEbook('pdf'),
|
||||
buildEbook('epub'),
|
||||
buildEbook('mobi')
|
||||
];
|
||||
@@ -0,0 +1,17 @@
|
||||
var path = require('path');
|
||||
|
||||
var options = require('./options');
|
||||
var initBook = require('../init');
|
||||
|
||||
module.exports = {
|
||||
name: 'init [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);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
var options = require('./options');
|
||||
var getBook = require('./getBook');
|
||||
|
||||
var Parse = require('../parse');
|
||||
var Plugins = require('../plugins');
|
||||
|
||||
module.exports = {
|
||||
name: 'install [book]',
|
||||
description: 'install all plugins dependencies',
|
||||
options: [
|
||||
options.log
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var book = getBook(args, kwargs);
|
||||
|
||||
return Parse.parseConfig(book)
|
||||
.then(function(resultBook) {
|
||||
return Plugins.installPlugins(resultBook);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
var Logger = require('../utils/logger');
|
||||
|
||||
var logOptions = {
|
||||
name: 'log',
|
||||
description: 'Minimum log level to display',
|
||||
values: Logger.LEVELS
|
||||
.keySeq()
|
||||
.map(function(s) {
|
||||
return s.toLowerCase();
|
||||
}).toJS(),
|
||||
defaults: 'info'
|
||||
};
|
||||
|
||||
var formatOption = {
|
||||
name: 'format',
|
||||
description: 'Format to build to',
|
||||
values: ['website', 'json', 'ebook'],
|
||||
defaults: 'website'
|
||||
};
|
||||
|
||||
var timingOption = {
|
||||
name: 'timing',
|
||||
description: 'Print timing debug information',
|
||||
defaults: false
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
log: logOptions,
|
||||
format: formatOption,
|
||||
timing: timingOption
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
var options = require('./options');
|
||||
var getBook = require('./getBook');
|
||||
|
||||
var Parse = require('../parse');
|
||||
|
||||
function printBook(book) {
|
||||
var logger = book.getLogger();
|
||||
|
||||
var config = book.getConfig();
|
||||
var configFile = config.getFile();
|
||||
|
||||
var summary = book.getSummary();
|
||||
var summaryFile = summary.getFile();
|
||||
|
||||
var readme = book.getReadme();
|
||||
var readmeFile = readme.getFile();
|
||||
|
||||
var glossary = book.getGlossary();
|
||||
var glossaryFile = glossary.getFile();
|
||||
|
||||
if (configFile.exists()) {
|
||||
logger.info.ln('Configuration file is', configFile.getPath());
|
||||
}
|
||||
|
||||
if (readmeFile.exists()) {
|
||||
logger.info.ln('Introduction file is', readmeFile.getPath());
|
||||
}
|
||||
|
||||
if (glossaryFile.exists()) {
|
||||
logger.info.ln('Glossary file is', glossaryFile.getPath());
|
||||
}
|
||||
|
||||
if (summaryFile.exists()) {
|
||||
logger.info.ln('Table of Contents file is', summaryFile.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
function printMultingualBook(book) {
|
||||
var logger = book.getLogger();
|
||||
var languages = book.getLanguages();
|
||||
var books = book.getBooks();
|
||||
|
||||
logger.info.ln(languages.size + ' languages');
|
||||
|
||||
languages.forEach(function(lang) {
|
||||
logger.info.ln('Language:', lang.getTitle());
|
||||
printBook(books.get(lang.getID()));
|
||||
logger.info.ln('');
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
name: 'parse [book]',
|
||||
description: 'parse and print debug information about a book',
|
||||
options: [
|
||||
options.log
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var book = getBook(args, kwargs);
|
||||
var logger = book.getLogger();
|
||||
|
||||
return Parse.parseBook(book)
|
||||
.then(function(resultBook) {
|
||||
var rootFolder = book.getRoot();
|
||||
var contentFolder = book.getContentRoot();
|
||||
|
||||
logger.info.ln('Book located in:', rootFolder);
|
||||
if (contentFolder != rootFolder) {
|
||||
logger.info.ln('Content located in:', contentFolder);
|
||||
}
|
||||
|
||||
if (resultBook.isMultilingual()) {
|
||||
printMultingualBook(resultBook);
|
||||
} else {
|
||||
printBook(resultBook);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,140 @@
|
||||
/* 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 waitForCtrlC() {
|
||||
var d = Promise.defer();
|
||||
|
||||
process.on('SIGINT', function() {
|
||||
d.resolve();
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
|
||||
function generateBook(args, kwargs) {
|
||||
var port = kwargs.port;
|
||||
var outputFolder = getOutputFolder(args);
|
||||
var book = getBook(args, kwargs);
|
||||
var Generator = Output.getGenerator(kwargs.format);
|
||||
|
||||
var hasWatch = kwargs['watch'];
|
||||
var hasLiveReloading = kwargs['live'];
|
||||
|
||||
// Stop server if running
|
||||
if (server.isRunning()) console.log('Stopping server');
|
||||
|
||||
return server.stop()
|
||||
.then(function() {
|
||||
return Parse.parseBook(book)
|
||||
.then(function(resultBook) {
|
||||
if (hasLiveReloading) {
|
||||
// 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 && hasLiveReloading) {
|
||||
// trigger livereload
|
||||
lrServer.changed({
|
||||
body: {
|
||||
files: [lrPath]
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
if (!hasWatch) {
|
||||
return waitForCtrlC();
|
||||
}
|
||||
|
||||
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 file watcher and live reloading',
|
||||
defaults: true
|
||||
},
|
||||
{
|
||||
name: 'live',
|
||||
description: 'Enable live reloading',
|
||||
defaults: true
|
||||
},
|
||||
options.log,
|
||||
options.format
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
server = new Server();
|
||||
var hasWatch = kwargs['watch'];
|
||||
var hasLiveReloading = kwargs['live'];
|
||||
|
||||
return Promise()
|
||||
.then(function() {
|
||||
if (!hasWatch || !hasLiveReloading) {
|
||||
return;
|
||||
}
|
||||
|
||||
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('');
|
||||
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
return generateBook(args, kwargs);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,34 +1,41 @@
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
|
||||
var events = require('events');
|
||||
var http = require('http');
|
||||
var send = require('send');
|
||||
var util = require('util');
|
||||
var url = require('url');
|
||||
|
||||
var Server = function() {
|
||||
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 true if the server is running
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
Server.prototype.isRunning = function() {
|
||||
return this.running != null;
|
||||
return !!this.running;
|
||||
};
|
||||
|
||||
// Stop the server
|
||||
/**
|
||||
Stop the server
|
||||
|
||||
@return {Promise}
|
||||
*/
|
||||
Server.prototype.stop = function() {
|
||||
var that = this;
|
||||
if (!this.isRunning()) return Q();
|
||||
if (!this.isRunning()) return Promise();
|
||||
|
||||
var d = Q.defer();
|
||||
var d = Promise.defer();
|
||||
this.running.close(function(err) {
|
||||
that.running = null;
|
||||
that.emit("state", false);
|
||||
that.emit('state', false);
|
||||
|
||||
if (err) d.reject(err);
|
||||
else d.resolve();
|
||||
@@ -41,14 +48,19 @@ Server.prototype.stop = function() {
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
Start the server
|
||||
|
||||
@return {Promise}
|
||||
*/
|
||||
Server.prototype.start = function(dir, port) {
|
||||
var that = this, pre = Q();
|
||||
var that = this, pre = Promise();
|
||||
port = port || 8004;
|
||||
|
||||
if (that.isRunning()) pre = this.stop();
|
||||
return pre
|
||||
.then(function() {
|
||||
var d = Q.defer();
|
||||
var d = Promise.defer();
|
||||
|
||||
that.running = http.createServer(function(req, res){
|
||||
// Render error
|
||||
@@ -65,8 +77,9 @@ Server.prototype.start = function(dir, port) {
|
||||
}
|
||||
|
||||
// Send file
|
||||
send(req, url.parse(req.url).pathname)
|
||||
.root(dir)
|
||||
send(req, url.parse(req.url).pathname, {
|
||||
root: dir
|
||||
})
|
||||
.on('error', error)
|
||||
.on('directory', redirect)
|
||||
.pipe(res);
|
||||
@@ -85,12 +98,12 @@ Server.prototype.start = function(dir, port) {
|
||||
|
||||
that.port = port;
|
||||
that.dir = dir;
|
||||
that.emit("state", true);
|
||||
that.emit('state', true);
|
||||
d.resolve();
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Server;
|
||||
@@ -0,0 +1,46 @@
|
||||
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,243 +0,0 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var path = require("path");
|
||||
var semver = require('semver');
|
||||
|
||||
var pkg = require('../package.json');
|
||||
var fs = require("./utils/fs");
|
||||
var i18n = require("./utils/i18n");
|
||||
|
||||
// Default plugins added to each books
|
||||
var defaultsPlugins = [];
|
||||
|
||||
// Normalize a list of plugins to use
|
||||
function normalizePluginsList(plugins) {
|
||||
// Normalize list to an array
|
||||
plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []);
|
||||
|
||||
// Remove empty parts
|
||||
plugins = _.compact(plugins);
|
||||
|
||||
// Divide as {name, version} to handle format like "myplugin@1.0.0"
|
||||
plugins = _.map(plugins, function(plugin) {
|
||||
if (plugin.name) return plugin;
|
||||
|
||||
var parts = plugin.split("@");
|
||||
return {
|
||||
'name': parts[0],
|
||||
'version': parts[1] // optional
|
||||
}
|
||||
});
|
||||
|
||||
// List plugins to remove
|
||||
var toremove = _.chain(plugins)
|
||||
.filter(function(plugin) {
|
||||
return plugin.name.length > 0 && plugin.name[0] == "-";
|
||||
})
|
||||
.map(function(plugin) {
|
||||
return plugin.name.slice(1);
|
||||
})
|
||||
.value();
|
||||
|
||||
// Merge with defaults
|
||||
plugins = _.chain(plugins)
|
||||
.concat(_.map(defaultsPlugins, function(plugin) {
|
||||
return { 'name': plugin }
|
||||
}))
|
||||
.uniq()
|
||||
.value();
|
||||
|
||||
// Build final list
|
||||
plugins = _.filter(plugins, function(plugin) {
|
||||
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-");
|
||||
});
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
// Normalize a list of plugin name to use
|
||||
function normalizePluginsNames(plugins) {
|
||||
return _.pluck(normalizePluginsList(plugins), "name");
|
||||
};
|
||||
|
||||
|
||||
var Configuration = function(book, options) {
|
||||
var that = this;
|
||||
|
||||
this.book = book;
|
||||
|
||||
this.options = _.cloneDeep(Configuration.DEFAULT);
|
||||
this.options = _.merge(this.options, options || {});
|
||||
|
||||
// options.input == book.root
|
||||
Object.defineProperty(this.options, "input", {
|
||||
get: function () {
|
||||
return that.book.root;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Read and parse the configuration
|
||||
Configuration.prototype.load = function() {
|
||||
var that = this;
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
try {
|
||||
var _config = require(path.resolve(that.book.root, that.options.configFile));
|
||||
that.options = _.merge(
|
||||
that.options,
|
||||
_.omit(_config, 'configFile', 'defaultsPlugins', 'generator', 'extension')
|
||||
);
|
||||
}
|
||||
catch(err) {
|
||||
if (err instanceof SyntaxError) return Q.reject(err);
|
||||
return Q();
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
if (!semver.satisfies(pkg.version, that.options.gitbook)) {
|
||||
throw "GitBook version doesn't satisfy version required by the book: "+that.options.gitbook;
|
||||
}
|
||||
if (that.options.gitbook == "*") {
|
||||
that.book.log.warn.ln("you should specify a gitbook version to use in your book.json, for example: "+(_.first(pkg.version.split("."))+".x.x"));
|
||||
}
|
||||
|
||||
that.options.output = path.resolve(that.options.output || path.join(that.book.root, "_book"));
|
||||
that.options.plugins = normalizePluginsList(that.options.plugins);
|
||||
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || "");
|
||||
that.options.plugins = _.union(that.options.plugins, that.options.defaultsPlugins);
|
||||
that.options.plugins = _.uniq(that.options.plugins, "name");
|
||||
|
||||
// Default value for text direction (from language)
|
||||
if (!that.options.direction) {
|
||||
var lang = i18n.getByLanguage(that.options.language);
|
||||
if (lang) that.options.direction = lang.direction;
|
||||
}
|
||||
|
||||
that.options.gitbook = pkg.version;
|
||||
});
|
||||
};
|
||||
|
||||
// Extend the configuration
|
||||
Configuration.prototype.extend = function(options) {
|
||||
_.extend(this.options, options);
|
||||
};
|
||||
|
||||
// Get structure file
|
||||
Configuration.prototype.getStructure = function(name) {
|
||||
return this.options.structure[name].split(".").slice(0, -1).join(".");
|
||||
};
|
||||
|
||||
// Return normalized language
|
||||
Configuration.prototype.normalizeLanguage = function() {
|
||||
return i18n.normalizeLanguage(this.options.language);
|
||||
};
|
||||
|
||||
|
||||
// Default configuration
|
||||
Configuration.DEFAULT = {
|
||||
// Options that can't be extend
|
||||
"configFile": "book",
|
||||
"generator": "website",
|
||||
"extension": null,
|
||||
|
||||
// Book metadats (somes are extracted from the README by default)
|
||||
"title": null,
|
||||
"description": null,
|
||||
"isbn": null,
|
||||
"language": "en",
|
||||
"direction": null,
|
||||
"author": null,
|
||||
|
||||
// version of gitbook to use
|
||||
"gitbook": "*",
|
||||
|
||||
// Structure
|
||||
"structure": {
|
||||
"langs": "LANGS.md",
|
||||
"readme": "README.md",
|
||||
"glossary": "GLOSSARY.md",
|
||||
"summary": "SUMMARY.md"
|
||||
},
|
||||
|
||||
// CSS Styles
|
||||
"styles": {
|
||||
"website": "styles/website.css",
|
||||
"ebook": "styles/ebook.css",
|
||||
"pdf": "styles/pdf.css",
|
||||
"mobi": "styles/mobi.css",
|
||||
"epub": "styles/epub.css"
|
||||
},
|
||||
|
||||
// Plugins list, can contain "-name" for removing default plugins
|
||||
"plugins": [],
|
||||
|
||||
// Global configuration for plugins
|
||||
"pluginsConfig": {
|
||||
"fontSettings": {
|
||||
"theme": null, //"sepia", "night" or "white",
|
||||
"family": "sans",// "serif" or "sans",
|
||||
"size": 2 // 1 - 4
|
||||
}
|
||||
},
|
||||
|
||||
// Variables for templating
|
||||
"variables": {},
|
||||
|
||||
// Set another theme with your own layout
|
||||
// It's recommended to use plugins or add more options for default theme, though
|
||||
// See https://github.com/GitbookIO/gitbook/issues/209
|
||||
"theme": path.resolve(__dirname, '../theme'),
|
||||
|
||||
// Links in template (null: default, false: remove, string: new value)
|
||||
"links": {
|
||||
// Custom links at top of sidebar
|
||||
"sidebar": {
|
||||
//"Custom link name": "https://customlink.com"
|
||||
},
|
||||
|
||||
// Sharing links
|
||||
"sharing": {
|
||||
"google": null,
|
||||
"facebook": null,
|
||||
"twitter": null,
|
||||
"weibo": null,
|
||||
"all": null
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Options for PDF generation
|
||||
"pdf": {
|
||||
// Add toc at the end of the file
|
||||
"toc": true,
|
||||
|
||||
// Add page numbers to the bottom of every page
|
||||
"pageNumbers": false,
|
||||
|
||||
// Font size for the file content
|
||||
"fontSize": 12,
|
||||
|
||||
// Paper size for the pdf
|
||||
// Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
|
||||
"paperSize": "a4",
|
||||
|
||||
// Margin (in pts)
|
||||
// Note: 72 pts equals 1 inch
|
||||
"margin": {
|
||||
"right": 62,
|
||||
"left": 62,
|
||||
"top": 56,
|
||||
"bottom": 56
|
||||
},
|
||||
|
||||
//Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
|
||||
"headerTemplate": "",
|
||||
|
||||
//Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
|
||||
"footerTemplate": ""
|
||||
}
|
||||
};
|
||||
|
||||
module.exports= Configuration;
|
||||
@@ -0,0 +1,6 @@
|
||||
var Immutable = require('immutable');
|
||||
var jsonSchemaDefaults = require('json-schema-defaults');
|
||||
|
||||
var schema = require('./configSchema');
|
||||
|
||||
module.exports = Immutable.fromJS(jsonSchemaDefaults(schema));
|
||||
@@ -0,0 +1,5 @@
|
||||
// Configuration files to test (sorted)
|
||||
module.exports = [
|
||||
'book.js',
|
||||
'book.json'
|
||||
];
|
||||
@@ -0,0 +1,188 @@
|
||||
module.exports = {
|
||||
'$schema': 'http://json-schema.org/schema#',
|
||||
'id': 'https://gitbook.com/schemas/book.json',
|
||||
'title': 'GitBook Configuration',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'root': {
|
||||
'type': 'string',
|
||||
'title': 'Path fro the root folder containing the book\'s content'
|
||||
},
|
||||
'title': {
|
||||
'type': 'string',
|
||||
'title': 'Title of the book, default is extracted from README'
|
||||
},
|
||||
'isbn': {
|
||||
'type': 'string',
|
||||
'title': 'ISBN for published book'
|
||||
},
|
||||
'author': {
|
||||
'type': 'string',
|
||||
'title': 'Name of the author'
|
||||
},
|
||||
'gitbook': {
|
||||
'type': 'string',
|
||||
'default': '*',
|
||||
'title': 'GitBook version to match'
|
||||
},
|
||||
'direction': {
|
||||
'type': 'string',
|
||||
'enum': ['ltr', 'rtl'],
|
||||
'title': 'Direction of texts, default is detected in the pages'
|
||||
},
|
||||
'theme': {
|
||||
'type': 'string',
|
||||
'default': 'default',
|
||||
'title': 'Name of the theme plugin to use'
|
||||
},
|
||||
'variables': {
|
||||
'type': 'object',
|
||||
'title': 'Templating context variables'
|
||||
},
|
||||
'plugins': {
|
||||
'oneOf': [
|
||||
{ '$ref': '#/definitions/pluginsArray' },
|
||||
{ '$ref': '#/definitions/pluginsString' }
|
||||
],
|
||||
'default': []
|
||||
},
|
||||
'pluginsConfig': {
|
||||
'type': 'object',
|
||||
'title': 'Configuration for plugins'
|
||||
},
|
||||
'structure': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'langs': {
|
||||
'default': 'LANGS.md',
|
||||
'type': 'string',
|
||||
'title': 'File to use as languages index',
|
||||
'pattern': '^[0-9a-zA-Z ... ]+$'
|
||||
},
|
||||
'readme': {
|
||||
'default': 'README.md',
|
||||
'type': 'string',
|
||||
'title': 'File to use as preface',
|
||||
'pattern': '^[0-9a-zA-Z ... ]+$'
|
||||
},
|
||||
'glossary': {
|
||||
'default': 'GLOSSARY.md',
|
||||
'type': 'string',
|
||||
'title': 'File to use as glossary index',
|
||||
'pattern': '^[0-9a-zA-Z ... ]+$'
|
||||
},
|
||||
'summary': {
|
||||
'default': 'SUMMARY.md',
|
||||
'type': 'string',
|
||||
'title': 'File to use as table of contents',
|
||||
'pattern': '^[0-9a-zA-Z ... ]+$'
|
||||
}
|
||||
},
|
||||
'additionalProperties': false
|
||||
},
|
||||
'pdf': {
|
||||
'type': 'object',
|
||||
'title': 'PDF specific configurations',
|
||||
'properties': {
|
||||
'pageNumbers': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'title': 'Add page numbers to the bottom of every page'
|
||||
},
|
||||
'fontSize': {
|
||||
'type': 'integer',
|
||||
'minimum': 8,
|
||||
'maximum': 30,
|
||||
'default': 12,
|
||||
'title': 'Font size for the PDF output'
|
||||
},
|
||||
'fontFamily': {
|
||||
'type': 'string',
|
||||
'default': 'Arial',
|
||||
'title': 'Font family for the PDF output'
|
||||
},
|
||||
'paperSize': {
|
||||
'type': 'string',
|
||||
'enum': ['a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'legal', 'letter'],
|
||||
'default': 'a4',
|
||||
'title': 'Paper size for the PDF'
|
||||
},
|
||||
'chapterMark': {
|
||||
'type': 'string',
|
||||
'enum': ['pagebreak', 'rule', 'both', 'none'],
|
||||
'default': 'pagebreak',
|
||||
'title': 'How to mark detected chapters'
|
||||
},
|
||||
'pageBreaksBefore': {
|
||||
'type': 'string',
|
||||
'default': '/',
|
||||
'title': 'An XPath expression. Page breaks are inserted before the specified elements. To disable use the expression: "/"'
|
||||
},
|
||||
'margin': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'right': {
|
||||
'type': 'integer',
|
||||
'title': 'Right Margin',
|
||||
'minimum': 0,
|
||||
'maximum': 100,
|
||||
'default': 62
|
||||
},
|
||||
'left': {
|
||||
'type': 'integer',
|
||||
'title': 'Left Margin',
|
||||
'minimum': 0,
|
||||
'maximum': 100,
|
||||
'default': 62
|
||||
},
|
||||
'top': {
|
||||
'type': 'integer',
|
||||
'title': 'Top Margin',
|
||||
'minimum': 0,
|
||||
'maximum': 100,
|
||||
'default': 56
|
||||
},
|
||||
'bottom': {
|
||||
'type': 'integer',
|
||||
'title': 'Bottom Margin',
|
||||
'minimum': 0,
|
||||
'maximum': 100,
|
||||
'default': 56
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'required': [],
|
||||
'definitions': {
|
||||
'pluginsArray': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'oneOf': [
|
||||
{ '$ref': '#/definitions/pluginObject' },
|
||||
{ '$ref': '#/definitions/pluginString' }
|
||||
]
|
||||
}
|
||||
},
|
||||
'pluginsString': {
|
||||
'type': 'string'
|
||||
},
|
||||
'pluginString': {
|
||||
'type': 'string'
|
||||
},
|
||||
'pluginObject': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string'
|
||||
},
|
||||
'version': {
|
||||
'type': 'string'
|
||||
}
|
||||
},
|
||||
'additionalProperties': false,
|
||||
'required': ['name']
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
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 };
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
var Immutable = require('immutable');
|
||||
var moment = require('moment');
|
||||
|
||||
module.exports = Immutable.Map({
|
||||
// Format a date
|
||||
// ex: 'MMMM Do YYYY, h:mm:ss a
|
||||
date: function(time, format) {
|
||||
return moment(time).format(format);
|
||||
},
|
||||
|
||||
// Relative Time
|
||||
dateFromNow: function(time) {
|
||||
return moment(time).fromNow();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
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'
|
||||
]);
|
||||
@@ -0,0 +1,6 @@
|
||||
// Files containing ignore pattner (sorted by priority)
|
||||
module.exports = [
|
||||
'.ignore',
|
||||
'.gitignore',
|
||||
'.bookignore'
|
||||
];
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
module.exports = '_assets';
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = [
|
||||
'init',
|
||||
'finish',
|
||||
'finish:before',
|
||||
'config',
|
||||
'page',
|
||||
'page:before'
|
||||
];
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
/*
|
||||
All GitBook plugins are NPM packages starting with this prefix.
|
||||
*/
|
||||
module.exports = 'gitbook-plugin-';
|
||||
@@ -0,0 +1,6 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
module.exports = Immutable.List([
|
||||
'js',
|
||||
'css'
|
||||
]);
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
module.exports = '_layouts';
|
||||
@@ -0,0 +1,83 @@
|
||||
jest.autoMockOff();
|
||||
|
||||
describe('MockFS', function() {
|
||||
var createMockFS = require('../mock');
|
||||
var fs = createMockFS({
|
||||
'README.md': 'Hello World',
|
||||
'SUMMARY.md': '# Summary',
|
||||
'folder': {
|
||||
'test.md': 'Cool',
|
||||
'folder2': {
|
||||
'hello.md': 'Hello',
|
||||
'world.md': 'World'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('exists', function() {
|
||||
pit('must return true for a file', function() {
|
||||
return fs.exists('README.md')
|
||||
.then(function(result) {
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
pit('must return false for a non existing file', function() {
|
||||
return fs.exists('README_NOTEXISTS.md')
|
||||
.then(function(result) {
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
pit('must return true for a directory', function() {
|
||||
return fs.exists('folder')
|
||||
.then(function(result) {
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
pit('must return true for a deep file', function() {
|
||||
return fs.exists('folder/test.md')
|
||||
.then(function(result) {
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
pit('must return true for a deep file (2)', function() {
|
||||
return fs.exists('folder/folder2/hello.md')
|
||||
.then(function(result) {
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('readAsString', function() {
|
||||
pit('must return content for a file', function() {
|
||||
return fs.readAsString('README.md')
|
||||
.then(function(result) {
|
||||
expect(result).toBe('Hello World');
|
||||
});
|
||||
});
|
||||
|
||||
pit('must return content for a deep file', function() {
|
||||
return fs.readAsString('folder/test.md')
|
||||
.then(function(result) {
|
||||
expect(result).toBe('Cool');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('readDir', function() {
|
||||
pit('must return content for a directory', function() {
|
||||
return fs.readDir('./')
|
||||
.then(function(files) {
|
||||
expect(files.size).toBe(3);
|
||||
expect(files.includes('README.md')).toBeTruthy();
|
||||
expect(files.includes('SUMMARY.md')).toBeTruthy();
|
||||
expect(files.includes('folder/')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
var path = require('path');
|
||||
var is = require('is');
|
||||
var Buffer = require('buffer').Buffer;
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var FS = require('../models/fs');
|
||||
var error = require('../utils/error');
|
||||
|
||||
/**
|
||||
Create a fake filesystem for unit testing GitBook.
|
||||
|
||||
@param {Map<String:String|Map>}
|
||||
*/
|
||||
function createMockFS(files) {
|
||||
files = Immutable.fromJS(files);
|
||||
var mtime = new Date();
|
||||
|
||||
function getFile(filePath) {
|
||||
var parts = path.normalize(filePath).split(path.sep);
|
||||
return parts.reduce(function(list, part, i) {
|
||||
if (!list) return null;
|
||||
|
||||
var file;
|
||||
|
||||
if (!part || part === '.') file = list;
|
||||
else file = list.get(part);
|
||||
|
||||
if (!file) return null;
|
||||
|
||||
if (is.string(file)) {
|
||||
if (i === (parts.length - 1)) return file;
|
||||
else return null;
|
||||
}
|
||||
|
||||
return file;
|
||||
}, files);
|
||||
}
|
||||
|
||||
function fsExists(filePath) {
|
||||
return Boolean(getFile(filePath) !== null);
|
||||
}
|
||||
|
||||
function fsReadFile(filePath) {
|
||||
var file = getFile(filePath);
|
||||
if (!is.string(file)) {
|
||||
throw error.FileNotFoundError({
|
||||
filename: filePath
|
||||
});
|
||||
}
|
||||
|
||||
return new Buffer(file, 'utf8');
|
||||
}
|
||||
|
||||
function fsStatFile(filePath) {
|
||||
var file = getFile(filePath);
|
||||
if (!file) {
|
||||
throw error.FileNotFoundError({
|
||||
filename: filePath
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
mtime: mtime
|
||||
};
|
||||
}
|
||||
|
||||
function fsReadDir(filePath) {
|
||||
var dir = getFile(filePath);
|
||||
if (!dir || is.string(dir)) {
|
||||
throw error.FileNotFoundError({
|
||||
filename: filePath
|
||||
});
|
||||
}
|
||||
|
||||
return dir
|
||||
.map(function(content, name) {
|
||||
if (!is.string(content)) {
|
||||
name = name + '/';
|
||||
}
|
||||
|
||||
return name;
|
||||
})
|
||||
.valueSeq();
|
||||
}
|
||||
|
||||
return FS.create({
|
||||
root: '',
|
||||
fsExists: fsExists,
|
||||
fsReadFile: fsReadFile,
|
||||
fsStatFile: fsStatFile,
|
||||
fsReadDir: fsReadDir
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = createMockFS;
|
||||
@@ -0,0 +1,42 @@
|
||||
var path = require('path');
|
||||
var Immutable = require('immutable');
|
||||
var fresh = require('fresh-require');
|
||||
|
||||
var fs = require('../utils/fs');
|
||||
var FS = require('../models/fs');
|
||||
|
||||
function fsReadDir(folder) {
|
||||
return fs.readdir(folder)
|
||||
.then(function(files) {
|
||||
files = Immutable.List(files);
|
||||
|
||||
return files
|
||||
.map(function(file) {
|
||||
if (file == '.' || file == '..') return;
|
||||
|
||||
var stat = fs.statSync(path.join(folder, file));
|
||||
if (stat.isDirectory()) file = file + path.sep;
|
||||
return file;
|
||||
})
|
||||
.filter(function(file) {
|
||||
return Boolean(file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function fsLoadObject(filename) {
|
||||
return fresh(filename, require);
|
||||
}
|
||||
|
||||
module.exports = function createNodeFS(root) {
|
||||
return FS.create({
|
||||
root: root,
|
||||
|
||||
fsExists: fs.exists,
|
||||
fsReadFile: fs.readFile,
|
||||
fsStatFile: fs.stat,
|
||||
fsReadDir: fsReadDir,
|
||||
fsLoadObject: fsLoadObject,
|
||||
fsReadAsStream: fs.readStream
|
||||
});
|
||||
};
|
||||
@@ -1,78 +0,0 @@
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var fs = require("./utils/fs");
|
||||
|
||||
var Plugin = require("./plugin");
|
||||
|
||||
var BaseGenerator = function(book) {
|
||||
this.book = book;
|
||||
|
||||
Object.defineProperty(this, "options", {
|
||||
get: function () {
|
||||
return this.book.options;
|
||||
}
|
||||
});
|
||||
|
||||
_.bindAll(this);
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.callHook = function(name, data) {
|
||||
return this.book.plugins.hook(name, data);
|
||||
};
|
||||
|
||||
// Prepare the genertor
|
||||
BaseGenerator.prototype.prepare = function() {
|
||||
var that = this;
|
||||
|
||||
return that.callHook("init");
|
||||
};
|
||||
|
||||
// Write a parsed file to the output
|
||||
BaseGenerator.prototype.convertFile = function(input) {
|
||||
return Q.reject(new Error("Could not convert "+input));
|
||||
};
|
||||
|
||||
// Copy file to the output (non parsable)
|
||||
BaseGenerator.prototype.transferFile = function(input) {
|
||||
return fs.copy(
|
||||
path.join(this.book.root, input),
|
||||
path.join(this.options.output, input)
|
||||
);
|
||||
};
|
||||
|
||||
// Copy a folder to the output
|
||||
BaseGenerator.prototype.transferFolder = function(input) {
|
||||
return fs.mkdirp(
|
||||
path.join(this.book.options.output, input)
|
||||
);
|
||||
};
|
||||
|
||||
// Copy the cover picture
|
||||
BaseGenerator.prototype.copyCover = function() {
|
||||
var that = this;
|
||||
|
||||
return Q.all([
|
||||
fs.copy(path.join(that.book.root, "cover.jpg"), path.join(that.options.output, "cover.jpg")),
|
||||
fs.copy(path.join(that.book.root, "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
|
||||
])
|
||||
.fail(function() {
|
||||
// If orignaly from multi-lang, try copy from parent
|
||||
if (!that.book.isSubBook()) return;
|
||||
|
||||
return Q.all([
|
||||
fs.copy(path.join(that.book.parentRoot(), "cover.jpg"), path.join(that.options.output, "cover.jpg")),
|
||||
fs.copy(path.join(that.book.parentRoot(), "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
|
||||
]);
|
||||
})
|
||||
.fail(function(err) {
|
||||
return Q();
|
||||
});
|
||||
};
|
||||
|
||||
// At teh end of the generation
|
||||
BaseGenerator.prototype.finish = function() {
|
||||
return Q.reject(new Error("Could not finish generation"));
|
||||
};
|
||||
|
||||
module.exports = BaseGenerator;
|
||||
@@ -1,139 +0,0 @@
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
var fs = require("../utils/fs");
|
||||
var stringUtils = require("../utils/string");
|
||||
var BaseGenerator = require("./website");
|
||||
|
||||
var Generator = function(book, format) {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
|
||||
// eBook format
|
||||
this.ebookFormat = format;
|
||||
|
||||
// Resources namespace
|
||||
this.namespace = "ebook";
|
||||
|
||||
// Styles to use
|
||||
this.styles = _.compact(["ebook", this.ebookFormat]);
|
||||
|
||||
// Convert images (svg -> png)
|
||||
this.convertImages = true;
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
Generator.prototype.prepareTemplates = function() {
|
||||
this.templates["page"] = this.book.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.html');
|
||||
this.templates["summary"] = this.book.plugins.template("ebook:summary") || path.resolve(this.options.theme, 'templates/ebook/summary.html');
|
||||
this.templates["glossary"] = this.book.plugins.template("ebook:glossary") || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
|
||||
|
||||
return Q();
|
||||
};
|
||||
|
||||
// Generate table of contents
|
||||
Generator.prototype.writeSummary = function() {
|
||||
var that = this;
|
||||
|
||||
that.book.log.info.ln("write SUMMARY.html");
|
||||
return this._writeTemplate(this.templates["summary"], {}, path.join(this.options.output, "SUMMARY.html"));
|
||||
};
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
var that = this;
|
||||
|
||||
return Q()
|
||||
.then(this.copyAssets)
|
||||
.then(this.copyCover)
|
||||
.then(this.writeGlossary)
|
||||
.then(this.writeSummary)
|
||||
.then(function() {
|
||||
if (!that.ebookFormat) return Q();
|
||||
|
||||
var d = Q.defer();
|
||||
|
||||
if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) {
|
||||
that.options.cover = path.join(that.options.output, "cover.jpg");
|
||||
}
|
||||
|
||||
var _options = {
|
||||
"--cover": that.options.cover,
|
||||
"--title": that.options.title,
|
||||
"--comments": that.options.description,
|
||||
"--isbn": that.options.isbn,
|
||||
"--authors": that.options.author,
|
||||
"--language": that.options.language,
|
||||
"--book-producer": "GitBook",
|
||||
"--publisher": "GitBook",
|
||||
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
|
||||
"--chapter-mark": "pagebreak",
|
||||
"--page-breaks-before": "/",
|
||||
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]",
|
||||
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]",
|
||||
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]",
|
||||
"--no-chapters-in-toc": true,
|
||||
"--max-levels": "1",
|
||||
"--breadth-first": true
|
||||
};
|
||||
|
||||
if (that.ebookFormat == "pdf") {
|
||||
var pdfOptions = that.options.pdf;
|
||||
|
||||
_.extend(_options, {
|
||||
"--margin-left": String(pdfOptions.margin.left),
|
||||
"--margin-right": String(pdfOptions.margin.right),
|
||||
"--margin-top": String(pdfOptions.margin.top),
|
||||
"--margin-bottom": String(pdfOptions.margin.bottom),
|
||||
"--pdf-default-font-size": String(pdfOptions.fontSize),
|
||||
"--pdf-mono-font-size": String(pdfOptions.fontSize),
|
||||
"--paper-size": String(pdfOptions.paperSize),
|
||||
"--pdf-page-numbers": Boolean(pdfOptions.pageNumbers),
|
||||
"--pdf-header-template": String(pdfOptions.headerTemplate) || "<p class='header'><span>"+that.options.title+"</span></p>",
|
||||
"--pdf-footer-template": String(pdfOptions.footerTemplate) || "<p class='footer'><span>_SECTION_</span> <span style='float:right;'>_PAGENUM_</span></p>"
|
||||
});
|
||||
} else if (that.ebookFormat == "epub") {
|
||||
_.extend(_options, {
|
||||
"--dont-split-on-page-breaks": true
|
||||
});
|
||||
}
|
||||
|
||||
var command = [
|
||||
"ebook-convert",
|
||||
path.join(that.options.output, "SUMMARY.html"),
|
||||
path.join(that.options.output, "index."+that.ebookFormat),
|
||||
stringUtils.optionsToShellArgs(_options)
|
||||
].join(" ");
|
||||
|
||||
that.book.log.info("start conversion to", that.ebookFormat, "....");
|
||||
|
||||
var child = exec(command, function (error, stdout, stderr) {
|
||||
if (error) {
|
||||
that.book.log.info.fail();
|
||||
|
||||
if (error.code == 127) {
|
||||
error.message = "Need to install ebook-convert from Calibre";
|
||||
} else {
|
||||
error.message = error.message + " "+stdout;
|
||||
}
|
||||
return d.reject(error);
|
||||
}
|
||||
|
||||
that.book.log.info.ok();
|
||||
d.resolve();
|
||||
});
|
||||
|
||||
child.stdout.on('data', function (data) {
|
||||
that.book.log.debug(data);
|
||||
});
|
||||
|
||||
child.stderr.on('data', function (data) {
|
||||
that.book.log.debug(data);
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Generator;
|
||||
@@ -1,11 +0,0 @@
|
||||
var _ = require("lodash");
|
||||
var EbookGenerator = require("./ebook");
|
||||
|
||||
module.exports = {
|
||||
json: require("./json"),
|
||||
website: require("./website"),
|
||||
ebook: EbookGenerator,
|
||||
pdf: _.partialRight(EbookGenerator, "pdf"),
|
||||
mobi: _.partialRight(EbookGenerator, "mobi"),
|
||||
epub: _.partialRight(EbookGenerator, "epub")
|
||||
};
|
||||
@@ -1,76 +0,0 @@
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
|
||||
var fs = require("../utils/fs");
|
||||
var BaseGenerator = require("../generator");
|
||||
var links = require("../utils/links");
|
||||
|
||||
var Generator = function() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
// Ignore some methods
|
||||
Generator.prototype.transferFile = function(input) { };
|
||||
|
||||
// Convert an input file
|
||||
Generator.prototype.convertFile = function(input) {
|
||||
var that = this;
|
||||
|
||||
return that.book.parsePage(input)
|
||||
.then(function(page) {
|
||||
var json = {
|
||||
progress: page.progress,
|
||||
sections: page.sections
|
||||
};
|
||||
|
||||
var output = links.changeExtension(page.path, ".json");
|
||||
output = path.join(that.options.output, output);
|
||||
|
||||
return fs.writeFile(
|
||||
output,
|
||||
JSON.stringify(json, null, 4)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// Finish generation
|
||||
Generator.prototype.finish = function() {
|
||||
return this.writeReadme();
|
||||
};
|
||||
|
||||
// Write README.json
|
||||
Generator.prototype.writeReadme = function() {
|
||||
var that = this;
|
||||
var mainlang, langs;
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
langs = that.book.langs;
|
||||
mainLang = langs.length > 0? _.first(langs).lang : null;
|
||||
|
||||
readme = links.changeExtension(that.book.readmeFile, ".json");
|
||||
|
||||
// Read readme from main language
|
||||
return fs.readFile(
|
||||
mainLang? path.join(that.options.output, mainLang, readme) : path.join(that.options.output, readme)
|
||||
);
|
||||
})
|
||||
.then(function(content) {
|
||||
// Extend it with infos about the languages
|
||||
var json = JSON.parse(content);
|
||||
_.extend(json, {
|
||||
langs: langs
|
||||
});
|
||||
|
||||
// Write it as README.json
|
||||
return fs.writeFile(
|
||||
path.join(that.options.output, "README.json"),
|
||||
JSON.stringify(json, null, 4)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Generator;
|
||||
@@ -1,279 +0,0 @@
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
|
||||
var nunjucks = require("nunjucks");
|
||||
var AutoEscapeExtension = require("nunjucks-autoescape");
|
||||
var FilterExtension = require("nunjucks-filter");
|
||||
var I18nExtension = require("nunjucks-i18n");
|
||||
|
||||
var fs = require("../utils/fs");
|
||||
var BaseGenerator = require("../generator");
|
||||
var links = require("../utils/links");
|
||||
var pageUtil = require("../utils/page");
|
||||
var i18n = require("../utils/i18n");
|
||||
|
||||
var pkg = require("../../package.json");
|
||||
|
||||
var Generator = function() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
|
||||
// Revision
|
||||
this.revision = new Date();
|
||||
|
||||
// Resources namespace
|
||||
this.namespace = "website";
|
||||
|
||||
// Style to integrates in the output
|
||||
this.styles = ["website"];
|
||||
|
||||
// Convert images (svg -> png)
|
||||
this.convertImages = false;
|
||||
|
||||
// Templates
|
||||
this.templates = {};
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
// Prepare the genertor
|
||||
Generator.prototype.prepare = function() {
|
||||
return BaseGenerator.prototype.prepare.apply(this)
|
||||
.then(this.prepareStyles)
|
||||
.then(this.prepareTemplates)
|
||||
.then(this.prepareTemplateEngine);
|
||||
};
|
||||
|
||||
// Prepare all styles
|
||||
Generator.prototype.prepareStyles = function() {
|
||||
var that = this;
|
||||
|
||||
this.styles = _.chain(this.styles)
|
||||
.map(function(style) {
|
||||
var stylePath = that.options.styles[style];
|
||||
if (fs.existsSync(path.resolve(that.book.root, stylePath))) {
|
||||
return stylePath;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.compact()
|
||||
.value();
|
||||
|
||||
return Q();
|
||||
};
|
||||
|
||||
// Prepare templates
|
||||
Generator.prototype.prepareTemplates = function() {
|
||||
this.templates["page"] = this.book.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/website/page.html');
|
||||
this.templates["langs"] = this.book.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/website/langs.html');
|
||||
this.templates["glossary"] = this.book.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/website/glossary.html');
|
||||
|
||||
return Q();
|
||||
};
|
||||
|
||||
// Prepare template engine
|
||||
Generator.prototype.prepareTemplateEngine = function() {
|
||||
var that = this;
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
var language = that.book.config.normalizeLanguage();
|
||||
|
||||
if (!i18n.getByLanguage(language) && language != "en") {
|
||||
that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLanguages().join(", ")+")");
|
||||
}
|
||||
|
||||
var folders = _.chain(that.templates)
|
||||
.values()
|
||||
.map(path.dirname)
|
||||
.uniq()
|
||||
.value();
|
||||
|
||||
that.env = new nunjucks.Environment(
|
||||
new nunjucks.FileSystemLoader(folders),
|
||||
{
|
||||
autoescape: true
|
||||
}
|
||||
);
|
||||
|
||||
// Add filter
|
||||
that.env.addFilter("contentLink", that.book.contentLink.bind(that.book));
|
||||
that.env.addFilter('lvl', function(lvl) {
|
||||
return lvl.split(".").length;
|
||||
});
|
||||
|
||||
// Add extension
|
||||
that.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(that.env));
|
||||
that.env.addExtension('FilterExtension', new FilterExtension(that.env));
|
||||
that.env.addExtension('I18nExtension', new I18nExtension({
|
||||
env: that.env,
|
||||
translations: i18n.getLocales(),
|
||||
locale: "language"
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
// Finis generation
|
||||
Generator.prototype.finish = function() {
|
||||
return this.copyAssets()
|
||||
.then(this.copyCover)
|
||||
.then(this.writeGlossary)
|
||||
.then(this.writeSearchIndex)
|
||||
.then(this.writeLangsIndex)
|
||||
};
|
||||
|
||||
// Convert an input file
|
||||
Generator.prototype.convertFile = function(input) {
|
||||
var that = this;
|
||||
|
||||
return that.book.parsePage(input, {
|
||||
convertImages: that.convertImages,
|
||||
interpolateTemplate: function(page) {
|
||||
return that.callHook("page:before", page);
|
||||
},
|
||||
interpolateContent: function(page) {
|
||||
return that.callHook("page", page);
|
||||
}
|
||||
})
|
||||
.then(function(page) {
|
||||
var relativeOutput = that.book.contentLink(page.path);
|
||||
var output = path.join(that.options.output, relativeOutput);
|
||||
|
||||
var basePath = path.relative(path.dirname(output), that.options.output) || ".";
|
||||
if (process.platform === 'win32') basePath = basePath.replace(/\\/g, '/');
|
||||
|
||||
that.book.log.debug.ln("write parsed file", page.path, "to", relativeOutput);
|
||||
|
||||
return that._writeTemplate(that.templates["page"], {
|
||||
progress: page.progress,
|
||||
|
||||
_input: page.path,
|
||||
content: page.sections,
|
||||
|
||||
basePath: basePath,
|
||||
staticBase: links.join(basePath, "gitbook")
|
||||
}, output);
|
||||
});
|
||||
};
|
||||
|
||||
// Write the index for langs
|
||||
Generator.prototype.writeLangsIndex = function() {
|
||||
var that = this;
|
||||
if (!this.book.langs.length) return Q();
|
||||
return this._writeTemplate(this.templates["langs"], {
|
||||
langs: this.book.langs
|
||||
}, path.join(this.options.output, "index.html"));
|
||||
};
|
||||
|
||||
// Write glossary
|
||||
Generator.prototype.writeGlossary = function() {
|
||||
var that = this;
|
||||
|
||||
// No glossary
|
||||
if (this.book.glossary.length == 0) return Q();
|
||||
|
||||
return this._writeTemplate(this.templates["glossary"], {}, path.join(this.options.output, "GLOSSARY.html"));
|
||||
};
|
||||
|
||||
// Write the search index
|
||||
Generator.prototype.writeSearchIndex = function() {
|
||||
var that = this;
|
||||
|
||||
return fs.writeFile(
|
||||
path.join(this.options.output, "search_index.json"),
|
||||
JSON.stringify(this.book.searchIndex)
|
||||
);
|
||||
};
|
||||
|
||||
// Convert a page into a normalized data set
|
||||
Generator.prototype.normalizePage = function(page) {
|
||||
var that = this;
|
||||
|
||||
var _callHook = function(name) {
|
||||
return that.callHook(name, page)
|
||||
.then(function(_page) {
|
||||
page = _page;
|
||||
return page;
|
||||
});
|
||||
};
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
return _callHook("page");
|
||||
})
|
||||
.then(function() {
|
||||
return page;
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a template
|
||||
Generator.prototype._writeTemplate = function(tpl, options, output, interpolate) {
|
||||
var that = this;
|
||||
|
||||
interpolate = interpolate || _.identity;
|
||||
return Q()
|
||||
.then(function(sections) {
|
||||
return that.env.render(
|
||||
tpl,
|
||||
_.extend({
|
||||
gitbook: {
|
||||
version: pkg.version
|
||||
},
|
||||
|
||||
styles: that.styles,
|
||||
|
||||
revision: that.revision,
|
||||
|
||||
title: that.options.title,
|
||||
description: that.options.description,
|
||||
language: that.book.config.normalizeLanguage(),
|
||||
|
||||
glossary: that.book.glossary,
|
||||
|
||||
summary: that.book.summary,
|
||||
allNavigation: that.book.navigation,
|
||||
|
||||
plugins: {
|
||||
resources: that.book.plugins.resources(that.namespace)
|
||||
},
|
||||
pluginsConfig: JSON.stringify(that.options.pluginsConfig),
|
||||
htmlSnippet: _.partial(_.partialRight(that.book.plugins.html, that, options), that.namespace),
|
||||
|
||||
options: that.options,
|
||||
|
||||
basePath: ".",
|
||||
staticBase: path.join(".", "gitbook"),
|
||||
}, options)
|
||||
);
|
||||
})
|
||||
.then(interpolate)
|
||||
.then(function(html) {
|
||||
return fs.writeFile(
|
||||
output,
|
||||
html
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// Copy assets
|
||||
Generator.prototype.copyAssets = function() {
|
||||
var that = this;
|
||||
|
||||
// Copy gitbook assets
|
||||
return fs.copy(
|
||||
path.join(that.options.theme, "assets"),
|
||||
path.join(that.options.output, "gitbook")
|
||||
)
|
||||
|
||||
// Copy plugins assets
|
||||
.then(function() {
|
||||
return Q.all(
|
||||
_.map(that.book.plugins.list, function(plugin) {
|
||||
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
|
||||
return plugin.copyAssets(pluginAssets, that.namespace);
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Generator;
|
||||
@@ -0,0 +1,28 @@
|
||||
var semver = require('semver');
|
||||
var pkg = require('../package.json');
|
||||
|
||||
var VERSION = pkg.version;
|
||||
var VERSION_STABLE = VERSION.replace(/\-(\S+)/g, '');
|
||||
|
||||
var START_TIME = new Date();
|
||||
|
||||
/**
|
||||
Verify that this gitbook version satisfies a requirement
|
||||
We can't directly use samver.satisfies since it will break all plugins when gitbook version is a prerelease (beta, alpha)
|
||||
|
||||
@param {String} condition
|
||||
@return {Boolean}
|
||||
*/
|
||||
function satisfies(condition) {
|
||||
// Test with real version
|
||||
if (semver.satisfies(VERSION, condition)) return true;
|
||||
|
||||
// Test with future stable release
|
||||
return semver.satisfies(VERSION_STABLE, condition);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
version: pkg.version,
|
||||
satisfies: satisfies,
|
||||
START_TIME: START_TIME
|
||||
};
|
||||
+8
-208
@@ -1,210 +1,10 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var tinylr = require('tiny-lr');
|
||||
var color = require('bash-color');
|
||||
var extend = require('extend');
|
||||
|
||||
var Book = require("./book");
|
||||
var Plugin = require("./plugin");
|
||||
var Server = require("./utils/server");
|
||||
var stringUtils = require("./utils/string");
|
||||
var watch = require("./utils/watch");
|
||||
var logger = require("./utils/logger");
|
||||
var common = require('./browser');
|
||||
|
||||
var LOG_OPTION = {
|
||||
name: "log",
|
||||
description: "Minimum log level to display",
|
||||
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
|
||||
defaults: "info"
|
||||
};
|
||||
|
||||
var FORMAT_OPTION = {
|
||||
name: "format",
|
||||
description: "Format to build to",
|
||||
values: ["website", "json", "ebook"],
|
||||
defaults: "website"
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Book: Book,
|
||||
LOG_LEVELS: logger.LEVELS,
|
||||
|
||||
commands: _.flatten([
|
||||
{
|
||||
name: "build [book] [output]",
|
||||
description: "build a book",
|
||||
options: [
|
||||
FORMAT_OPTION,
|
||||
LOG_OPTION
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
var output = args[1] || path.join(input, "_book");
|
||||
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
'output': output
|
||||
},
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
|
||||
return book.parse()
|
||||
.then(function() {
|
||||
return book.generate(kwargs.format);
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_.map(["pdf", "epub", "mobi"], function(ebookType) {
|
||||
return {
|
||||
name: ebookType+" [book] [output]",
|
||||
description: "build a book to "+ebookType,
|
||||
options: [
|
||||
LOG_OPTION
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
var output = args[1];
|
||||
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
|
||||
return book.parse()
|
||||
.then(function() {
|
||||
return book.generateFile(output, {
|
||||
ebookFormat: ebookType
|
||||
});
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
};
|
||||
}),
|
||||
|
||||
{
|
||||
name: "serve [book]",
|
||||
description: "Build then serve a gitbook from a directory",
|
||||
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
|
||||
},
|
||||
FORMAT_OPTION,
|
||||
LOG_OPTION
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
var server = new Server();
|
||||
|
||||
// Init livereload server
|
||||
var lrServer = tinylr({});
|
||||
var lrPath = undefined;
|
||||
|
||||
var generate = function() {
|
||||
if (server.isRunning()) console.log("Stopping server");
|
||||
|
||||
return server.stop()
|
||||
.then(function() {
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
'defaultsPlugins': ["livereload"]
|
||||
},
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
|
||||
return book.parse()
|
||||
.then(function() {
|
||||
return book.generate(kwargs.format);
|
||||
})
|
||||
.thenResolve(book);
|
||||
})
|
||||
.then(function(book) {
|
||||
console.log();
|
||||
console.log('Starting server ...');
|
||||
return server.start(book.options.output, kwargs.port)
|
||||
.then(function() {
|
||||
console.log('Serving book on http://localhost:'+kwargs.port);
|
||||
|
||||
if (lrPath) {
|
||||
// trigger livereload
|
||||
lrServer.changed({
|
||||
body: {
|
||||
files: [lrPath]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!kwargs.watch) return;
|
||||
|
||||
return watch(book.root)
|
||||
.then(function(filepath) {
|
||||
// set livereload path
|
||||
lrPath = filepath;
|
||||
console.log("Restart after change in files");
|
||||
console.log('');
|
||||
return generate();
|
||||
})
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
return Q.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 generate();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: "install [book]",
|
||||
description: "install plugins dependencies",
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
|
||||
var book = new Book(input);
|
||||
|
||||
return book.config.load()
|
||||
.then(function() {
|
||||
return book.plugins.install();
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: "init [directory]",
|
||||
description: "create files and folders based on contents of SUMMARY.md",
|
||||
exec: function(args, kwargs) {
|
||||
return Book.init(args[0] || process.cwd())
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
}
|
||||
])
|
||||
};
|
||||
module.exports = extend({
|
||||
initBook: require('./init'),
|
||||
createNodeFS: require('./fs/node'),
|
||||
Output: require('./output'),
|
||||
commands: require('./cli')
|
||||
}, common);
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
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 articlePath = article.getPath();
|
||||
var filePath = articlePath? path.join(rootFolder, articlePath) : null;
|
||||
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;
|
||||
@@ -0,0 +1,35 @@
|
||||
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;
|
||||
@@ -0,0 +1,22 @@
|
||||
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;
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
/**
|
||||
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;
|
||||
@@ -0,0 +1,21 @@
|
||||
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;
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
/**
|
||||
Encode a SummaryArticle to JSON
|
||||
|
||||
@param {GlossaryEntry}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeGlossaryEntry(entry) {
|
||||
return {
|
||||
id: entry.getID(),
|
||||
name: entry.getName(),
|
||||
description: entry.getDescription()
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = encodeGlossaryEntry;
|
||||
@@ -0,0 +1,25 @@
|
||||
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;
|
||||
@@ -0,0 +1,39 @@
|
||||
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;
|
||||
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
@@ -0,0 +1,20 @@
|
||||
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;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user