├── .gitignore ├── .nvmrc ├── .travis.yml ├── README.md ├── build └── README.md ├── config.js ├── modules.md ├── package.json ├── scripts ├── html.js ├── index.js ├── modules.js ├── read.js ├── render.js ├── sync.js └── util.js └── src ├── index.js ├── router.js └── views ├── contributor-list.js ├── module-page.js └── nav.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log* 3 | /src/model.json 4 | /build 5 | .data 6 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 4 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | branches: 3 | only: 4 | - master 5 | before_script: 6 | - git config --global user.email "michael.williams@enspiral.com" 7 | - git config --global user.name "Mikey" 8 | - git remote add deploy https://ahdinosaur:${GH_TOKEN}@github.com/pull-stream/pull-stream.github.io.git 9 | script: npm run travis 10 | env: 11 | global: 12 | secure: Y4Z19AA5X+XM88/eXKKPaERe7Vahx6SCsHv98v9BlL2jl9lUve0U8UThTuuSIcw3hq9y8vdPpphGRaZNt5SSdupyF6kkmGlrYlDWPbN6+j3QbiFqzRmBj6oAiDsdou69Z54WoGOPkOqmxSvOdKT+6EZYt2NXgvv1a1/2s5w6iOrwpQH8HvAbwqUJQKat2nO4NohR5ylrabABVWkU1ftetwggLrIfjbr7YnMzzORiOWnv2UERtASOiLtYIXz4cpv3jSe90OWmd8REHa6e1OjMA77YCRmGw4FG2cdBosGGpU4cVi2Qq/GNkM51hwlG1vKQo248iea5d9RUXBgsIoL5u70CBGII3wXfWbXePbG7yFNbagd/9UfYUVi5yjsuc1MQxFhzeJfGqY9fCAW7+ucQ4taKJDWyjgIv9h3e6kPmOZ+HiXElLgPtsU/pXMND70cJoaEQySqDTwIWlV2Sav5xXFL1LGJx5SXgFeo6T5KVV9lonbSiUggUVDdk+WFHzp9+xZAzPlivE+TXeOBCQ0nhjVmShk3ZP7xucwcLOIJf5Oa21Sw6gZd4JRRA2cWn6oSlYXXSapYAWY/d2ZyRqBXX1qOtSZ9duLqSgXiB1fNTDiN3BkWLkNbmOS8UigSqaeEkAxqmhzEsUIlBmq+ciPsqav8NRQ5lQacfUsTE0Hq7mm8= 13 | cache: 14 | directories: 15 | - node_modules 16 | - .data 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pull-stream-docs 2 | 3 | - uses [`ecosystem-docs`](https://github.com/hughsk/ecosystem-docs) to aggregate modules within the `pull-stream` ecosystem, both inside the [`pull-stream` org](https://github.com/pull-stream) and out. 4 | - using [`inu`](https://github.com/ahdinosaur/inu) to render a static website to discover and reference modules within the `pull-stream` ecosystem. 5 | 6 | the list of modules included within the ecosystem is [./modules.md](./modules.md). 7 | 8 | the built output is [`pull-stream/pull-stream.github.io`](https://github.com/pull-stream/pull-stream.github.io) which is rendered at . 9 | 10 | ## adding a module to the ecosystem 11 | 12 | 1. edit [./modules.md](./modules.md) to include your module within the appropriate category. 13 | 1. submit a pull request here to apply this change. 14 | 1. a `pull-stream` maintainer will merge your change and re-deploy the website. 15 | 16 | ## maintainer how to 17 | 18 | ### install 19 | 20 | ```shell 21 | git clone git://github.com/pull-stream/pull-stream-docs 22 | cd pull-stream-docs 23 | npm install 24 | ``` 25 | 26 | ### start development server 27 | 28 | ```shell 29 | npm start 30 | ``` 31 | 32 | ### deploy to production 33 | 34 | ```shell 35 | npm run deploy:remote 36 | npm run deploy 37 | ``` 38 | 39 | ## inspiration 40 | 41 | - [stackgl/packages](https://github.com/stackgl/packages) 42 | 43 | ## license 44 | 45 | The Apache License 46 | 47 | Copyright © 2016 Michael Williams 48 | 49 | Licensed under the Apache License, Version 2.0 (the "License"); 50 | you may not use this file except in compliance with the License. 51 | You may obtain a copy of the License at 52 | 53 | http://www.apache.org/licenses/LICENSE-2.0 54 | 55 | Unless required by applicable law or agreed to in writing, software 56 | distributed under the License is distributed on an "AS IS" BASIS, 57 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 58 | See the License for the specific language governing permissions and 59 | limitations under the License. 60 | -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | # pull-stream.github.io 2 | 3 | > Website for the pull-stream ecosystem 4 | 5 | Generated using https://github.com/pull-stream/pull-stream-docs 6 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const Path = require('path') 2 | 3 | module.exports = { 4 | data: Path.join(__dirname, '.data'), 5 | modules: Path.join(__dirname, 'modules.md'), 6 | index: Path.join(__dirname, 'build/index.html'), 7 | model: Path.join(__dirname, 'src/model.json'), 8 | core: Path.join(__dirname, 'node_modules/pull-stream') 9 | } 10 | -------------------------------------------------------------------------------- /modules.md: -------------------------------------------------------------------------------- 1 | # core 2 | 3 | * pull-stream/pull-stream 4 | 5 | # combinators 6 | 7 | * pull-stream/pull-cat 8 | * pull-stream/pull-defer 9 | * pull-stream/pull-many 10 | * pull-stream/pull-merge 11 | * pull-stream/pull-paramap 12 | * pull-stream/pull-flatmap 13 | * dominictarr/pull-map-last 14 | * pull-stream/pull-pair 15 | * pull-stream/pull-through 16 | * pull-stream/pull-traverse 17 | * pull-stream/pull-window 18 | * pull-stream/pull-next 19 | * pull-stream/pull-zip 20 | * pull-stream/pull-sort 21 | * pull-stream/pull-sorted-merge 22 | * pull-stream/pull-tee 23 | * pull-stream/pull-high-watermark 24 | * pull-stream/pull-abortable 25 | * pull-stream/pull-peek 26 | * pull-stream/pull-cache 27 | * regular/pull-generate 28 | * regular/pull-debounce 29 | * nichoth/pull-combine-latest 30 | * nichoth/pull-with-latest 31 | * nichoth/pull-scan 32 | * dominictarr/pull-cont 33 | * nichoth/pull-catch 34 | * elavoie/pull-stream-function-to-object 35 | * elavoie/pull-eager-buffer 36 | * amsross/pull-fmap 37 | * amsross/pull-monad 38 | * amsross/pull-tap 39 | * nichoth/pull-flat-merge 40 | * jdvorak/pull-offset-limit 41 | * dominictarr/pull-group 42 | * elavoie/pull-stubborn 43 | 44 | # real-time 45 | 46 | * pull-stream/pull-pushable 47 | * pull-stream/pull-notify 48 | * pull-stream/pull-live 49 | * dominictarr/pull-throttle 50 | 51 | # file system and databases 52 | 53 | * pull-stream/pull-glob 54 | * pull-stream/pull-level 55 | * DamonOehlman/pull-file 56 | * dominictarr/pull-write 57 | * dominictarr/pull-write-file 58 | * pull-stream/pull-watch 59 | * ipfs/interface-pull-blob-store 60 | * ipfs/js-fs-pull-blob-store 61 | * ipfs/js-idb-pull-blob-store 62 | * jamen/pull-vinyl 63 | * jamen/pull-files 64 | * dominictarr/multiblob 65 | * tableflip/pull-file-reader 66 | 67 | 68 | # text 69 | 70 | * pull-stream/pull-split 71 | * pull-stream/pull-stringify 72 | * dominictarr/pull-json-doubleline 73 | * pull-stream/pull-utf8-decoder 74 | * diasdavid/pull-ndjson 75 | 76 | # binary 77 | 78 | * pull-stream/pull-handshake 79 | * dominictarr/pull-randomly-split 80 | * dominictarr/pull-reader 81 | * dignifiedquire/pull-length-prefixed 82 | * dignifiedquire/pull-block 83 | * jamen/pull-stdio 84 | 85 | # networks 86 | 87 | * dominictarr/pull-net 88 | * DamonOehlman/pull-ws 89 | * pull-stream/pull-ws-server 90 | * ssbc/muxrpc 91 | * ssbc/multiserver 92 | * pull-stream/pull-http-server 93 | * regular/pull-paginated-api-request 94 | * jamen/pull-fetch 95 | * ahdinosaur/pull-xhr 96 | * elavoie/pull-sync 97 | * elavoie/pull-limit 98 | * elavoie/pull-lend 99 | * elavoie/pull-lend-stream 100 | * reqshark/pull-recvfrom 101 | 102 | # interop 103 | 104 | * pull-stream/stream-to-pull-stream 105 | * pull-stream/pull-stream-to-stream 106 | * queckezz/pull-promise 107 | * rjmk/static-land-pull-stream 108 | * jamen/pull-spawn-process 109 | * nrkn/pull-iterable 110 | * maackle/pull2chan 111 | * brechtcs/pull-resolve 112 | * alanshaw/pull-stream-to-async-iterator 113 | * alanshaw/async-iterator-to-pull-stream 114 | 115 | # crypto 116 | 117 | * dominictarr/pull-box-stream 118 | * auditdrivencrypto/secret-handshake 119 | 120 | # dom 121 | 122 | * pietgeursen/pull-dom-mutants 123 | * pietgeursen/pull-dom-driver 124 | * santiagogil/pull-ric 125 | * nichoth/react-pull-stream 126 | * nichoth/yo-pull-stream 127 | 128 | # parser 129 | 130 | * jamen/pull-css 131 | 132 | # debugging 133 | 134 | * elavoie/pull-probe 135 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pull-stream-docs", 3 | "version": "0.0.0", 4 | "description": "pull stream ecosystem docs", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "true", 8 | "bundle": "browserify src -g envify -g uglifyify -p [ css-extract -o build/bundle.css ]", 9 | "build:sync": "DEBUG=* node scripts sync", 10 | "build:html": "node scripts html", 11 | "build:js": "npm run --silent bundle -- -o build/bundle.js", 12 | "build": "npm-run-all -s build:*", 13 | "watch:sync": "chokidar 'scripts/sync.js' -c $(which node)' scripts sync'", 14 | "watch:html": "chokidar 'scripts/*.js' -c $(which node)' scripts html'", 15 | "watch:js": "budo src:bundle.js -d build --live -- -d", 16 | "watch": "npm-run-all -p watch:*", 17 | "start": "npm-run-all -s build:sync build:html watch", 18 | "disc": "npm run --silent bundle -- --full-paths | discify --open", 19 | "push": "gh-pages -d build -o deploy -b master", 20 | "deploy:remote": "git remote add deploy git@github.com:pull-stream/pull-stream.github.io", 21 | "deploy": "npm-run-all -s build push", 22 | "travis": "if test \"$TRAVIS\" = \"true\" && test \"$TRAVIS_EVENT_TYPE\" != \"pull_request\" && test \"$TRAVIS_BRANCH\" = \"master\"; then npm run deploy; else npm run build; fi" 23 | }, 24 | "browserify": { 25 | "transform": [ 26 | "sheetify/transform", 27 | "es2020", 28 | "brfs" 29 | ] 30 | }, 31 | "remarkConfig": { 32 | "settings": { 33 | "commonmark": true, 34 | "bullet": "*" 35 | } 36 | }, 37 | "repository": { 38 | "type": "git", 39 | "url": "git+https://github.com/ahdinosaur/pull-stream-docs.git" 40 | }, 41 | "keywords": [], 42 | "author": "Mikey (http://dinosaur.is)", 43 | "license": "Apache-2.0", 44 | "bugs": { 45 | "url": "https://github.com/ahdinosaur/pull-stream-docs/issues" 46 | }, 47 | "homepage": "https://github.com/ahdinosaur/pull-stream-docs#readme", 48 | "devDependencies": { 49 | "brfs": "^1.4.3", 50 | "browserify": "^13.0.0", 51 | "budo": "^8.1.0", 52 | "cheerio": "^0.20.0", 53 | "chokidar-cli": "^1.2.0", 54 | "css-extract": "^1.1.0", 55 | "disc": "^1.3.2", 56 | "ecosystem-docs": "^1.1.1", 57 | "envify": "^3.4.0", 58 | "es2020": "^1.1.6", 59 | "gh-pages": "^0.11.0", 60 | "ghauth": "^3.2.0", 61 | "npm-run-all": "^1.6.0", 62 | "remark": "^4.2.2", 63 | "remark-github": "^4.0.1", 64 | "remark-highlight.js": "^3.1.0", 65 | "remark-html": "^3.0.0", 66 | "run-parallel": "^1.1.6", 67 | "tape": "^4.5.1", 68 | "uglifyify": "^3.0.1", 69 | "watchify": "^3.7.0" 70 | }, 71 | "dependencies": { 72 | "array-find": "^1.0.0", 73 | "hash-match": "^1.0.2", 74 | "highlight.js": "^9.4.0", 75 | "insert-css": "^1.1.0", 76 | "inu": "^2.3.1", 77 | "normalize.css": "^4.1.1", 78 | "pull-pushable": "^2.0.0", 79 | "pull-stream": "github:pull-stream/pull-stream", 80 | "sheetify": "^5.0.1" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /scripts/html.js: -------------------------------------------------------------------------------- 1 | const ecosystem = require('ecosystem-docs') 2 | 3 | const config = require('../config') 4 | const util = require('./util') 5 | const read = require('./read') 6 | const render = require('./render') 7 | 8 | module.exports = html 9 | 10 | function html (modules, done) { 11 | read(modules, function (err, modules) { 12 | if (err) { return done(err) } 13 | 14 | render(modules, done) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /scripts/index.js: -------------------------------------------------------------------------------- 1 | const modules = require('./modules') 2 | const commands = { 3 | sync: require('./sync'), 4 | html: require('./html') 5 | } 6 | const command = process.argv[2] 7 | 8 | commands[command](modules, function (err) { 9 | if (err) throw err 10 | }) 11 | 12 | -------------------------------------------------------------------------------- /scripts/modules.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const Path = require('path') 3 | const remark = require('remark') 4 | const assert = require('assert') 5 | 6 | const config = require('../config') 7 | 8 | module.exports = getModulesByCategory( 9 | fs.readFileSync(config.modules, 'utf8') 10 | ) 11 | 12 | function getModulesByCategory (modulesMarkdown) { 13 | const ast = remark.parse(String(modulesMarkdown)) 14 | const modules = [] 15 | 16 | var category = null 17 | 18 | ast.children.forEach((node) => { 19 | const type = node.type 20 | 21 | if (type === 'heading') { 22 | return category = remark 23 | .stringify(node) 24 | .replace(/^\#+/g, '') 25 | .trim() 26 | } else if (type !== 'list' || category === null) { 27 | return 28 | } 29 | 30 | node.children.forEach((listItem) => { 31 | // TODO why error on stringify(listItem) ? 32 | assert.equal(listItem.children.length, 1, 'list item should have only one child.') 33 | const modulePath = remark.stringify(listItem.children[0]) 34 | modules.push({ 35 | path: modulePath, 36 | category 37 | }) 38 | }) 39 | }) 40 | 41 | return modules 42 | } 43 | -------------------------------------------------------------------------------- /scripts/read.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const Path = require('path') 3 | const ecosystem = require('ecosystem-docs') 4 | const remark = require('remark') 5 | const html = require('remark-html') 6 | const highlight = require('remark-highlight.js') 7 | const cheerio = require('cheerio') 8 | 9 | const util = require('./util') 10 | const config = require('../config') 11 | 12 | module.exports = readModules 13 | 14 | function readModules (modules, done) { 15 | const moduleList = util.toModuleList(modules) 16 | 17 | ecosystem.read(moduleList, { 18 | data: config.data 19 | }, (err, data) => { 20 | if (err) { return done(err) } 21 | 22 | data = formatData(modules, data) 23 | 24 | const coreModule = data.modules.find((module) => { 25 | return module.category === 'core' 26 | }) 27 | getCoreModules(coreModule, config.core).map((module) => { 28 | data.modules.push(module) 29 | }) 30 | coreModule.category = null 31 | 32 | done(null, data) 33 | }) 34 | } 35 | 36 | function formatData (origModules, data) { 37 | const contributors = [] 38 | const categories = [] 39 | return { 40 | contributors: contributors, 41 | categories: categories, 42 | modules: data.map((module) => { 43 | const category = getCategory(origModules, module) 44 | if (categories.indexOf(category) === -1) categories.push(category) 45 | return { 46 | user: module.user, 47 | name: module.name, 48 | path: module.path, 49 | name: module.name, 50 | stars: module.stars, 51 | issues: module.issues, 52 | readme: formatReadme(module), 53 | contributors: indexContributors(contributors, module.contributors), 54 | version: module.package && module.package.version || false, 55 | npmName: module.package && module.package.name || false, 56 | category: category 57 | } 58 | }) 59 | } 60 | } 61 | 62 | function indexContributors (indexed, contributors) { 63 | return contributors.map((contributor) => { 64 | const formatted = formatContributor(contributor) 65 | const index = indexed.findIndex(c => c.name === formatted.name) 66 | if (index === -1) { 67 | indexed.push(formatted) 68 | return indexed.length - 1 69 | } else { 70 | return index 71 | } 72 | }) 73 | } 74 | 75 | function formatContributor (contributor) { 76 | return { 77 | name: contributor.login, 78 | image: contributor.avatar_url 79 | } 80 | } 81 | 82 | function formatReadme (module) { 83 | const ast = remark.parse(module.readme) 84 | 85 | // remove all content before the first heading 86 | for (var i = 0; i < ast.children.length; i++) { 87 | var child = ast.children[i] 88 | if (child.type === 'heading') break 89 | ast.children.splice(i--, 1) 90 | } 91 | 92 | // ensure there's only one

in the document, 93 | // and that

's that are code-only are at least

94 | for (; i < ast.children.length; i++) { 95 | var child = ast.children[i] 96 | if (child.type !== 'heading') continue 97 | if (child.depth === 1) child.depth = 2 98 | if (child.depth === 2) { 99 | if (child.children.length !== 1) continue 100 | if (child.children[0].type !== 'inlineCode') continue 101 | child.depth = 3 102 | } 103 | } 104 | 105 | var $ = cheerio.load( 106 | remark() 107 | .use([html, highlight]) 108 | .process(remark.stringify(ast)) 109 | ) 110 | 111 | // Remove badges 112 | $('img[src*="://img.shields.io"]').remove() 113 | $('img[src*="://badges.github.io"]').remove() 114 | $('img[src*="://nodei.co"]').remove() 115 | $('img[src*="://david-dm.org"]').remove() 116 | $('img[src*="://badge.fury.io"]').remove() 117 | $('img[src*="://travis-ci.org"]').remove() 118 | $('img[src*="://secure.travis-ci.org"]').remove() 119 | $('img[src*="://ci.testling.com"]').remove() 120 | $('img[src*="://coveralls.io"]').remove() 121 | $('img[src*="://circleci.com"]').remove() 122 | 123 | $('h1 img').remove() 124 | 125 | // Resolve relative URLs in READMEs for images and anchors 126 | $('a:not([href^=http]):not([href^=#])').each(function(i,el) { 127 | var $a = $(el) 128 | $a.attr('href', 'http://github.com/' + module.path + '/blob/master/' + (module.subpath ? `docs/${module.subpath}/../` : '') + $a.attr('href')) 129 | }) 130 | 131 | $('img:not([src^=http])').each(function(i,el) { 132 | var $img = $(el) 133 | $img.attr('src', 'https://raw.githubusercontent.com/' + module.path + '/master/' (module.subpath ? `docs/${module.subpath}/../` : '') + $img.attr('src')) 134 | }) 135 | 136 | // guarantee that the first heading is an

, 137 | // and wrap it up in an link to the repository. 138 | var headings = $('h1, h2, h3, h4, h5, h6') 139 | if (headings && headings[0] && module.path) { 140 | $(headings[0]).replaceWith($( 141 | '

' 142 | + '' 143 | + $(headings[0]).text() 144 | + '' 145 | + '
' 146 | + '

' 147 | )) 148 | } 149 | 150 | return $.html() 151 | } 152 | 153 | function getCategory (modulesWithCategory, module) { 154 | return modulesWithCategory 155 | .find((moduleWithCategory) => { 156 | return module.path === moduleWithCategory.path 157 | }) 158 | .category 159 | } 160 | 161 | function getCoreModules (coreModule, corePath) { 162 | const modules = [] 163 | 164 | ;['pull'].forEach((moduleName) => { 165 | modules.push(readCoreModule(coreModule, corePath, moduleName, moduleName)) 166 | }) 167 | 168 | ;['sources', 'throughs', 'sinks'].forEach((type) => { 169 | const typePath = Path.join(corePath, type) 170 | const moduleNames = fs.readdirSync(typePath) 171 | .filter(p => p.endsWith('.js')) 172 | .filter(p => p !== 'index.js') 173 | .map(p => p.slice(0, -3)) 174 | 175 | moduleNames.forEach((moduleName) => { 176 | const modulePath = Path.join(type, moduleName) 177 | modules.push(readCoreModule(coreModule, corePath, moduleName, modulePath)) 178 | }) 179 | }) 180 | 181 | return modules 182 | } 183 | 184 | function readCoreModule (coreModule, corePath, moduleName, modulePath) { 185 | const requirePath = Path.join(coreModule.name, modulePath) 186 | const readmePath = Path.join(corePath, 'docs', modulePath + '.md') 187 | 188 | var readme 189 | try { 190 | readme = fs.readFileSync(readmePath, 'utf8') 191 | } catch (err) { 192 | if (err.code !== 'ENOENT') { throw err } 193 | readme = `# ${requirePath}` 194 | } 195 | 196 | var module = { 197 | user: coreModule.user, 198 | name: moduleName, 199 | path: coreModule.path, 200 | subpath: modulePath, 201 | stars: coreModule.stars, 202 | issues: coreModule.issues, 203 | readme: readme, 204 | contributors: coreModule.contributors, 205 | version: coreModule.package && coreModule.package.version || false, 206 | npmName: coreModule.package && coreModule.package.name || false, 207 | category: coreModule.category 208 | } 209 | return Object.assign(module, { 210 | readme: formatReadme(module) 211 | }) 212 | } 213 | -------------------------------------------------------------------------------- /scripts/render.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const inu = require('inu') 3 | const parallel = require('run-parallel') 4 | 5 | const config = require('../config') 6 | 7 | module.exports = render 8 | 9 | function render (model, done) { 10 | const doctype = '\n' 11 | const html = doctype + inu.html` 12 | 13 | 14 | pull-stream 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | ` 24 | 25 | parallel([ 26 | (cb) => { 27 | fs.writeFile(config.index, html, cb) 28 | }, 29 | (cb) => { 30 | fs.writeFile(config.model, JSON.stringify(model), cb) 31 | } 32 | ], done) 33 | } 34 | -------------------------------------------------------------------------------- /scripts/sync.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const ghAuth = require('ghauth') 3 | const ecosystem = require('ecosystem-docs') 4 | 5 | const util = require('./util') 6 | const config = require('../config') 7 | 8 | module.exports = syncModules 9 | 10 | function syncModules (modules, done) { 11 | const moduleList = util.toModuleList(modules) 12 | 13 | function sync (token) { 14 | ecosystem.sync(moduleList, { 15 | data: config.data, 16 | token: token 17 | }, done) 18 | } 19 | 20 | if (process.env.GH_TOKEN) { 21 | sync(process.env.GH_TOKEN) 22 | } else { 23 | ghAuth({ 24 | configName: 'ecosystem-docs', 25 | userAgent: 'ecosystem-docs', 26 | scopes: ['user'] 27 | }, (err, auth) => { 28 | if (err) { return done(err) } 29 | sync(auth.token) 30 | }) 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /scripts/util.js: -------------------------------------------------------------------------------- 1 | function toModuleList (modules) { 2 | return modules.map(module => module.path) 3 | } 4 | 5 | function indexByKey (key, array) { 6 | return array.reduce((sofar, value) => { 7 | const index = value[key] 8 | return Object.assign(sofar, { 9 | [index]: sofar[index] === undefined 10 | ? [value] 11 | : sofar[index].concat([value]) 12 | }) 13 | }, {}) 14 | } 15 | 16 | module.exports = { 17 | toModuleList, 18 | indexByCategory: indexByKey.bind(null, 'category') 19 | } 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const inu = require('inu') 3 | const pull = inu.pull 4 | const find = require('array-find') 5 | const css = require('sheetify') 6 | 7 | css('normalize.css') 8 | 9 | const model = JSON.parse( 10 | fs.readFileSync(__dirname + '/model.json', 'utf8') 11 | ) 12 | 13 | const router = require('./router') 14 | const nav = require('./views/nav') 15 | const modulePage = require('./views/module-page') 16 | 17 | const prefix = css` 18 | nav { 19 | position: fixed; 20 | top: 0px; 21 | bottom: 0px; 22 | left: 0px; 23 | width: 332px; 24 | overflow-x: hidden; 25 | overflow-y: auto; 26 | } 27 | 28 | article { 29 | margin-left: 332px; 30 | } 31 | ` 32 | 33 | const app = { 34 | init: () => { 35 | const routerState = router.init() 36 | return { 37 | model: Object.assign(model, { 38 | modules: model.modules.map((module) => { 39 | return Object.assign(module, { 40 | contributors: deIndexContributors(model.contributors, module.contributors) 41 | }) 42 | }), 43 | route: routerState.model 44 | }), 45 | effect: routerState.effect 46 | } 47 | }, 48 | update: (model, action) => { 49 | const domain = action.type.split(':')[0] 50 | if (domain === 'router') { 51 | return { 52 | model: Object.assign({}, model, { 53 | route: router.update(model, action).model 54 | }) 55 | } 56 | } 57 | return { model: model } 58 | }, 59 | view: (model) => { 60 | const route = model.route || 'pull-stream' 61 | const module = find(model.modules, (module) => { 62 | return module.name === route 63 | }) 64 | 65 | return inu.html` 66 |
67 | ${nav(model)} 68 | ${modulePage(module)} 69 |
70 | ` 71 | }, 72 | run: router.run 73 | } 74 | 75 | const main = document.querySelector('main') 76 | 77 | pull( 78 | inu.start(app).views(), 79 | pull.drain((view) => { 80 | inu.html.update(main, view) 81 | }) 82 | ) 83 | 84 | function deIndexContributors (indexed, contributors) { 85 | return contributors.map((index) => indexed[index]) 86 | } 87 | -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | const hashMatch = require('hash-match') 2 | const Pushable = require('pull-pushable') 3 | 4 | // routing demo 5 | module.exports = { 6 | 7 | init: () => ({ 8 | model: formatHash(window.location.hash), 9 | effect: { type: 'router:init' } 10 | }), 11 | 12 | update: (model, action) => { 13 | switch (action.type) { 14 | case 'router:set': 15 | return { model: action.payload } 16 | default: 17 | return { model: model } 18 | } 19 | }, 20 | 21 | run: (effect, actions) => { 22 | if (effect.type === 'router:init') { 23 | var effectActions 24 | const handleLocationChange = () => { 25 | effectActions.push({ 26 | type: 'router:set', 27 | payload: formatHash(window.location.hash) 28 | }) 29 | } 30 | effectActions = Pushable((err) => { 31 | window.removeEventListener(handleLocationChange) 32 | }) 33 | window.addEventListener('hashchange', handleLocationChange) 34 | return effectActions 35 | } 36 | } 37 | } 38 | 39 | function formatHash (hash) { 40 | return hashMatch(hash).slice(1) || null 41 | } 42 | -------------------------------------------------------------------------------- /src/views/contributor-list.js: -------------------------------------------------------------------------------- 1 | const html = require('inu/html') 2 | const css = require('sheetify') 3 | 4 | const prefix = css` 5 | :host { 6 | overflow: hidden; 7 | } 8 | 9 | ul { 10 | display: flex; 11 | list-style-type: none; 12 | margin: 0px; 13 | padding: 0px; 14 | } 15 | 16 | img { 17 | display: inline-block; 18 | border-radius: 25px; 19 | width: 25px; 20 | height: 25px; 21 | margin-left: 5px; 22 | } 23 | ` 24 | 25 | module.exports = contributorsList 26 | 27 | function contributorsList (contributors) { 28 | return html` 29 |
30 |
    31 | ${contributors.slice(0, 20).sort(shuffle).map(c => html` 32 |
  • 33 | 34 | 35 | 36 |
  • 37 | `)} 38 |
39 |
40 | ` 41 | } 42 | 43 | function shuffle() { 44 | return Math.random() - 0.5 45 | } 46 | -------------------------------------------------------------------------------- /src/views/module-page.js: -------------------------------------------------------------------------------- 1 | const css = require('sheetify') 2 | 3 | const contribList = require('./contributor-list') 4 | 5 | css('highlight.js/styles/agate.css') 6 | 7 | module.exports = modulePage 8 | 9 | function modulePage (module) { 10 | const el = document.createElement('article') 11 | 12 | el.innerHTML = module.readme 13 | 14 | const title = el.querySelector('.title') 15 | const contrib = title.querySelector('.contrib') 16 | 17 | title.replaceChild(contribList(module.contributors), contrib) 18 | 19 | return el 20 | } 21 | -------------------------------------------------------------------------------- /src/views/nav.js: -------------------------------------------------------------------------------- 1 | const html = require('inu').html 2 | 3 | module.exports = nav 4 | 5 | function nav (model) { 6 | return html` 7 | 30 | ` 31 | } 32 | --------------------------------------------------------------------------------