├── src ├── strategies │ ├── sass │ │ ├── epub.css │ │ ├── cover.jpg │ │ ├── clean.js │ │ ├── index.js │ │ ├── scrapper.js │ │ └── scrapperMd.js │ ├── lodash │ │ ├── epub.css │ │ ├── cover.jpg │ │ ├── clean.js │ │ ├── index.js │ │ ├── scrapper.js │ │ └── scrapperMd.js │ ├── react │ │ ├── epub.css │ │ ├── cover.jpg │ │ ├── clean.js │ │ ├── index.js │ │ ├── scrapper.js │ │ └── scrapperMd.js │ ├── angular2 │ │ ├── cover.jpg │ │ ├── clean.js │ │ ├── index.js │ │ └── scrapper.js │ ├── express │ │ ├── cover.jpg │ │ ├── index.js │ │ └── scrapper.js │ ├── underscore │ │ ├── cover.jpg │ │ ├── index.js │ │ └── scrapper.js │ └── apollo-react │ │ ├── cover.jpg │ │ ├── index.js │ │ └── scrapperMd.js ├── getAboutPage.js ├── run.js ├── tocObjToHtml.js └── tocToArray.js ├── .gitignore ├── docs ├── og.jpg ├── ereader.jpg ├── gallery-bg.jpg ├── download │ ├── elm.epub │ ├── mobx.epub │ ├── sass.epub │ ├── vue.epub │ ├── express.epub │ ├── lodash.epub │ ├── react.epub │ ├── angular2.epub │ ├── socketio.epub │ ├── apolloreact.epub │ └── underscore.epub ├── assets │ ├── book_react.jpg │ ├── book_sass.jpg │ ├── book_express.jpg │ ├── book_lodash.jpg │ ├── book_angular2.jpg │ ├── book_underscore.jpg │ └── book_apolloreact.jpg ├── lib │ └── cutereset.css ├── style.css └── index.html ├── docs-assets └── thumbnail.jpg ├── package.json ├── README.md └── index.js /src/strategies/sass/epub.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | _tmp 4 | *.log 5 | -------------------------------------------------------------------------------- /docs/og.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/og.jpg -------------------------------------------------------------------------------- /docs/ereader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/ereader.jpg -------------------------------------------------------------------------------- /src/strategies/lodash/epub.css: -------------------------------------------------------------------------------- 1 | .buttons-unit, iframe { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /src/strategies/react/epub.css: -------------------------------------------------------------------------------- 1 | .buttons-unit, iframe { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /docs/gallery-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/gallery-bg.jpg -------------------------------------------------------------------------------- /docs/download/elm.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/elm.epub -------------------------------------------------------------------------------- /docs/download/mobx.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/mobx.epub -------------------------------------------------------------------------------- /docs/download/sass.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/sass.epub -------------------------------------------------------------------------------- /docs/download/vue.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/vue.epub -------------------------------------------------------------------------------- /docs-assets/thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs-assets/thumbnail.jpg -------------------------------------------------------------------------------- /docs/assets/book_react.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/assets/book_react.jpg -------------------------------------------------------------------------------- /docs/assets/book_sass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/assets/book_sass.jpg -------------------------------------------------------------------------------- /docs/download/express.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/express.epub -------------------------------------------------------------------------------- /docs/download/lodash.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/lodash.epub -------------------------------------------------------------------------------- /docs/download/react.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/react.epub -------------------------------------------------------------------------------- /docs/assets/book_express.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/assets/book_express.jpg -------------------------------------------------------------------------------- /docs/assets/book_lodash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/assets/book_lodash.jpg -------------------------------------------------------------------------------- /docs/download/angular2.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/angular2.epub -------------------------------------------------------------------------------- /docs/download/socketio.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/socketio.epub -------------------------------------------------------------------------------- /docs/assets/book_angular2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/assets/book_angular2.jpg -------------------------------------------------------------------------------- /docs/assets/book_underscore.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/assets/book_underscore.jpg -------------------------------------------------------------------------------- /docs/download/apolloreact.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/apolloreact.epub -------------------------------------------------------------------------------- /docs/download/underscore.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/download/underscore.epub -------------------------------------------------------------------------------- /src/strategies/lodash/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/src/strategies/lodash/cover.jpg -------------------------------------------------------------------------------- /src/strategies/react/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/src/strategies/react/cover.jpg -------------------------------------------------------------------------------- /src/strategies/sass/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/src/strategies/sass/cover.jpg -------------------------------------------------------------------------------- /docs/assets/book_apolloreact.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/docs/assets/book_apolloreact.jpg -------------------------------------------------------------------------------- /src/strategies/angular2/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/src/strategies/angular2/cover.jpg -------------------------------------------------------------------------------- /src/strategies/express/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/src/strategies/express/cover.jpg -------------------------------------------------------------------------------- /src/strategies/underscore/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/src/strategies/underscore/cover.jpg -------------------------------------------------------------------------------- /src/strategies/apollo-react/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javierbyte/docs2epub/HEAD/src/strategies/apollo-react/cover.jpg -------------------------------------------------------------------------------- /src/strategies/angular2/clean.js: -------------------------------------------------------------------------------- 1 | function htmlFilter (content) { 2 | return content.replace(/code-example/gi, 'code').replace(/code-pane/gi, 'code') 3 | } 4 | 5 | module.exports = htmlFilter 6 | -------------------------------------------------------------------------------- /src/strategies/express/index.js: -------------------------------------------------------------------------------- 1 | var CONFIG = { 2 | title: 'Express', 3 | author: 'Express', 4 | cover: './src/strategies/express/cover.jpg', 5 | 6 | docsUrl: 'http://expressjs.com/en/4x/api.html', 7 | repoUrl: 'https://github.com/expressjs/express', 8 | licenceUrl: 'https://raw.githubusercontent.com/expressjs/express/master/LICENSE' 9 | } 10 | 11 | module.exports = CONFIG 12 | -------------------------------------------------------------------------------- /src/strategies/angular2/index.js: -------------------------------------------------------------------------------- 1 | var CONFIG = { 2 | title: 'angular2', 3 | author: 'angular2', 4 | cover: './src/strategies/angular2/cover.jpg', 5 | 6 | docsUrl: 'https://angular.io/docs/ts/latest/', 7 | repoUrl: 'https://github.com/angular/angular', 8 | licenceUrl: 'https://raw.githubusercontent.com/angular/angular/master/LICENSE', 9 | 10 | cleanHtml: require('./clean') 11 | } 12 | 13 | module.exports = CONFIG 14 | -------------------------------------------------------------------------------- /src/strategies/underscore/index.js: -------------------------------------------------------------------------------- 1 | var CONFIG = { 2 | title: 'Underscore', 3 | author: 'Underscore', 4 | cover: './src/strategies/underscore/cover.jpg', 5 | 6 | docsUrl: 'http://underscorejs.org/', 7 | repoUrl: 'https://github.com/jashkenas/underscore', 8 | licenceUrl: 'https://raw.githubusercontent.com/jashkenas/underscore/master/LICENSE' 9 | 10 | // cleanHtml: require('./clean') 11 | } 12 | 13 | module.exports = CONFIG 14 | -------------------------------------------------------------------------------- /src/strategies/react/clean.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | var cheerio = require('cheerio') 3 | 4 | function htmlFilter (content) { 5 | var $ = cheerio.load(content) 6 | 7 | var elToRemove = [ 8 | '.hash-link', 9 | '.edit-page-link', 10 | '.want-to-skip-all-this-and-just-see-the-source' 11 | ] 12 | 13 | _.forEach(elToRemove, el => $(el).remove()) 14 | 15 | return $.html() 16 | } 17 | 18 | module.exports = htmlFilter 19 | -------------------------------------------------------------------------------- /src/strategies/sass/clean.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | var cheerio = require('cheerio') 3 | 4 | function htmlFilter (content) { 5 | var $ = cheerio.load(content) 6 | 7 | var elToRemove = [ 8 | '.hash-link', 9 | '.edit-page-link', 10 | '.want-to-skip-all-this-and-just-see-the-source' 11 | ] 12 | 13 | _.forEach(elToRemove, el => $(el).remove()) 14 | 15 | return $.html() 16 | } 17 | 18 | module.exports = htmlFilter 19 | -------------------------------------------------------------------------------- /src/strategies/lodash/clean.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | var cheerio = require('cheerio') 3 | 4 | function htmlFilter (content) { 5 | var $ = cheerio.load(content) 6 | 7 | var elToRemove = [ 8 | '.hash-link', 9 | '.edit-page-link', 10 | '.want-to-skip-all-this-and-just-see-the-source' 11 | ] 12 | 13 | _.forEach(elToRemove, el => $(el).remove()) 14 | 15 | return $.html() 16 | } 17 | 18 | module.exports = htmlFilter 19 | -------------------------------------------------------------------------------- /src/strategies/underscore/scrapper.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | const CONFIG = require('./index.js') 3 | 4 | function strategy () { 5 | return new Promise(function (resolve, reject) { 6 | resolve( 7 | _.assign({}, CONFIG, { 8 | content: [{ 9 | title: 'Underscore', 10 | level: 0, 11 | url: 'http://underscorejs.org/' 12 | }] 13 | }) 14 | ) 15 | }) 16 | } 17 | 18 | module.exports = strategy 19 | -------------------------------------------------------------------------------- /src/strategies/express/scrapper.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | const CONFIG = require('./index.js') 3 | 4 | function strategy () { 5 | return new Promise(function (resolve, reject) { 6 | resolve( 7 | _.assign({}, CONFIG, { 8 | content: [{ 9 | title: 'Express', 10 | level: 0, 11 | url: 'http://expressjs.com/en/api.html' 12 | }] 13 | }) 14 | ) 15 | }) 16 | } 17 | 18 | module.exports = strategy 19 | -------------------------------------------------------------------------------- /src/strategies/lodash/index.js: -------------------------------------------------------------------------------- 1 | var CONFIG = { 2 | title: 'Lodash', 3 | author: 'Lodash', 4 | cover: './src/strategies/lodash/cover.jpg', 5 | 6 | epubStylesheet: './src/strategies/lodash/epub.css', 7 | epubTOCDepth: false, 8 | 9 | docsUrl: 'https://lodash.com/docs/4.15.0', 10 | repoUrl: 'https://github.com/lodash/lodash', 11 | licenceUrl: 'https://raw.githubusercontent.com/lodash/lodash/master/LICENSE', 12 | 13 | type: 'MARKDOWN' 14 | } 15 | 16 | module.exports = CONFIG 17 | -------------------------------------------------------------------------------- /src/strategies/sass/index.js: -------------------------------------------------------------------------------- 1 | var CONFIG = { 2 | title: 'Sass', 3 | author: 'Sass', 4 | cover: './src/strategies/sass/cover.jpg', 5 | 6 | epubStylesheet: './src/strategies/sass/epub.css', 7 | epubTOCDepth: 1, 8 | 9 | docsUrl: 'http://sass-lang.com/', 10 | repoUrl: 'https://github.com/sass/sass', 11 | licenceUrl: 'https://raw.githubusercontent.com/sass/sass/stable/MIT-LICENSE', 12 | 13 | type: 'MARKDOWN', 14 | cleanHtml: require('./clean') 15 | } 16 | 17 | module.exports = CONFIG 18 | -------------------------------------------------------------------------------- /src/strategies/apollo-react/index.js: -------------------------------------------------------------------------------- 1 | var CONFIG = { 2 | title: 'ApolloReact', 3 | author: 'Apollo', 4 | cover: './src/strategies/apollo-react/cover.jpg', 5 | 6 | epubStylesheet: './src/strategies/react/epub.css', 7 | epubTOCDepth: 1, 8 | 9 | docsUrl: 'http://dev.apollodata.com/react/', 10 | repoUrl: 'https://github.com/apollographql/react-docs', 11 | licenceUrl: 'https://raw.githubusercontent.com/apollographql/apollo-client/master/LICENSE', 12 | 13 | type: 'MARKDOWN' 14 | } 15 | 16 | module.exports = CONFIG 17 | -------------------------------------------------------------------------------- /src/strategies/react/index.js: -------------------------------------------------------------------------------- 1 | var CONFIG = { 2 | title: 'React', 3 | author: 'Facebook', 4 | cover: './src/strategies/react/cover.jpg', 5 | 6 | epubStylesheet: './src/strategies/react/epub.css', 7 | epubTOCDepth: 1, 8 | 9 | docsUrl: 'https://facebook.github.io/react/docs/getting-started.html', 10 | repoUrl: 'https://github.com/facebook/react', 11 | licenceUrl: 'https://raw.githubusercontent.com/facebook/react/master/LICENSE-docs', 12 | 13 | type: 'MARKDOWN', 14 | cleanHtml: require('./clean') 15 | } 16 | 17 | module.exports = CONFIG 18 | -------------------------------------------------------------------------------- /src/getAboutPage.js: -------------------------------------------------------------------------------- 1 | function getAboutPageTemplate(tocObj) { 2 | return ` 3 |
4 |

About this book.

5 |
6 | Documentation generated by docs2epub [http://javier.xyz/docs2epub/] on ${new Date()}, scrapped from ${tocObj.docsUrl}. 7 |
8 |

9 |
10 | Find more about this project on ${tocObj.repoUrl}. 11 | LICENCE of ${tocObj.title}: ${tocObj.licenceUrl} 12 |
13 |
14 | `; 15 | } 16 | 17 | function getAboutPage(tocObj) { 18 | return { 19 | title: `${tocObj.title} documentation`, 20 | level: 0, 21 | result: { 22 | title: `${tocObj.title} documentation`, 23 | content: getAboutPageTemplate(tocObj) 24 | } 25 | }; 26 | } 27 | 28 | module.exports = getAboutPage; 29 | -------------------------------------------------------------------------------- /src/run.js: -------------------------------------------------------------------------------- 1 | const tocToArray = require('./tocToArray'); 2 | 3 | const strategies = { 4 | react: require('./strategies/react/scrapperMd'), 5 | apolloreact: require('./strategies/apollo-react/scrapperMd'), 6 | lodash: require('./strategies/lodash/scrapperMd'), 7 | underscore: require('./strategies/underscore/scrapper'), 8 | express: require('./strategies/express/scrapper'), 9 | angular2: require('./strategies/angular2/scrapper'), 10 | sass: require('./strategies/sass/scrapperMd') 11 | }; 12 | 13 | function run(strategyId) { 14 | return new Promise(function(resolve, reject) { 15 | strategies 16 | [strategyId]() 17 | .then(tocToArray) 18 | .then(docArray => { 19 | resolve(docArray); 20 | }) 21 | .catch(reject); 22 | }); 23 | } 24 | 25 | module.exports = run; 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs2epub", 3 | "version": "0.1.0", 4 | "description": "ever wanted to have your favorite library docs on epub format? search no more ;)", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/javierbyte/docs2epub.git" 12 | }, 13 | "author": "", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/javierbyte/docs2epub/issues" 17 | }, 18 | "homepage": "https://github.com/javierbyte/docs2epub#readme", 19 | "dependencies": { 20 | "async": "^2.0.1", 21 | "axios": "^0.14.0", 22 | "cheerio": "^0.22.0", 23 | "epub-gen": "0.0.16", 24 | "express": "^4.14.0", 25 | "lodash": "^4.15.0", 26 | "moment": "^2.14.1", 27 | "node-readability": "^2.2.0", 28 | "pug": "^2.0.0-beta6", 29 | "slug": "^0.9.1" 30 | }, 31 | "devDependencies": { 32 | "standard": "^8.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/strategies/lodash/scrapper.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | const cheerio = require('cheerio') 4 | const axios = require('axios') 5 | const url = require('url') 6 | 7 | const CONFIG = require('./index.js') 8 | 9 | const DOCURL = 'https://facebook.github.io/react/docs/getting-started.html' 10 | 11 | function strategy () { 12 | return new Promise(function (resolve, reject) { 13 | axios.get(DOCURL) 14 | .then(res => { 15 | var $ = cheerio.load(res.data) 16 | var tocArray = [] 17 | 18 | $('.nav-docs-section').each(function (index, el) { 19 | var parentName = $(el).find('h3').text() 20 | 21 | if (parentName === 'Community Resources') return 22 | 23 | tocArray.push({ 24 | title: parentName, 25 | level: 1 26 | }) 27 | 28 | $(this).find('a').each(function (childIndex, childEl) { 29 | tocArray.push({ 30 | parent: parentName, 31 | level: 2, 32 | title: $(childEl).text(), 33 | url: url.resolve(DOCURL, $(childEl).attr('href')) 34 | }) 35 | }) 36 | }) 37 | 38 | resolve( 39 | _.assign({}, CONFIG, { 40 | type: 'HTML', 41 | content: tocArray 42 | }) 43 | ) 44 | }) 45 | }) 46 | } 47 | 48 | module.exports = strategy 49 | -------------------------------------------------------------------------------- /src/strategies/react/scrapper.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | const cheerio = require('cheerio') 4 | const axios = require('axios') 5 | const url = require('url') 6 | 7 | const CONFIG = require('./index.js') 8 | 9 | const DOCURL = 'https://facebook.github.io/react/docs/getting-started.html' 10 | 11 | function strategy () { 12 | return new Promise(function (resolve, reject) { 13 | axios.get(DOCURL) 14 | .then(res => { 15 | var $ = cheerio.load(res.data) 16 | var tocArray = [] 17 | 18 | $('.nav-docs-section').each(function (index, el) { 19 | var parentName = $(el).find('h3').text() 20 | 21 | if (parentName === 'Community Resources') return 22 | 23 | tocArray.push({ 24 | title: parentName, 25 | level: 1 26 | }) 27 | 28 | $(this).find('a').each(function (childIndex, childEl) { 29 | tocArray.push({ 30 | parent: parentName, 31 | level: 2, 32 | title: $(childEl).text(), 33 | url: url.resolve(DOCURL, $(childEl).attr('href')) 34 | }) 35 | }) 36 | }) 37 | 38 | resolve( 39 | _.assign({}, CONFIG, { 40 | type: 'HTML', 41 | content: tocArray 42 | }) 43 | ) 44 | }) 45 | }) 46 | } 47 | 48 | module.exports = strategy 49 | -------------------------------------------------------------------------------- /src/strategies/sass/scrapper.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | const cheerio = require('cheerio') 4 | const axios = require('axios') 5 | const url = require('url') 6 | 7 | const CONFIG = require('./index.js') 8 | 9 | const DOCURL = 'https://facebook.github.io/react/docs/getting-started.html' 10 | 11 | function strategy () { 12 | return new Promise(function (resolve, reject) { 13 | axios.get(DOCURL) 14 | .then(res => { 15 | var $ = cheerio.load(res.data) 16 | var tocArray = [] 17 | 18 | $('.nav-docs-section').each(function (index, el) { 19 | var parentName = $(el).find('h3').text() 20 | 21 | if (parentName === 'Community Resources') return 22 | 23 | tocArray.push({ 24 | title: parentName, 25 | level: 1 26 | }) 27 | 28 | $(this).find('a').each(function (childIndex, childEl) { 29 | tocArray.push({ 30 | parent: parentName, 31 | level: 2, 32 | title: $(childEl).text(), 33 | url: url.resolve(DOCURL, $(childEl).attr('href')) 34 | }) 35 | }) 36 | }) 37 | 38 | resolve( 39 | _.assign({}, CONFIG, { 40 | type: 'HTML', 41 | content: tocArray 42 | }) 43 | ) 44 | }) 45 | }) 46 | } 47 | 48 | module.exports = strategy 49 | -------------------------------------------------------------------------------- /src/strategies/lodash/scrapperMd.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const _ = require('lodash') 3 | const async = require('async') 4 | 5 | const CONFIG = require('./index.js') 6 | 7 | var docList = [{ 8 | title: 'Lodash', 9 | url: 'https://raw.githubusercontent.com/lodash/lodash/master/doc/README.md' 10 | }] 11 | 12 | docList = _.map(docList, (el, idx) => { 13 | el.index = ('000' + idx).slice(-3) 14 | return el 15 | }) 16 | 17 | function requestUrl (doc, cb) { 18 | axios.get(doc.url).then(res => { 19 | console.log('READED', doc.url) 20 | cb(null, _.assign({}, doc, { 21 | result: { 22 | title: doc.title, 23 | content: res.data 24 | .replace(/
/g, '\n') 25 | .replace(/\[&#x.*\[Ⓣ\]\[1\]/g, '') 26 | .replace(/([^#]|^)#### /g, '\n\n\n#### ') 27 | .replace(/\n\n/g, '\n') 28 | .replace(/(## `.*?`)/g, '\n$1') 29 | .replace(/

/g, 'h2>') 31 | } 32 | })) 33 | }, (err) => { 34 | cb(err) 35 | }) 36 | } 37 | 38 | function strategy () { 39 | return new Promise(function (resolve, reject) { 40 | async.map(docList, requestUrl, function (asyncErr, asyncRes) { 41 | if (asyncErr) { 42 | reject(asyncErr) 43 | return 44 | } 45 | 46 | resolve( 47 | _.assign({}, CONFIG, { 48 | content: asyncRes 49 | }) 50 | ) 51 | }) 52 | }) 53 | } 54 | 55 | module.exports = strategy 56 | -------------------------------------------------------------------------------- /src/tocObjToHtml.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | var getHtml = function(content) { 4 | return ` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 37 | 38 | 39 |
40 | ${content} 41 |
42 | 43 | 44 | `; 45 | }; 46 | 47 | function tocObjToHtml(tocObj) { 48 | return getHtml( 49 | _.map(tocObj.content, tocEl => { 50 | if (tocEl.result) { 51 | return ` 52 |
53 |

${tocEl.result.title}

54 |
${tocEl.result.content}
55 |
56 | `; 57 | } 58 | 59 | if (tocEl.level === 0) return `

${tocEl.title}

`; 60 | if (tocEl.level === 1) return `

${tocEl.title}

`; 61 | 62 | return ''; 63 | }).join('') 64 | ); 65 | } 66 | 67 | module.exports = tocObjToHtml; 68 | -------------------------------------------------------------------------------- /src/strategies/angular2/scrapper.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | const cheerio = require('cheerio') 4 | const axios = require('axios') 5 | const url = require('url') 6 | 7 | const CONFIG = require('./index.js') 8 | 9 | const DOCURL = 'https://angular.io/docs/ts/latest/quickstart.html' 10 | 11 | function strategy () { 12 | return new Promise(function (resolve, reject) { 13 | axios.get(DOCURL) 14 | .then(res => { 15 | var $ = cheerio.load(res.data) 16 | var tocArray = [] 17 | 18 | $('.nav-blocks').each(function (index, el) { 19 | var parentName = $(el).find('nav-title').first().text() 20 | 21 | if (parentName === 'API Reference') return 22 | 23 | tocArray.push({ 24 | title: parentName, 25 | level: 1 26 | }) 27 | 28 | $(this).find('.nav-unordered-lists a').each(function (childIndex, childEl) { 29 | tocArray.push({ 30 | parent: parentName, 31 | level: 2, 32 | title: $(childEl).text(), 33 | url: url.resolve(DOCURL, $(childEl).attr('href')) 34 | }) 35 | }) 36 | 37 | $(this).find('.nav-ordered-lists a').each(function (childIndex, childEl) { 38 | tocArray.push({ 39 | parent: parentName, 40 | level: 2, 41 | title: $(childEl).text(), 42 | url: url.resolve(DOCURL, $(childEl).attr('href')) 43 | }) 44 | }) 45 | }) 46 | 47 | resolve( 48 | _.assign({}, CONFIG, { 49 | content: tocArray 50 | }) 51 | ) 52 | }) 53 | }) 54 | } 55 | 56 | module.exports = strategy 57 | -------------------------------------------------------------------------------- /src/strategies/sass/scrapperMd.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const _ = require('lodash') 3 | const async = require('async') 4 | 5 | const CONFIG = require('./index.js') 6 | 7 | var docList = [{ 8 | title: 'Sass reference', 9 | url: 'https://raw.githubusercontent.com/sass/sass/stable/doc-src/SASS_REFERENCE.md' 10 | }, { 11 | title: 'Indented syntax', 12 | url: 'https://raw.githubusercontent.com/sass/sass/stable/doc-src/INDENTED_SYNTAX.md' 13 | }, { 14 | title: 'SCSS for SASS Users', 15 | url: 'https://raw.githubusercontent.com/sass/sass/stable/doc-src/SCSS_FOR_SASS_USERS.md' 16 | }, { 17 | title: 'FAQ', 18 | url: 'https://raw.githubusercontent.com/sass/sass/stable/doc-src/FAQ.md' 19 | }, { 20 | title: 'Changelog', 21 | url: 'https://raw.githubusercontent.com/sass/sass/stable/doc-src/SASS_CHANGELOG.md' 22 | }] 23 | 24 | docList = _.map(docList, (el, idx) => { 25 | el.index = ('000' + idx).slice(-3) 26 | return el 27 | }) 28 | 29 | function requestUrl (doc, cb) { 30 | axios.get(doc.url).then(res => { 31 | console.log('READED', doc.url) 32 | cb(null, _.assign({}, doc, { 33 | result: { 34 | title: doc.title, 35 | content: res.data 36 | } 37 | })) 38 | }, (err) => { 39 | cb(err) 40 | }) 41 | } 42 | 43 | function strategy () { 44 | return new Promise(function (resolve, reject) { 45 | async.map(docList, requestUrl, function (asyncErr, asyncRes) { 46 | if (asyncErr) { 47 | reject(asyncErr) 48 | return 49 | } 50 | 51 | resolve( 52 | _.assign({}, CONFIG, { 53 | content: asyncRes 54 | }) 55 | ) 56 | }) 57 | }) 58 | } 59 | 60 | module.exports = strategy 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Get your favorite docs as ebooks! 2 | Doc scraper and ebook generator. 3 | 4 | [![docs2epub](docs/og.jpg)](http://javier.xyz/docs2epub/) 5 | 6 | # Library 7 | List of precompiled ebooks created with docs2epub. 8 | 9 | * React [[epub]](http://javier.xyz/docs2epub/download/react.epub) [stable] 10 | * Lodash [[epub]](http://javier.xyz/docs2epub/download/lodash.epub) [stable] 11 | * Sass [[epub]](http://javier.xyz/docs2epub/download/sass.epub) [beta] 12 | * Underscore [[epub]](http://javier.xyz/docs2epub/download/underscore.epub) [beta] 13 | * express [[epub]](http://javier.xyz/docs2epub/download/express.epub) [beta] 14 | * angular2 [[epub]](http://javier.xyz/docs2epub/download/angular2.epub) [alpha] 15 | 16 | # Generate your own ebook form docs 17 | The objective of this tool is to be a ready to go documentation parser and ebook generator (from scraping documentation sites or markdown). 18 | 19 | It has a central processing and epub generator based on [strategies](https://github.com/javierbyte/docs2epub/tree/master/src/strategies). 20 | 21 | If you want to add your own ebook generator you'll have to add a 'strategy' to the the `/src/strategies/` dir that returns a `docObj` object as described on [tocToArray.js](https://github.com/javierbyte/docs2epub/blob/master/src/tocToArray.js). And then require it on [run.js](https://github.com/javierbyte/docs2epub/blob/master/src/run.js#L3). 22 | 23 | Then run 24 | ``` 25 | node index.js --project 26 | ``` 27 | 28 | With `yourprojectid` being the key on the `run.js` require. 29 | 30 | And you'll have your `.epub` on the `docs/downloads` directory! 31 | 32 | Remember to add the original documentation licence. And feel free to open an issue here or create a PR if you want to add your generated ebook to the library. 33 | 34 | # Features 35 | * Pluggable system to add more documentation sources. 36 | * Uses [epub-gen](https://github.com/cyrilis/epub-gen) tuned for code. 37 | 38 | # Future improvements 39 | * Easier and smarter way to add more documentation sources. 40 | * Cronjob to auto-update library. 41 | 42 | # Licence 43 | MIT. 44 | -------------------------------------------------------------------------------- /src/tocToArray.js: -------------------------------------------------------------------------------- 1 | /* 2 | We should return a "dobObj" object, that looks like this: 3 | 4 | { 5 | title: 'React', // name 6 | author: 'Facebook', // author 7 | cover: './src/strategies/react/cover.jpg', // cover url, relative or absolute 8 | 9 | epubStylesheet: './src/strategies/react/epub.css', // custom css url 10 | epubTOCDepth: 1, 11 | 12 | docsUrl: 'https://facebook.github.io/react/docs/getting-started.html', 13 | repoUrl: 'https://github.com/facebook/react', 14 | licenceUrl: 'https://raw.githubusercontent.com/facebook/react/master/LICENSE-docs', 15 | 16 | type: 'MARKDOWN', // MARKDOWN or HTML 17 | content: [{ 18 | title: 'something', 19 | url: 'http://javier.xyz' 20 | }] 21 | } 22 | 23 | The `content.url` property should be either a html website (server side rendering) or a markdown file. 24 | Remember to specify a `type` `MARKDOWN` o `HTML`. 25 | */ 26 | 27 | var _ = require('lodash'); 28 | var read = require('node-readability'); 29 | var cheerio = require('cheerio'); 30 | 31 | var getAboutPage = require('./getAboutPage'); 32 | 33 | function htmlSafe(content) { 34 | var $ = cheerio.load(content); 35 | 36 | var elToRemove = ['script']; 37 | 38 | _.forEach(elToRemove, el => $(el).remove()); 39 | 40 | return $.html(); 41 | } 42 | 43 | function resolveTocEl(tocEl, tocObj) { 44 | var cleanHtml = tocObj.cleanHtml || 45 | function(noop) { 46 | return noop; 47 | }; 48 | 49 | return new Promise(function(resolve, reject) { 50 | read(tocEl.url, function(err, article) { 51 | console.log('READED', tocEl.url); 52 | 53 | console.log(article.content); 54 | 55 | if (err || !article) { 56 | reject(err); 57 | return; 58 | } 59 | 60 | var title = article.title; 61 | var content = htmlSafe(cleanHtml(article.content)); 62 | 63 | article.close(); 64 | 65 | resolve( 66 | _.assign(tocEl, { 67 | result: { 68 | title: title, 69 | content: content 70 | } 71 | }) 72 | ); 73 | }); 74 | }); 75 | } 76 | 77 | function getContent(tocContent, tocObj) { 78 | return new Promise(function(resolve, reject) { 79 | if ( 80 | _.every(tocContent, toc => { 81 | return !toc.url || toc.result; 82 | }) 83 | ) { 84 | resolve(tocContent); 85 | return; 86 | } 87 | 88 | var toSearchContentKey = _.findKey(tocContent, toc => { 89 | return !(!toc.url || toc.result); 90 | }); 91 | 92 | resolveTocEl(tocContent[toSearchContentKey], tocObj) 93 | .then(res => { 94 | tocContent[toSearchContentKey] = res; 95 | resolve(getContent(tocContent, tocObj)); 96 | }) 97 | .catch(reject); 98 | }); 99 | } 100 | 101 | function tocToArray(toc) { 102 | return new Promise(function(resolve, reject) { 103 | getContent(toc.content, toc) 104 | .then(res => { 105 | resolve( 106 | _.assign({}, toc, { 107 | content: res 108 | }) 109 | ); 110 | }) 111 | .catch(reject); 112 | }); 113 | } 114 | 115 | module.exports = tocToArray; 116 | -------------------------------------------------------------------------------- /src/strategies/apollo-react/scrapperMd.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const _ = require('lodash') 3 | const async = require('async') 4 | 5 | const CONFIG = require('./index.js') 6 | 7 | var docList = [{ 8 | title: 'Index', 9 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/index.md' 10 | }, { 11 | title: 'Initialization', 12 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/initialization.md' 13 | }, { 14 | title: 'Higher Order Components', 15 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/higher-order-components.md' 16 | }, { 17 | title: 'Example Schema', 18 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/example-schema.md' 19 | }, { 20 | title: 'Queries', 21 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/queries.md' 22 | }, { 23 | title: 'Mutations', 24 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/mutations.md' 25 | }, { 26 | title: 'Receiving Updates', 27 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/receiving-updates.md' 28 | }, { 29 | title: 'Cache Updates', 30 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/cache-updates.md' 31 | }, { 32 | title: 'Auth', 33 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/auth.md' 34 | }, { 35 | title: 'Pagination', 36 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/pagination.md' 37 | }, { 38 | title: 'Optimistic UI', 39 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/optimistic-ui.md' 40 | }, { 41 | title: 'Fragments', 42 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/fragments.md' 43 | }, { 44 | title: 'Prefetching', 45 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/prefetching.md' 46 | }, { 47 | title: 'React Native', 48 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/react-native.md' 49 | }, { 50 | title: 'Redux', 51 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/redux.md' 52 | }, { 53 | title: 'Webpack', 54 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/webpack.md' 55 | }, { 56 | title: 'Server Side Rendering', 57 | url: 'https://raw.githubusercontent.com/apollographql/react-docs/master/source/server-side-rendering.md' 58 | }] 59 | 60 | docList = _.map(docList, (el, idx) => { 61 | el.index = ('000' + idx).slice(-3) 62 | return el 63 | }) 64 | 65 | function requestUrl (doc, cb) { 66 | axios.get(doc.url).then(res => { 67 | console.log('READED', doc.url) 68 | cb(null, _.assign({}, doc, { 69 | result: { 70 | title: doc.title, 71 | content: `\n# ${doc.title}\n\n` + res.data 72 | // .replace(/\]\(\/react\/img/g, '](https://raw.githubusercontent.com/facebook/react/master/docs/img') 73 | } 74 | })) 75 | }, (err) => { 76 | cb(err) 77 | }) 78 | } 79 | 80 | function strategy () { 81 | return new Promise(function (resolve, reject) { 82 | async.map(docList, requestUrl, function (asyncErr, asyncRes) { 83 | if (asyncErr) { 84 | reject(asyncErr) 85 | return 86 | } 87 | 88 | resolve( 89 | _.assign({}, CONFIG, { 90 | content: asyncRes 91 | }) 92 | ) 93 | }) 94 | }) 95 | } 96 | 97 | module.exports = strategy 98 | -------------------------------------------------------------------------------- /docs/lib/cutereset.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | position: relative; 5 | text-rendering: optimizeLegibility; 6 | } 7 | *, 8 | *:before, 9 | *:after { 10 | box-sizing: inherit; 11 | } 12 | html { 13 | box-sizing: border-box; 14 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 15 | } 16 | a { 17 | text-decoration: none; 18 | } 19 | ul, 20 | ol { 21 | padding-left: 1em; 22 | } 23 | img { 24 | max-width: 100%; 25 | height: auto; 26 | } 27 | .cf:before, 28 | .cf:after { 29 | content: " "; 30 | display: table; 31 | } 32 | .cf:after { 33 | clear: both; 34 | } 35 | .flex { 36 | display: flex; 37 | } 38 | .flex-wrap { 39 | flex-wrap: wrap; 40 | } 41 | .flex-1 { 42 | flex: 1; 43 | } 44 | .flex-2 { 45 | flex: 2; 46 | } 47 | .flex-direction-column { 48 | flex-direction: column; 49 | } 50 | .flex-align-center { 51 | align-items: center; 52 | } 53 | .flex-align-start { 54 | align-items: flex-start; 55 | } 56 | .flex-align-stretch { 57 | align-items: stretch; 58 | } 59 | .flex-content-center { 60 | flex-content: center; 61 | } 62 | .flex-justify-center { 63 | justify-content: center; 64 | } 65 | .margin-1 { 66 | margin: 1 rem; 67 | } 68 | .padding-1 { 69 | padding: 1rem; 70 | } 71 | .padding-top-1 { 72 | padding-top: 1rem; 73 | } 74 | .margin-top-1 { 75 | margin-top: 1rem; 76 | } 77 | .padding-left-1 { 78 | padding-left: 1rem; 79 | } 80 | .margin-left-1 { 81 | margin-left: 1rem; 82 | } 83 | .padding-right-1 { 84 | padding-right: 1rem; 85 | } 86 | .margin-right-1 { 87 | margin-right: 1rem; 88 | } 89 | .padding-bottom-1 { 90 | padding-bottom: 1rem; 91 | } 92 | .margin-bottom-1 { 93 | margin-bottom: 1rem; 94 | } 95 | .margin-2 { 96 | margin: 2 rem; 97 | } 98 | .padding-2 { 99 | padding: 2rem; 100 | } 101 | .padding-top-2 { 102 | padding-top: 2rem; 103 | } 104 | .margin-top-2 { 105 | margin-top: 2rem; 106 | } 107 | .padding-left-2 { 108 | padding-left: 2rem; 109 | } 110 | .margin-left-2 { 111 | margin-left: 2rem; 112 | } 113 | .padding-right-2 { 114 | padding-right: 2rem; 115 | } 116 | .margin-right-2 { 117 | margin-right: 2rem; 118 | } 119 | .padding-bottom-2 { 120 | padding-bottom: 2rem; 121 | } 122 | .margin-bottom-2 { 123 | margin-bottom: 2rem; 124 | } 125 | .margin-3 { 126 | margin: 3 rem; 127 | } 128 | .padding-3 { 129 | padding: 3rem; 130 | } 131 | .padding-top-3 { 132 | padding-top: 3rem; 133 | } 134 | .margin-top-3 { 135 | margin-top: 3rem; 136 | } 137 | .padding-left-3 { 138 | padding-left: 3rem; 139 | } 140 | .margin-left-3 { 141 | margin-left: 3rem; 142 | } 143 | .padding-right-3 { 144 | padding-right: 3rem; 145 | } 146 | .margin-right-3 { 147 | margin-right: 3rem; 148 | } 149 | .padding-bottom-3 { 150 | padding-bottom: 3rem; 151 | } 152 | .margin-bottom-3 { 153 | margin-bottom: 3rem; 154 | } 155 | .margin-4 { 156 | margin: 4 rem; 157 | } 158 | .padding-4 { 159 | padding: 4rem; 160 | } 161 | .padding-top-4 { 162 | padding-top: 4rem; 163 | } 164 | .margin-top-4 { 165 | margin-top: 4rem; 166 | } 167 | .padding-left-4 { 168 | padding-left: 4rem; 169 | } 170 | .margin-left-4 { 171 | margin-left: 4rem; 172 | } 173 | .padding-right-4 { 174 | padding-right: 4rem; 175 | } 176 | .margin-right-4 { 177 | margin-right: 4rem; 178 | } 179 | .padding-bottom-4 { 180 | padding-bottom: 4rem; 181 | } 182 | .margin-bottom-4 { 183 | margin-bottom: 4rem; 184 | } 185 | h1, 186 | h2, 187 | h3, 188 | h4, 189 | h5, 190 | h6 { 191 | line-height: 1.127; 192 | margin-top: 0; 193 | margin-bottom: 0.309rem; 194 | } 195 | h1 { 196 | font-size: 4.23em; 197 | } 198 | h2 { 199 | font-size: 2.617em; 200 | } 201 | h3 { 202 | font-size: 1.618em; 203 | } 204 | h4 { 205 | font-size: 1.272em; 206 | } 207 | h5 { 208 | font-size: 1.127em; 209 | } 210 | h6 { 211 | font-size: 1em; 212 | } 213 | -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #fcfcfc; 3 | } 4 | 5 | body, input, textarea { 6 | font-family: Actor; 7 | color: #333; 8 | font-size: 18px; 9 | } 10 | 11 | h1, h2, h3, h4 { 12 | font-family: Roboto Slab; 13 | } 14 | 15 | h1, h2 { 16 | font-weight: 400; 17 | letter-spacing: -1px; 18 | } 19 | 20 | h1 { 21 | font-size: 3.8em; 22 | } 23 | h2 { 24 | font-size: 2em; 25 | } 26 | 27 | .w { 28 | margin: 0 auto; 29 | max-width: 980px; 30 | } 31 | .section { 32 | padding: 5vmin 0; 33 | } 34 | 35 | .slogan { 36 | font-size: 1.3em; 37 | } 38 | 39 | .txt-center { 40 | text-align: center; 41 | } 42 | 43 | .header { 44 | background: #fcfcfc; 45 | overflow: hidden; 46 | } 47 | .ereader { 48 | margin-bottom: -8rem; 49 | } 50 | 51 | .gallery { 52 | background: #555 url(gallery-bg.jpg) repeat; 53 | color: #ddd; 54 | } 55 | .gallery h1, .gallery h2 { 56 | color: #fff; 57 | } 58 | .gallery a { 59 | color: #ddd; 60 | text-decoration: underline; 61 | } 62 | .single-line { 63 | padding: 0.5rem; 64 | } 65 | 66 | .book-gallery { 67 | overflow: auto; 68 | } 69 | .book-container { 70 | min-width: 25%; 71 | float: left; 72 | margin-top: 1rem; 73 | } 74 | .book { 75 | width: 100%; 76 | height: 0; 77 | padding-bottom: 133.33%; 78 | background-size: cover; 79 | background-repeat: no-repeat; 80 | background-color: #808080; 81 | box-shadow: rgba(0, 0, 0, 0.1) 0 1px 0, rgba(0, 0, 0, 0.1) 0 1px 5px; 82 | } 83 | .book::after { 84 | display: block; 85 | position: absolute; 86 | bottom: 0.5rem; 87 | right: -0.5rem; 88 | text-transform: uppercase; 89 | color: #fff; 90 | line-height: 1; 91 | padding: 0.25rem 0.5rem; 92 | font-size: 0.7em; 93 | text-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0; 94 | } 95 | .book.-alpha::after { 96 | background: #C0392B; 97 | content: 'Alpha' 98 | } 99 | .book.-beta::after { 100 | background: #E67E22; 101 | content: 'Beta' 102 | } 103 | .book.-stable::after { 104 | background: #27AE60; 105 | content: 'Stable' 106 | } 107 | 108 | .badge-explication { 109 | padding-bottom: 1rem; 110 | } 111 | .badge-explication-item { 112 | padding: 0.25rem 0; 113 | font-size: 14px; 114 | line-height: 1; 115 | } 116 | .badge-explication-item-descriptor { 117 | padding: 0.25rem 0.5rem; 118 | } 119 | .badge { 120 | text-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0; 121 | text-transform: uppercase; 122 | color: #fff; 123 | line-height: 1; 124 | padding: 0.25rem 0.5rem; 125 | margin-right: 0.25rem; 126 | } 127 | .badge.-alpha { 128 | background: #C0392B; 129 | } 130 | .badge.-beta { 131 | background: #E67E22; 132 | } 133 | .badge.-stable { 134 | background: #27AE60; 135 | } 136 | 137 | .book-title { 138 | font-weight: 700; 139 | margin-top: 1rem; 140 | } 141 | .book-date { 142 | font-size: 0.8em; 143 | font-weight: 400; 144 | } 145 | .book-controls { 146 | padding-top: 0.5rem; 147 | } 148 | .book-controls-control { 149 | padding: 0.33rem 0.8rem; 150 | border:1px solid #777; 151 | border-radius: 2rem; 152 | color: #eee; 153 | font-size: 0.75em; 154 | text-decoration: none !important; 155 | } 156 | .book-controls-control:hover { 157 | border-color: #fff; 158 | color: #fff; 159 | background: rgba(255, 255, 255, 0.05); 160 | } 161 | 162 | .github-corner { 163 | position: fixed; 164 | top: 0; 165 | right: 0; 166 | } 167 | 168 | .footer { 169 | background: #eee; 170 | } 171 | 172 | @media screen and (max-width: 980px) { 173 | body { 174 | font-size: 16px; 175 | } 176 | } 177 | 178 | @media screen and (max-width: 640px) { 179 | .hide-on-mobile { 180 | display: none; 181 | } 182 | 183 | .center-on-mobile { 184 | text-align: center; 185 | } 186 | 187 | h1 { 188 | font-size: 3em; 189 | } 190 | 191 | body { 192 | font-size: 14px; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var run = require('./src/run'); 2 | 3 | var _ = require('lodash'); 4 | var epub = require('epub-gen'); 5 | var fs = require('fs'); 6 | var async = require('async'); 7 | var slug = require('slug'); 8 | var moment = require('moment'); 9 | 10 | const tocObjToHtml = require('./src/tocObjToHtml'); 11 | 12 | var strategyToRunId = Math.max(process.argv.indexOf('--project'), process.argv.indexOf('-p')); 13 | var strategyToRun = strategyToRunId !== -1 && _.get(process.argv, parseInt(strategyToRunId) + 1); 14 | 15 | if (!strategyToRun) { 16 | console.error('Please pass an --project argument with the ID of the project that you want to compile'); 17 | process.exit(1); 18 | } 19 | 20 | console.log(`Scrapping ${strategyToRun}`); 21 | 22 | function getEpubOptions(tocObj) { 23 | return { 24 | title: tocObj.title, 25 | cover: tocObj.cover, 26 | author: tocObj.author, 27 | css: 'code,pre{font-size: 0.9em;background:#fafafa;padding:0.5em;display:block;margin:0.5rem 0}', 28 | content: _.chain(tocObj.content) 29 | .filter(el => el.result) 30 | .map(tocEl => { 31 | return { 32 | title: tocEl.title, 33 | data: tocEl.result ? tocEl.result.content : '' 34 | }; 35 | }) 36 | .value() 37 | }; 38 | } 39 | 40 | function execCommand(command, cb) { 41 | var sys = require('sys'); 42 | var exec = require('child_process').exec; 43 | function puts(error, stdout, stderr) { 44 | if (error) throw error; 45 | sys.puts(stdout); 46 | cb(); 47 | } 48 | exec(command, puts); 49 | } 50 | 51 | // generations trategies 52 | function generateEpubFromHtml(tocArray) { 53 | var epubOptions = getEpubOptions(tocArray); 54 | new epub(epubOptions, 'docs/download/' + strategyToRun + '.epub'); 55 | } 56 | 57 | function generateHtmlFromHtml(tocArray) { 58 | fs.writeFile('docs/download/' + strategyToRun + '.html', tocObjToHtml(tocArray), (err, res) => { 59 | // console.log({err, res}) 60 | }); 61 | } 62 | 63 | function generateEpubMetadata(docObj) { 64 | return new Promise(function(resolve, reject) { 65 | var epubMeta = ` 66 | 67 | --- 68 | title: 69 | - type: main 70 | text: ${docObj.title} 71 | creator: 72 | - role: author 73 | text: ${docObj.author} 74 | rights: ${docObj.licenceUrl} 75 | include-before: Documentation for ${docObj.title} ([${docObj.docsUrl}](${docObj.docsUrl})) generated by docs2epub ([http://javier.xyz/docs2epub/](http://javier.xyz/docs2epub/)) on ${moment().format('YYYY/MM/DD')}. 76 | date: ${moment().format('YYYY/MM/DD')} 77 | description: Documentation for ${docObj.title} ([${docObj.docsUrl}](${docObj.docsUrl})) generated by docs2epub ([http://javier.xyz/docs2epub/](http://javier.xyz/docs2epub/)) on ${moment().format('YYYY/MM/DD')}. 78 | ... 79 | 80 | `; 81 | fs.writeFile(`_tmp/epub/${slug(docObj.title)}/meta.txt`, epubMeta, (err, res) => { 82 | if (err) { 83 | reject(err); 84 | return; 85 | } 86 | resolve(res); 87 | }); 88 | }); 89 | } 90 | 91 | function generateEpubFromMarkdown(docObj) { 92 | return new Promise(function(resolve, reject) { 93 | execCommand(`rm -rf _tmp/epub/${slug(docObj.title)} && mkdir -p _tmp/epub/${slug(docObj.title)}`, () => { 94 | async.map( 95 | docObj.content, 96 | (tocEl, cb) => { 97 | if (tocEl.result) { 98 | fs.writeFile(`_tmp/epub/${slug(docObj.title)}/${tocEl.index}.md`, tocEl.result.content, cb); 99 | } 100 | }, 101 | (err, res) => { 102 | if (err) { 103 | reject(err); 104 | return; 105 | } 106 | var pandocCommand = `pandoc -s -o docs/download/${docObj.title.toLowerCase()}.epub _tmp/epub/${slug(docObj.title)}/meta.txt `; 107 | pandocCommand += _.map(docObj.content, tocEl => `_tmp/epub/${slug(docObj.title)}/${tocEl.index}.md `).join( 108 | '' 109 | ); 110 | 111 | if (docObj.cover) { 112 | pandocCommand += `--epub-cover-image=${docObj.cover} `; 113 | } 114 | 115 | if (docObj.epubStylesheet) { 116 | pandocCommand += `--epub-stylesheet=${docObj.epubStylesheet} `; 117 | } 118 | 119 | if (docObj.epubTOCDepth) { 120 | pandocCommand += `--table-of-contents --toc-depth=${docObj.epubTOCDepth}`; 121 | } 122 | 123 | generateEpubMetadata(docObj).then( 124 | () => { 125 | execCommand(pandocCommand, () => { 126 | console.log('Done.'); 127 | }); 128 | }, 129 | reject 130 | ); 131 | } 132 | ); 133 | }); 134 | }); 135 | } 136 | 137 | run(strategyToRun) 138 | .then(tocArray => { 139 | if (tocArray.type === 'MARKDOWN') { 140 | generateEpubFromMarkdown(tocArray).catch(err => console.log(err)); 141 | } else { 142 | generateEpubFromHtml(tocArray); 143 | generateHtmlFromHtml(tocArray); 144 | } 145 | }) 146 | .catch(err => { 147 | console.log(err.stack); 148 | }); 149 | -------------------------------------------------------------------------------- /src/strategies/react/scrapperMd.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const _ = require('lodash') 3 | const async = require('async') 4 | 5 | const CONFIG = require('./index.js') 6 | 7 | var docList = [{ 8 | title: 'Getting started', 9 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/getting-started.md' 10 | }, { 11 | title: 'Tutorial', 12 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/tutorial.md' 13 | }, { 14 | title: 'Thinking in react', 15 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/thinking-in-react.md' 16 | }, { 17 | title: 'Why react', 18 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/01-why-react.md' 19 | }, { 20 | title: 'Displaying data', 21 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/02-displaying-data.md' 22 | }, { 23 | title: 'JSX in depth', 24 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/02.1-jsx-in-depth.md' 25 | }, { 26 | title: 'JSX spread', 27 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/02.2-jsx-spread.md' 28 | }, { 29 | title: 'JSX gotchas', 30 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/02.3-jsx-gotchas.md' 31 | }, { 32 | title: 'Interactivity and dynamic uis', 33 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/03-interactivity-and-dynamic-uis.md' 34 | }, { 35 | title: 'Multiple components', 36 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/04-multiple-components.md' 37 | }, { 38 | title: 'Reusable components', 39 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/05-reusable-components.md' 40 | }, { 41 | title: 'Transferring props', 42 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/06-transferring-props.md' 43 | }, { 44 | title: 'Forms', 45 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/07-forms.md' 46 | }, { 47 | title: 'Working with the browser', 48 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/08-working-with-the-browser.md' 49 | }, { 50 | title: 'More about refs', 51 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/08.1-more-about-refs.md' 52 | }, { 53 | title: 'Tooling integration', 54 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/09-tooling-integration.md' 55 | }, { 56 | title: 'Language tooling', 57 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/09.1-language-tooling.md' 58 | }, { 59 | title: 'Package management', 60 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/09.2-package-management.md' 61 | }, { 62 | title: 'Environments', 63 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/09.3-environments.md' 64 | }, { 65 | title: 'Addons', 66 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10-addons.md' 67 | }, { 68 | title: 'Animation', 69 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.1-animation.md' 70 | }, { 71 | title: 'Form input binding sugar', 72 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.2-form-input-binding-sugar.md' 73 | }, { 74 | title: 'Class name manipulation', 75 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.3-class-name-manipulation.md' 76 | }, { 77 | title: 'Test utils', 78 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.4-test-utils.md' 79 | }, { 80 | title: 'Clone with props', 81 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.5-clone-with-props.md' 82 | }, { 83 | title: 'Create fragment', 84 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.6-create-fragment.md' 85 | }, { 86 | title: 'Update', 87 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.7-update.md' 88 | }, { 89 | title: 'Pure render mixin', 90 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.8-pure-render-mixin.md' 91 | }, { 92 | title: 'Perf', 93 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.9-perf.md' 94 | }, { 95 | title: 'Shallow compare', 96 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/10.10-shallow-compare.md' 97 | }, { 98 | title: 'Advanced performance', 99 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/11-advanced-performance.md' 100 | }, { 101 | title: 'Context', 102 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/12-context.md' 103 | }, { 104 | title: 'Top level api', 105 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-01-top-level-api.md' 106 | }, { 107 | title: 'Component api', 108 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-02-component-api.md' 109 | }, { 110 | title: 'Component specs', 111 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-03-component-specs.md' 112 | }, { 113 | title: 'Tags and attributes', 114 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-04-tags-and-attributes.md' 115 | }, { 116 | title: 'Events', 117 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-05-events.md' 118 | }, { 119 | title: 'Dom differences', 120 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-06-dom-differences.md' 121 | }, { 122 | title: 'Special non dom attributes', 123 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-07-special-non-dom-attributes.md' 124 | }, { 125 | title: 'Reconciliation', 126 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-08-reconciliation.md' 127 | }, { 128 | title: 'Webcomponents', 129 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-09-webcomponents.md' 130 | }, { 131 | title: 'Glossary', 132 | url: 'https://raw.githubusercontent.com/facebook/react/master/docs/docs/ref-10-glossary.md' 133 | }] 134 | 135 | docList = _.map(docList, (el, idx) => { 136 | el.index = ('000' + idx).slice(-3) 137 | return el 138 | }) 139 | 140 | function requestUrl (doc, cb) { 141 | axios.get(doc.url).then(res => { 142 | console.log('READED', doc.url) 143 | cb(null, _.assign({}, doc, { 144 | result: { 145 | title: doc.title, 146 | content: `\n# ${doc.title}\n\n` + res.data 147 | .replace(/\]\(\/react\/img/g, '](https://raw.githubusercontent.com/facebook/react/master/docs/img') 148 | .replace(//, 'See [$1]($1)') 149 | } 150 | })) 151 | }, (err) => { 152 | cb(err) 153 | }) 154 | } 155 | 156 | function strategy () { 157 | return new Promise(function (resolve, reject) { 158 | async.map(docList, requestUrl, function (asyncErr, asyncRes) { 159 | if (asyncErr) { 160 | reject(asyncErr) 161 | return 162 | } 163 | 164 | resolve( 165 | _.assign({}, CONFIG, { 166 | content: asyncRes 167 | }) 168 | ) 169 | }) 170 | }) 171 | } 172 | 173 | module.exports = strategy 174 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | docs2epub 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |

docs2epub

31 |
32 |
Get your favorite docs as ebooks!
33 |
Doc scraper and ebook generator.
34 |
35 |
36 | 37 | 38 | 39 |
40 |
41 |
42 | 43 |
44 |
45 | 46 | 133 | 134 |
135 |
136 |
137 |

Want to know about new ebooks?

138 |
139 | 140 | 141 | 146 |
147 |
148 |
149 | 150 | 151 | 152 | 153 |
154 |
155 |
156 |
157 | 158 | 159 |
160 | Max one email per week, promise 🙌 161 |
162 |
163 |
164 | 165 | 170 | 171 | 172 | 173 | 181 | 182 | 183 | --------------------------------------------------------------------------------