├── .jshintignore ├── package.json ├── .jshintrc ├── .gitignore ├── LICENSE ├── .fancom ├── README.md ├── index.js └── walker.js /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "md-file-tree", 3 | "version": "0.2.0", 4 | "author": "Michał Budzyński ", 5 | "repository": "git@github.com:michalbe/md-file-tree.git", 6 | "bin": { 7 | "md-file-tree": "./index.js" 8 | }, 9 | "scripts": { 10 | "lint": "jshint ." 11 | }, 12 | "dependencies": { 13 | "event-state": "^0.3.5" 14 | }, 15 | "devDependencies": { 16 | "jshint": "^2.10.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "camelcase": false, 3 | "curly": true, 4 | "forin": false, 5 | "latedef": false, 6 | "newcap": false, 7 | "noarg": true, 8 | "nonew": true, 9 | "quotmark": "single", 10 | "undef": true, 11 | "unused": "vars", 12 | "strict": true, 13 | "trailing": true, 14 | "maxlen": 120, 15 | "eqnull": true, 16 | "esnext": true, 17 | "expr": true, 18 | "globalstrict": true, 19 | "maxerr": 1000, 20 | "regexdash": true, 21 | "laxcomma": true, 22 | "proto": true, 23 | "node": true, 24 | "devel": true, 25 | "nonstandard": true, 26 | "worker": true 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test.md 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 29 | node_modules 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Michał Budzyński 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /.fancom: -------------------------------------------------------------------------------- 1 | # ____ 2 | # / __/___ _____ _________ ____ ___ 3 | # / /_/ __ `/ __ \/ ___/ __ \/ __ `__ \ 4 | # / __/ /_/ / / / / /__/ /_/ / / / / / / 5 | # /_/ \__,_/_/ /_/\___/\____/_/ /_/ /_/ 6 | # 7 | # Fancy Commit Manager Config file 8 | # https://github.com/michalbe/fancom 9 | 10 | # Name of the repository, default: name of the current repo 11 | #reponame="reponame" 12 | 13 | # Do we want to push to the temporary branch? 14 | #pushtotemp=1 15 | 16 | # After pushing to the repo, what branch needs to be compared? 17 | #comparewith="branch to compare" 18 | 19 | # Suffix added to the temporary created, developer's branch, default: 'temp' 20 | #devsuffix="temp" 21 | 22 | # Set to `1` to add random emoji file to the commit message, default: no 23 | emoji=1 24 | 25 | # Add all the changes made in the repo to the commit, default: no 26 | #addall=1 27 | 28 | # Prefix added to the commit message, default: [REPONAME] 29 | #prefix="Shit's on fire yo" 30 | 31 | # Add branch name to the prefix of the commit, default: no 32 | #branchincommit=1 33 | 34 | # Skip the prefix in the commit message, default: no 35 | noprefix=1 36 | 37 | # Open github compare page in the browser after succesfull push 38 | opencompare=0 39 | 40 | # Github username, default: username of the owner of the current repo 41 | #author="authorname" 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # md-file-tree by [@michalbe](http://github.com/michalbe) 2 | 3 | Generate markdown tree of all the files in a directory, recursively. 4 | 5 | ## How to use? 6 | 7 | ### Install the script 8 | 9 | ```bash 10 | $ npm install md-file-tree -g 11 | ``` 12 | 13 | ### Run the tree script in any directory 14 | 15 | ```bash 16 | $ md-file-tree 17 | ``` 18 | 19 | ### Enable emoji (📂 & 📄) with the command line switch 20 | 21 | ```bash 22 | $ md-file-tree --emoji 23 | $ md-file-tree -e 24 | ``` 25 | 26 | ### Redirect the output to a file 27 | 28 | ```bash 29 | $ md-file-tree > list.md 30 | ``` 31 | 32 | This generates the `list.md` file with: 33 | 34 | ```markdown 35 | - __michal__ 36 | - [LICENSE](LICENSE) 37 | - [README.md](README.md) 38 | - __bin__ 39 | - [cli.js](bin/cli.js) 40 | - [michal.png](michal.png) 41 | - [node\_modules](node_modules) 42 | - [npm\-debug.log](npm-debug.log) 43 | - [package.json](package.json) 44 | - [screen.png](screen.png) 45 | - __scripts__ 46 | - [assert.js](scripts/assert.js) 47 | - [fancom.js](scripts/fancom.js) 48 | - [jshintrc.js](scripts/jshintrc.js) 49 | - [package\-json.js](scripts/package-json.js) 50 | - [precommit\-hook.js](scripts/precommit-hook.js) 51 | - [scripts.js](scripts/scripts.js) 52 | - [tests.js](scripts/tests.js) 53 | - __tests__ 54 | - [michal\-tests.js](tests/michal-tests.js) 55 | ``` 56 | 57 | ## Hidden files & directories 58 | 59 | Please note that this script __skips__ all hidden files and directories (with `.`, like `.git` or `.gitignore`) & 60 | the contents of the `node_modules` directory. 61 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const tree = require('./walker'); 5 | 6 | const emoji = process.argv.includes('--emoji') || process.argv.includes('-e'); 7 | const cleanMarkdown = name => name.replace(/([\\\/_*|-])/g, '\\$1'); 8 | const directoryName = name => { 9 | return '- ' + (emoji ? '📂 ' : '') + '__' + cleanMarkdown(name) + '__\n'; 10 | }; 11 | const filename = (name, path) => { 12 | const link = path.replace(/^\/?(.+?)\/?$/, '$1') + '/' + encodeURIComponent(name); 13 | return '- ' + (emoji ? '📄 ' : '') + '[' + cleanMarkdown(name) + '](' + link.replace(/^\/?(.+?)$/, '$1') + ')\n'; 14 | }; 15 | const addIndentation = i => { 16 | return ' '.repeat(i * 2 + 1); 17 | }; 18 | 19 | const main = () => { 20 | const dirPath = process.cwd(); 21 | const dir = dirPath.split('/').pop(); 22 | 23 | let indentation = 0; 24 | let output = directoryName(dir); 25 | 26 | const parseResult = result => { 27 | indentation++; 28 | Object.keys(result).sort().forEach(key => { 29 | const data = result[key]; 30 | if (typeof data === 'string' && key[0] !== '.') { 31 | const path = data.split('/'); 32 | output += addIndentation(indentation) + filename(path.pop(), path.join('/')); 33 | } else if (typeof data === 'object') { 34 | output += addIndentation(indentation) + directoryName(key); 35 | parseResult(data); 36 | indentation--; 37 | } 38 | }); 39 | }; 40 | 41 | tree(dirPath, (err, result) => { 42 | parseResult(result); 43 | console.log(output); 44 | }); 45 | 46 | }; 47 | 48 | main(); 49 | -------------------------------------------------------------------------------- /walker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'), 4 | Emitter = require('events').EventEmitter, 5 | emitter = new Emitter(), 6 | eventState = require('event-state'), 7 | 8 | dirTree = (path, cb) => { 9 | const buildBranch = (path, branch) => { 10 | 11 | fs.readdir(path, (err, files) => { 12 | 13 | if (err) { 14 | //Errors result in a false value in the tree. 15 | branch[path] = false; 16 | cb(err); 17 | } else { 18 | const newEvents = files.map(file => { 19 | return path + '/' + file; 20 | }); 21 | 22 | if (!state) { 23 | // If this is the first iteration, 24 | // initialize the dynamic state machine (DSM). 25 | state = emitter.required(newEvents, () => { 26 | // Allow for multiple paradigms vis-a-vis callback and promises. 27 | 28 | // resolve the promise with the completed tree.. 29 | cb(null, tree); 30 | }); 31 | } else { 32 | // Add events to the DSM for the directory's children 33 | state.add(newEvents); 34 | } 35 | 36 | // Check each file descriptor to see if it's a directory. 37 | files.forEach(file => { 38 | const filePath = path + '/' + file; 39 | fs.stat(filePath, (err, stats) => { 40 | if (err) { 41 | // Errors result in a false value in the tree 42 | branch[file] = false; 43 | emitter.emit(filePath, true); 44 | } else if (stats.isDirectory() && file[0] !== '.' && file !== 'node_modules') { 45 | 46 | // Directories are object properties on the tree. 47 | branch[file] = {}; 48 | //console.log('cur dir name', file); 49 | // Recurse into the directory. 50 | buildBranch(filePath, branch[file]); 51 | } else { 52 | const fp = filePath.replace(process.cwd(), ''); 53 | //console.log(dir, file); 54 | // If it's not a directory, it's a file. 55 | // Files get a true value in the tree. 56 | branch[file] = fp; 57 | emitter.emit(filePath, true); 58 | } 59 | }); 60 | }); 61 | } 62 | 63 | //Once we've read the directory, we can raise the event for the parent 64 | // directory and let it's children take care of themselves. 65 | emitter.emit(path, true); 66 | }); 67 | }; 68 | 69 | let tree = {}, 70 | state; 71 | 72 | return buildBranch(path, tree); 73 | }; 74 | 75 | emitter.required = eventState; 76 | 77 | module.exports = dirTree; 78 | --------------------------------------------------------------------------------