├── .eslintignore ├── test ├── mocha.opts └── startserver.test.js ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── index.js ├── lib ├── detect.js ├── middleware │ ├── index.js │ ├── logger │ │ └── index.js │ ├── directory │ │ ├── default.tpl │ │ └── index.js │ ├── static │ │ └── index.js │ └── markdown │ │ ├── index.js │ │ ├── default.tpl │ │ └── slide.tpl ├── request.js ├── socket.js ├── file.js ├── mime.js ├── index.js ├── plugin.js └── server.js ├── HISTORY.md ├── bin ├── ss-2.js └── startserver ├── LICENSE ├── package.json ├── .eslintrc ├── README.md └── README.md.html /.eslintignore: -------------------------------------------------------------------------------- 1 | test/ 2 | build/ 3 | coverage/ 4 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require should 2 | --reporter spec 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | .idea/* 5 | Thumbs.db 6 | coverage/ 7 | .project 8 | build/ 9 | plugins/ 10 | .nyc_output/ 11 | *.sw* 12 | *.un~ 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "4.1.2" 5 | script: make travis 6 | after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to startserver 2 | 3 | - Fork the project, make a change, and send a pull request; 4 | - Have a look at code style now before starting; 5 | - Make sure the tests case (`$ make test`) pass before sending a pull request; 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2014 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | module.exports = require('./lib'); 17 | -------------------------------------------------------------------------------- /lib/detect.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2014 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var foc = require('foc'); 17 | var detect = require('detect-port'); 18 | 19 | module.exports = foc(function(port, fn) { 20 | detect(port) 21 | .then(function(_port) { 22 | fn(null, _port); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /lib/middleware/index.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2014 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var middlewares = ['logger', 'markdown', 'static', 'directory']; 17 | 18 | module.exports = function(server) { 19 | middlewares.forEach(function(i) { 20 | server.bundle(require('./' + i)); 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /lib/middleware/logger/index.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2013 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var logx = require('logx'); 17 | 18 | function *logger() { 19 | var log = 'Method: '.gray + this.req.method.red; 20 | log += ' Url: '.gray + this.req.url.red; 21 | logx.info(log); 22 | yield this.next(); 23 | } 24 | 25 | module.exports = logger; 26 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | 1.1.0 / 2015-4-21 2 | ================== 3 | 4 | * Add pdf style 5 | 6 | 1.0.0 / 2015-3-15 7 | ================== 8 | 9 | * Add plugin function in package.json 10 | * Bugfix unknow mime type 11 | 12 | 0.6.40 / 2015-2-11 13 | ================== 14 | 15 | * Support optional index router 16 | * Bugfix markdown render 17 | 18 | 0.6.36 / 2015-1-30 19 | ================== 20 | 21 | * Support respose certification 22 | * Add X-Powered-By to header 23 | 24 | 0.5.26 / 2014-10-18 25 | =================== 26 | 27 | * Some bugfix 28 | * Add update tips. 29 | 30 | 0.5.22 / 2014-10-06 31 | =================== 32 | 33 | * Update tempalte engine 34 | 35 | 0.5.16 / 2014-10-03 36 | =================== 37 | 38 | * Beautify Slider style 39 | 40 | 0.1.0 / 2014-06-03 41 | =================== 42 | 43 | * First Commit 44 | -------------------------------------------------------------------------------- /lib/request.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2014 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var logger = require('logx'); 17 | var request = require('co-request'); 18 | 19 | module.exports = function *(options) { 20 | try { 21 | return yield request(options); 22 | } catch (err) { 23 | logger.warn('Get remote update info failed.'); 24 | if (err.code === 'ETIMEDOUT') { 25 | return null; 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /bin/ss-2.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | const open = require('open'); 6 | const { program, Option } = require('commander'); 7 | const { execSync } = require('child_process'); 8 | 9 | const pkg = require('../package.json'); 10 | 11 | program 12 | .addOption(new Option('-p, --port ', 'port number').default(8000, 'port 8000')) 13 | .parse();; 14 | 15 | if (program.versions) { 16 | console.info('\n ' + pkg.version + '\n'); 17 | process.exit(0); 18 | } 19 | 20 | async function main() { 21 | const cwd = process.cwd(); 22 | const { port } = program.opts(); 23 | const cmd = [ 24 | 'python3', 25 | '-m', 26 | 'http.server', 27 | `${port}` 28 | ].join(' '); 29 | const url = `http://localhost:${port}`; 30 | await open(url); 31 | execSync(cmd, { 32 | stdio: 'inherit', 33 | cwd, 34 | }); 35 | } 36 | 37 | main().then().catch(console.log); 38 | -------------------------------------------------------------------------------- /lib/socket.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * StartServer by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2014 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var WebSocketServer = require('ws').Server; 17 | 18 | var detect = require('./detect'); 19 | 20 | var wss = null; 21 | var ws = null; 22 | var port = null; 23 | 24 | exports.init = function *() { 25 | port = yield detect(8085); 26 | wss = wss || new WebSocketServer({port: port}); 27 | wss.on('connection', function(_ws) { 28 | ws = ws || _ws; 29 | }); 30 | }; 31 | 32 | exports.send = function(data) { 33 | if (ws) { 34 | ws.send(data); 35 | } 36 | }; 37 | 38 | exports.getSocketPort = function() { 39 | return port; 40 | }; 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 xdf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/file.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2013 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var fs = require('fs'); 17 | var foc = require('foc'); 18 | 19 | function isExistedFile(p) { 20 | return fs.existsSync(p) && fs.statSync(p).isFile(); 21 | } 22 | 23 | function isExistedDir(p) { 24 | return fs.existsSync(p) && fs.statSync(p).isDirectory(); 25 | } 26 | 27 | module.exports = fs; 28 | module.exports.exists = foc(fs.exists); 29 | module.exports.readFile = foc(fs.readFile); 30 | module.exports.writeFile = foc(fs.writeFile); 31 | module.exports.stat = foc(fs.stat); 32 | module.exports.readdir = foc(fs.readdir); 33 | module.exports.isExistedFile = isExistedFile; 34 | module.exports.isExistedDir = isExistedDir; 35 | -------------------------------------------------------------------------------- /lib/middleware/directory/default.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 |

Index of <#=path#>

15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <#if (parentDir) {#> 23 | 24 | 25 | 26 | 27 | 28 | <#}#> 29 | 30 | <#for (var i =0; i< list.length; i++) {#> 31 | <#var item = list[i]#> 32 | 33 | 34 | 35 | 36 | 37 | <#}#> 38 | 39 |
NameSizeLast modified
Parent Directory
<#=item.name#><#=item.size#><#=item.lastModified#>
40 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/mime.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2013 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var mime = { 17 | '.css': 'text/css', 18 | '.gif': 'image/gif', 19 | '.html': 'text/html', 20 | '.htm': 'text/html', 21 | '.ico': 'image/x-icon', 22 | '.jpeg': 'image/jpeg', 23 | '.jpg': 'image/jpeg', 24 | '.js': 'text/javascript', 25 | '.json': 'application/json', 26 | '.pdf': 'application/pdf', 27 | '.png': 'image/png', 28 | '.svg': 'image/svg+xml', 29 | '.swf': 'application/x-shockwave-flash', 30 | '.tiff': 'image/tiff', 31 | '.txt': 'text/plain', 32 | '.wav': 'audio/x-wav', 33 | '.wma': 'audio/x-ms-wma', 34 | '.wmv': 'video/x-ms-wmv', 35 | '.xml': 'text/xml', 36 | '.swp': 'text/html', 37 | '.md': 'text/html', 38 | '.markdown': 'text/html', 39 | '.cer': 'application/pkix-cert', 40 | '.crt': 'application/x-x509-ca-cert', 41 | '.doc': 'application/msword', 42 | '.gltf': 'application/json', 43 | '.glb': 'application/octet-stream', 44 | 'unknow': 'application/octet-stream' 45 | }; 46 | 47 | module.exports = mime; 48 | -------------------------------------------------------------------------------- /lib/middleware/static/index.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2013 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 'use strict'; 14 | 15 | var path = require('path'); 16 | var parse = require('url').parse; 17 | 18 | var fs = require('../../file'); 19 | 20 | var root = process.cwd(); 21 | 22 | function *statics() { 23 | var url = parse(this.req.url); 24 | var dir = decodeURIComponent(url.pathname); 25 | var file = path.normalize(path.join(root, dir)); 26 | 27 | if (fs.isExistedFile(file)) { 28 | var data = yield fs.readFile(file); 29 | this.statusCode = 200; 30 | var t = this.mime[path.extname(file)] || this.mime.unknow; 31 | this.setHeader('Content-Type', t); 32 | if (this.options.cors) { 33 | this.setHeader('Access-Control-Allow-Origin', '*'); 34 | } 35 | this.body = data; 36 | yield this.end(); 37 | } else if (fs.isExistedDir(file)) { 38 | yield this.next(); 39 | } else { 40 | this.statusCode = 404; 41 | this.setHeader('Content-Type', this.mime['.txt']); 42 | this.body = '404 Not Found'; 43 | yield this.next(); 44 | } 45 | } 46 | 47 | module.exports = statics; 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startserver", 3 | "version": "2.0.1", 4 | "description": "Yet another http server", 5 | "keywords": [ 6 | "server", 7 | "http", 8 | "dev" 9 | ], 10 | "main": "index.js", 11 | "bin": { 12 | "ss": "./bin/ss-2.js", 13 | "startserver": "./bin/ss-2.js" 14 | }, 15 | "files": [ 16 | "index.js", 17 | "bin/*", 18 | "lib/**/*.js", 19 | "lib/**/*.tpl" 20 | ], 21 | "repository": { 22 | "type": "git", 23 | "url": "git://github.com/xudafeng/startserver.git" 24 | }, 25 | "preferGlobal": true, 26 | "dependencies": { 27 | "co-request": "~0.2.0", 28 | "colorx": "~0.0.5", 29 | "commander": "^12.0.0", 30 | "detect-port": "^1.2.0", 31 | "foc": "~0.1.6", 32 | "highlight.js": "~8.3.0", 33 | "ipv4": "^1.0.4", 34 | "logx": "~0.0.4", 35 | "marked": "~0.3.3", 36 | "microtemplate": "~0.1.2", 37 | "open": "^8.4.2", 38 | "ws": "^1.1.1" 39 | }, 40 | "devDependencies": { 41 | "co-mocha": "^1.1.3", 42 | "command-line-test": "^1.0.3", 43 | "eslint": "~0.24.0", 44 | "git-contributor": "1", 45 | "mocha": "*", 46 | "nyc": "^11.6.0", 47 | "pre-commit": "^1.1.3", 48 | "should": "*", 49 | "should-http": "~0.0.2" 50 | }, 51 | "scripts": { 52 | "test": "nyc --reporter=lcov --reporter=text mocha", 53 | "lint": "eslint .", 54 | "contributor": "git-contributor" 55 | }, 56 | "precommit": [ 57 | "lint" 58 | ], 59 | "homepage": "http://startserver.github.io", 60 | "license": "MIT" 61 | } 62 | -------------------------------------------------------------------------------- /test/startserver.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var CliTest = require('command-line-test'); 5 | 6 | var StartServer = require('../lib/server'); 7 | var logger = require('../lib/middleware/logger'); 8 | var markdown = require('../lib/middleware/markdown'); 9 | var statics = require('../lib/middleware/static'); 10 | var directory = require('../lib/middleware/directory'); 11 | 12 | var pkg = require('../package'); 13 | 14 | describe('/build/server.js', function() { 15 | var server; 16 | 17 | describe('main', function () { 18 | it('should has not error', function() { 19 | var error; 20 | try{ 21 | server = new StartServer(); 22 | } catch (e) { 23 | error = e; 24 | } 25 | (typeof error === 'undefined').should.be.true; 26 | }); 27 | }); 28 | 29 | describe('stack', function() { 30 | it('init function must have this members', function() { 31 | server.stack.should.have.length(0); 32 | }); 33 | }); 34 | 35 | describe('middleware', function() { 36 | it('should be ok', function() { 37 | server 38 | .bundle(logger) 39 | .bundle(markdown) 40 | .bundle(statics) 41 | .bundle(directory); 42 | server.stack.should.have.length(4); 43 | }); 44 | }); 45 | 46 | describe('middleware', function() { 47 | it('command line tool should be ok', function *() { 48 | var cliTest = new CliTest(); 49 | var binFile = path.resolve(pkg.bin['startserver']); 50 | 51 | var res = yield cliTest.execFile(binFile, ['-v'], {}); 52 | res.stdout.should.be.containEql(pkg.version); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /bin/startserver: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* ================================================================ 3 | * startserver by xdf(xudafeng[at]126.com) 4 | * 5 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 6 | * 7 | * ================================================================ 8 | * Copyright 2013 xdf 9 | * 10 | * Licensed under the MIT License 11 | * You may not use this file except in compliance with the License. 12 | * 13 | * ================================================================ */ 14 | 15 | 'use strict'; 16 | 17 | process.title = 'startserver'; 18 | 19 | require('colorx'); 20 | var program = require('commander'); 21 | var logger = require('logx'); 22 | 23 | var pkg = require('../package.json'); 24 | var startserver = require('..'); 25 | 26 | program 27 | .option('-s, --silent', 'start http server without opening browser') 28 | .option('-m, --markdown', 'auto parse and render markdown file') 29 | .option('-f, --pdf', 'render markdown file with pdf style') 30 | .option('-p, --port', 'port to use (8080 default)') 31 | .option('-d, --disable', 'disable default index router') 32 | .option('-c, --cors', 'allow Cross-Origin Resource Sharing(CORS)') 33 | .option('-v, --versions', 'output version infomation') 34 | .usage('[port 8080]'); 35 | 36 | program 37 | .command('plugins') 38 | .description('show plugin list') 39 | .action(function(env, options){ 40 | startserver.plugin(); 41 | }); 42 | 43 | program 44 | .command('generate [file]') 45 | .description('generate static file') 46 | .action(function(env, options){ 47 | if (env) { 48 | startserver.generate(env); 49 | } else { 50 | logger.warn('Arguments Error.'); 51 | program.help(); 52 | } 53 | }); 54 | 55 | program.parse(process.argv); 56 | 57 | if (program.versions) { 58 | console.info('\n ' + pkg.version.gray + '\n'); 59 | process.exit(0); 60 | } 61 | 62 | if (!program.args[0] || parseInt(program.args[0]) || program.port) { 63 | var port = parseInt(program.args[0]) || 8080; 64 | program.pkg = pkg; 65 | startserver.init(port, program); 66 | } 67 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2014 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var foc = require('foc'); 17 | var host = require('ipv4'); 18 | var path = require('path'); 19 | var logger = require('logx'); 20 | 21 | var fs = require('./file'); 22 | var Server = require('./server'); 23 | var plugin = require('./plugin'); 24 | var request = require('./request'); 25 | var middleware = require('./middleware'); 26 | var render = require('./middleware/markdown').render; 27 | 28 | var root = process.cwd(); 29 | 30 | var opt = { 31 | uri: 'http://registry.npmjs.org/startserver/latest', 32 | method: 'get', 33 | timeout: 2000 34 | }; 35 | 36 | function *getUpdateInfo(options) { 37 | var pkg = options.pkg; 38 | var result = yield request(opt); 39 | 40 | if (!result) { 41 | return; 42 | } 43 | 44 | var data = JSON.parse(result.body); 45 | 46 | if (data.version && pkg.version !== data.version) { 47 | logger.warn('Version [' + pkg.version.gray + '] is outdate'.yellow); 48 | var npmi = 'Exec npm i -g startserver@'; 49 | logger.warn(npmi.gray + data.version.gray + ' to update'.yellow); 50 | } 51 | } 52 | 53 | function *init(port, options) { 54 | var server = new Server(options); 55 | 56 | middleware(server); 57 | 58 | yield *getUpdateInfo(options); 59 | 60 | yield *server.listen(port, host); 61 | 62 | var plugins = yield *plugin(true); 63 | 64 | server.on('beforeEnd', function(ctx) { 65 | plugins.forEach(function(i) { 66 | i.call(ctx, ctx.req, ctx.res); 67 | }); 68 | }); 69 | } 70 | 71 | function *generate(file) { 72 | var dir = path.dirname(root).split(path.sep); 73 | var dist = path.resolve(file); 74 | var res = yield *render('slide', dir[dir.length - 1], dist); 75 | yield fs.writeFile(dist + '.html', res); 76 | logger.info('File ' + dist.gray + '.html '.gray + 'created'.blue); 77 | } 78 | 79 | exports.init = foc(init); 80 | exports.plugin = foc(plugin); 81 | exports.generate = foc(generate); 82 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | env: 2 | node: true 3 | 4 | # enable ECMAScript features 5 | ecmaFeatures: 6 | blockBindings: true 7 | templateStrings: true 8 | octalLiterals: true 9 | binaryLiterals: true 10 | generators: true 11 | forOf: true 12 | objectLiteralShorthandProperties: true 13 | 14 | rules: 15 | # Possible Errors 16 | # list: https://github.com/eslint/eslint/tree/master/docs/rules#possible-errors 17 | ## check debugger sentence 18 | no-debugger: 2 19 | ## check duplicate arguments 20 | no-dupe-args: 2 21 | ## check duplicate object keys 22 | no-dupe-keys: 2 23 | ## check duplicate switch-case 24 | no-duplicate-case: 2 25 | ## disallow assignment of exceptional params 26 | no-ex-assign: 2 27 | ## disallow use of reserved words as keys like enum, class 28 | no-reserved-keys: 2 29 | ## disallow unreachable code 30 | no-unreachable: 2 31 | ## require valid typeof compared string like typeof foo === 'strnig' 32 | valid-typeof: 2 33 | 34 | # Best Practices 35 | # list: https://github.com/eslint/eslint/tree/master/docs/rules#best-practices 36 | ## require falls through comment on switch-case 37 | no-fallthrough: 2 38 | 39 | # Stylistic Issues 40 | # list: https://github.com/eslint/eslint/tree/master/docs/rules#stylistic-issues 41 | ## use single quote, we can use double quote when escape chars 42 | quotes: [2, "single", "avoid-escape"] 43 | ## 2 space indentation 44 | indent: [2, 2] 45 | ## add space after comma 46 | comma-spacing: 2 47 | ## put semi-colon 48 | semi: 2 49 | ## require spaces operator like var sum = 1 + 1; 50 | space-infix-ops: 2 51 | ## require spaces return, throw, case 52 | space-return-throw-case: 2 53 | ## no space before function, eg. 'function()' 54 | space-before-function-paren: [2, "never"] 55 | ## require space before blocks, eg 'function() {' 56 | space-before-blocks: [2, "always"] 57 | ## require parens for Constructor 58 | new-parens: 2 59 | ## max 80 length 60 | max-len: [2, 80, 2] 61 | ## max 2 consecutive empty lines 62 | no-multiple-empty-lines: [2, {max: 2}] 63 | ## require newline at end of files 64 | eol-last: 2 65 | ## no trailing spaces 66 | no-trailing-spaces: 2 67 | # Strict Mode 68 | # list: https://github.com/eslint/eslint/tree/master/docs/rules#strict-mode 69 | ## 'use strict' on top 70 | strict: [2, "global"] 71 | 72 | # Variables 73 | # list: https://github.com/eslint/eslint/tree/master/docs/rules#variables 74 | ## disallow use of undefined variables (globals) 75 | no-undef: 2 76 | -------------------------------------------------------------------------------- /lib/middleware/markdown/index.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2013 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var path = require('path'); 17 | var marked = require('marked'); 18 | var parse = require('url').parse; 19 | var highlight = require('highlight.js'); 20 | var microtemplate = require('microtemplate'); 21 | 22 | var fs = require('../../file'); 23 | var socket = require('../../socket'); 24 | 25 | var root = process.cwd(); 26 | 27 | marked.setOptions({ 28 | highlight: function(code) { 29 | return highlight.highlightAuto(code).value; 30 | } 31 | }); 32 | 33 | function *template(type, data) { 34 | var templateTpl = path.join(__dirname, type + '.tpl'); 35 | templateTpl = yield fs.readFile(templateTpl, 'utf8'); 36 | templateTpl = microtemplate.compile(templateTpl); 37 | return templateTpl(data); 38 | } 39 | 40 | function isMarkdownJSFile(url) { 41 | return !!~url.lastIndexOf('.md.js'); 42 | } 43 | 44 | function *render(type, title, file, pdf) { 45 | if (isMarkdownJSFile(file)) { 46 | var reg = /---md-start---([\s\S]*)---md-end---/; 47 | var str = require(file).toString(); 48 | var match = str.match(reg); 49 | file = match[1]; 50 | } else { 51 | file = yield fs.readFile(file, 'utf8'); 52 | } 53 | var res = yield *template(type, { 54 | content: marked(file), 55 | socketPort: socket.getSocketPort(), 56 | title: title, 57 | pdf: pdf || false 58 | }); 59 | return res; 60 | } 61 | 62 | function *markdown() { 63 | if (!this.options.markdown && !this.options.pdf) { 64 | return yield this.next(); 65 | } 66 | var cookie = this.req.headers.cookie; 67 | var url = parse(this.req.url); 68 | var dir = decodeURIComponent(url.pathname); 69 | var file = path.normalize(path.join(root, dir)); 70 | var extname = path.extname(file).toLowerCase(); 71 | var isMarkdownFile = extname === '.md' 72 | || extname === '.markdown' 73 | || isMarkdownJSFile(file); 74 | 75 | if (isMarkdownFile) { 76 | var type = cookie && !!~cookie.indexOf('startserver-slide=true'); 77 | this.statusCode = 200; 78 | this.setHeader('Content-Type', this.mime['.html']); 79 | var t = type ? 'slide' : 'default'; 80 | this.body = yield *render.call(this, t, dir, file, !!this.options.pdf); 81 | yield this.end(); 82 | } else { 83 | yield this.next(); 84 | } 85 | } 86 | 87 | module.exports = markdown; 88 | module.exports.render = render; 89 | module.exports.renderMd = marked; 90 | -------------------------------------------------------------------------------- /lib/plugin.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * StartServer by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2014 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var path = require('path'); 17 | var logger = require('logx'); 18 | 19 | var fs = require('./file'); 20 | 21 | /** 22 | * load plugins 23 | */ 24 | 25 | var plugins = []; 26 | 27 | function *localPlugins(pluginsDir) { 28 | var tempList = []; 29 | var list = yield fs.readdir(pluginsDir); 30 | 31 | list.forEach(function(key) { 32 | var pluginPath = path.join(pluginsDir, key); 33 | 34 | if (fs.isExistedDir(pluginPath)) { 35 | tempList.push(pluginPath); 36 | } 37 | }); 38 | 39 | return tempList; 40 | } 41 | 42 | function *plugin(load) { 43 | var list = []; 44 | var pluginsDir = path.join(__dirname, '..', 'plugins'); 45 | 46 | if (fs.isExistedDir(pluginsDir)) { 47 | list = list.concat(yield *localPlugins(pluginsDir)); 48 | } 49 | 50 | var pkgPath = path.resolve('./package.json'); 51 | 52 | if (fs.isExistedFile(pkgPath)) { 53 | var pkg = require(pkgPath); 54 | 55 | if (pkg.startserver) { 56 | list = list.concat(yield *pkgPlugins(pkg.startserver)); 57 | } 58 | } 59 | 60 | list.forEach(function(pluginPath) { 61 | var pluginPkg = path.join(pluginPath, 'package.json'); 62 | pluginPkg = require(pluginPkg); 63 | var pluginIndex = path.join(pluginPath, pluginPkg.main); 64 | var name = pluginPkg.name; 65 | var version = pluginPkg.version; 66 | 67 | if (load) { 68 | var mod = require(pluginIndex); 69 | 70 | if (typeof mod === 'function') { 71 | plugins.push(mod); 72 | var log = 'Plugin ' + name.gray + '@'.gray; 73 | log += version.gray + ' loaded'.blue; 74 | logger.info(log); 75 | } 76 | } else { 77 | console.log(' ' + name.blue + '@'.blue + version.blue); 78 | } 79 | }); 80 | 81 | if (!load && !list.length) { 82 | logger.warn('not any plugins'); 83 | } 84 | return plugins; 85 | } 86 | 87 | function *pkgPlugins(list) { 88 | var tempList = []; 89 | 90 | list.forEach(function(item) { 91 | Object.keys(item).map(function(key) { 92 | var pluginPath = path.resolve(path.join('node_modules', key)); 93 | 94 | if (fs.isExistedDir(pluginPath)) { 95 | tempList.push(pluginPath); 96 | } 97 | }); 98 | }); 99 | 100 | return tempList; 101 | } 102 | 103 | module.exports = plugin; 104 | -------------------------------------------------------------------------------- /lib/middleware/directory/index.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * startserver by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2013 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var path = require('path'); 17 | var parse = require('url').parse; 18 | var microtemplate = require('microtemplate'); 19 | 20 | var fs = require('../../file'); 21 | 22 | var root = process.cwd(); 23 | var dateReg = /year|month|day|hour|minite|second/g; 24 | 25 | function *render(data) { 26 | var template = path.join(__dirname, 'default.tpl'); 27 | template = yield fs.readFile(template, 'utf8'); 28 | template = microtemplate.compile(template); 29 | return template(data); 30 | } 31 | 32 | function format(date) { 33 | return 'year-month-day hour:minite:second'.replace(dateReg, function(t) { 34 | switch (t) { 35 | case 'year': 36 | return date.getFullYear(); 37 | case 'month': 38 | return date.getMonth(); 39 | case 'day': 40 | return date.getDate(); 41 | case 'hour': 42 | return date.getHours(); 43 | case 'minite': 44 | return date.getMinutes(); 45 | case 'second': 46 | return date.getSeconds(); 47 | } 48 | }); 49 | } 50 | 51 | function *filter(files, p) { 52 | var list = []; 53 | files.forEach(function(i) { 54 | var link = path.join(p, i); 55 | var local = root + link; 56 | var stat = fs.statSync(local); 57 | var size = stat.size; 58 | var mtime = stat.mtime; 59 | 60 | if (fs.isExistedDir(local)) { 61 | i += '/'; 62 | size = '-'; 63 | } 64 | 65 | list.push({ 66 | name: i, 67 | path: link, 68 | lastModified: format(mtime), 69 | size: size 70 | }); 71 | }); 72 | return list; 73 | } 74 | 75 | function *directory() { 76 | var url = this.req.url; 77 | var dir = decodeURIComponent(parse(url).pathname); 78 | var local = path.normalize(path.join(root, dir)); 79 | var parentPath = path.normalize(dir.replace(path.basename(dir), '')); 80 | 81 | if (dir === '/') { 82 | parentPath = ''; 83 | } 84 | 85 | if (!fs.isExistedDir(local)) { 86 | return yield this.next(); 87 | } 88 | 89 | if (local.slice(-1) !== path.sep) { 90 | this.statusCode = 302; 91 | this.setHeader('Location', url + '/'); 92 | return yield this.next(); 93 | } 94 | 95 | // index router 96 | if (!this.options.disable) { 97 | var arr = ['index.html', 'index.htm', 'default.html', 'default.htm']; 98 | 99 | for (var i = 0; i < arr.length; i++) { 100 | var p = local + arr[i]; 101 | 102 | if (fs.isExistedFile(p)) { 103 | this.statusCode = 200; 104 | this.setHeader('Content-Type', this.mime['.html']); 105 | this.body = yield fs.readFile(p); 106 | yield this.next(); 107 | break; 108 | } else { 109 | continue; 110 | } 111 | } 112 | } 113 | 114 | var files = yield fs.readdir(path.resolve(local)); 115 | files = files.sort(); 116 | files = yield *filter(files, dir); 117 | this.statusCode = 200; 118 | this.setHeader('Content-Type', this.mime['.html']); 119 | 120 | this.body = yield *render({ 121 | path: dir, 122 | parentDir: parentPath, 123 | list: files 124 | }); 125 | yield this.next(); 126 | } 127 | 128 | module.exports = directory; 129 | -------------------------------------------------------------------------------- /lib/server.js: -------------------------------------------------------------------------------- 1 | /* ================================================================ 2 | * StartServer by xdf(xudafeng[at]126.com) 3 | * 4 | * first created at : Mon Jun 02 2014 20:15:51 GMT+0800 (CST) 5 | * 6 | * ================================================================ 7 | * Copyright 2014 xdf 8 | * 9 | * Licensed under the MIT License 10 | * You may not use this file except in compliance with the License. 11 | * 12 | * ================================================================ */ 13 | 14 | 'use strict'; 15 | 16 | var os = require('os'); 17 | var foc = require('foc'); 18 | var util = require('util'); 19 | var http = require('http'); 20 | var logger = require('logx'); 21 | var exec = foc(require('child_process').exec); 22 | var EventEmitter = require('events').EventEmitter; 23 | 24 | var mime = require('./mime'); 25 | var detect = require('./detect'); 26 | 27 | var platform = process.platform; 28 | var linuxShell = platform === 'linux' ? 'xdg-open' : 'open'; 29 | var openShell = platform === 'win32' ? 'start' : linuxShell; 30 | 31 | /** 32 | * bind 33 | */ 34 | function bind() { 35 | var that = this; 36 | that.on('start', function(ctx) { 37 | that.count = -1; 38 | that.emit('next', ctx); 39 | }).on('next', function(ctx) { 40 | try { 41 | that.count++; 42 | if (that.stack[that.count]) { 43 | that.stack[that.count].handle.call(ctx); 44 | } else { 45 | that.emit('end', ctx); 46 | } 47 | } catch(e) { 48 | ctx.res.end(e.toString()); 49 | } 50 | }).on('end', function(ctx) { 51 | that.emit('beforeEnd', ctx); 52 | ctx.res.statusCode = ctx.statusCode || 500; 53 | 54 | ctx.headers.forEach(function(header) { 55 | Object.keys(header).forEach(function(k) { 56 | ctx.res.setHeader(k, header[k]); 57 | }); 58 | }); 59 | var powerByString = that.options.pkg.name + '/' + that.options.pkg.version; 60 | powerByString += ' node' + '/' + process.version; 61 | powerByString += ' (' + os.platform() + ')'; 62 | ctx.res.setHeader('Server', powerByString); 63 | ctx.res.write(ctx.body || '', 'utf8'); 64 | ctx.res.end(); 65 | }); 66 | } 67 | 68 | /** 69 | * constructor function 70 | */ 71 | function Server(options) { 72 | var that = this; 73 | that.options = options || {}; 74 | that.stack = []; 75 | bind.call(that); 76 | } 77 | 78 | /** 79 | * server listen 80 | */ 81 | function *listen(_port, host) { 82 | var that = this; 83 | 84 | var port = yield detect(_port); 85 | 86 | if (port !== _port) { 87 | logger.info(`port ${_port} was occupied`); 88 | } 89 | 90 | http.createServer(function(req, res) { 91 | // malloc ctx 92 | var ctx = { 93 | mime: mime, 94 | req: req, 95 | res: res, 96 | options: that.options, 97 | headers: [], 98 | next: foc(function next() { 99 | that.emit('next', ctx); 100 | }), 101 | end: foc(function() { 102 | that.emit('end', ctx); 103 | }), 104 | body: null, 105 | statusCode: null, 106 | type: null, 107 | setHeader: function(k, v) { 108 | var obj = {}; 109 | obj[k] = v; 110 | this.headers.push(obj); 111 | } 112 | }; 113 | 114 | that.emit('start', ctx); 115 | }).listen(port, '0.0.0.0'); 116 | 117 | var url = 'http://' + host + ':' + port; 118 | 119 | /** 120 | * open browser when options.silent true 121 | */ 122 | if (!this.options.silent) { 123 | yield exec(openShell + ' ' + url); 124 | } 125 | 126 | logger.info('Running at '.blue + url.blue); 127 | } 128 | 129 | /** 130 | * bundle function 131 | */ 132 | function bundle(middleware) { 133 | var that = this; 134 | var name = middleware.prototype.constructor.name; 135 | that.stack.push({ 136 | name: name || 'anonymous', 137 | handle: foc(middleware) 138 | }); 139 | return that; 140 | } 141 | 142 | // add event target 143 | util.inherits(Server, EventEmitter); 144 | 145 | //augment function 146 | Server.prototype.listen = listen; 147 | Server.prototype.bundle = bundle; 148 | 149 | module.exports = Server; 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # startserver 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![build status][travis-image]][travis-url] 5 | [![Test coverage][coveralls-image]][coveralls-url] 6 | [![node version][node-image]][node-url] 7 | [![npm download][download-image]][download-url] 8 | 9 | [npm-image]: https://img.shields.io/npm/v/startserver.svg?style=flat-square 10 | [npm-url]: https://npmjs.org/package/startserver 11 | [travis-image]: https://img.shields.io/travis/xudafeng/startserver.svg?style=flat-square 12 | [travis-url]: https://travis-ci.org/xudafeng/startserver 13 | [coveralls-image]: https://img.shields.io/coveralls/xudafeng/startserver.svg?style=flat-square 14 | [coveralls-url]: https://coveralls.io/r/xudafeng/startserver?branch=master 15 | [node-image]: https://img.shields.io/badge/node.js-%3E=_4-green.svg?style=flat-square 16 | [node-url]: http://nodejs.org/download/ 17 | [download-image]: https://img.shields.io/npm/dm/startserver.svg?style=flat-square 18 | [download-url]: https://npmjs.org/package/startserver 19 | 20 | > Yet another http server. 21 | 22 | ## Installation 23 | 24 | ```shell 25 | $ npm i startserver -g 26 | ``` 27 | 28 | ## Quick Start 29 | 30 | ```shell 31 | $ startserver 32 | ``` 33 | 34 | It also can be use it like this: 35 | 36 | ```shell 37 | $ startserver -p 6789 38 | ``` 39 | 40 | There are more shorter alias for it: 41 | 42 | ```shell 43 | $ ss 44 | ``` 45 | 46 | ## Features 47 | 48 | ### Server Everywhere 49 | 50 | Run it at every directory under the root. 51 | 52 | * Automatic detection of unoccupied port. 53 | 54 | ### Slider Revolution 55 | 56 | Generate slider with `README.md` file or other markdown file. 57 | 58 | * Suppor a inverse color style. 59 | * Double click for temporary modifications, again to restore. 60 | * Normal to read makedown. 61 | * Highlight for your code block. 62 | * Thumbnail mode provided. 63 | * Watching markdown file. 64 | 65 | ### Others 66 | 67 | * Generator support and compatible runtime. 68 | * Original javascript source code. 69 | 70 | ## Commands 71 | 72 | #### plugins 73 | 74 | show plugin list 75 | 76 | #### generate 77 | 78 | ```shell 79 | $ startsever generate README.md 80 | ``` 81 | 82 | generate static slide file to markdown [[sample]](https://rawgit.com/xudafeng/startserver/master/README.md.html) 83 | 84 | ## Plugins 85 | 86 | ![logo](https://avatars3.githubusercontent.com/u/9607546?v=3&s=100) 87 | 88 | [plugins list](//github.com/startserver) 89 | 90 | ## Options 91 | 92 | #### -s, --silent 93 | 94 | start http server without opening browser 95 | 96 | #### -m, --markdown 97 | 98 | auto parse and render markdown file 99 | 100 | #### -f, --pdf 101 | 102 | render markdown file with pdf style 103 | 104 | #### -p, --port 105 | 106 | port to use (8080 default) 107 | 108 | #### -d, --disable 109 | 110 | disable default index router 111 | 112 | #### -v, --version 113 | 114 | output version infomation 115 | 116 | ## Help 117 | 118 | ```shell 119 | $ startserver -h 120 | ``` 121 | 122 | 123 | 124 | ## Contributors 125 | 126 | |[
xudafeng](https://github.com/xudafeng)
|[
karoo](https://github.com/karoo)
|[
06wj](https://github.com/06wj)
|[
ottomao](https://github.com/ottomao)
|[
helldance](https://github.com/helldance)
|[
zhuyali](https://github.com/zhuyali)
| 127 | | :---: | :---: | :---: | :---: | :---: | :---: | 128 | [
meowtec](https://github.com/meowtec)
129 | 130 | This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Mon Jul 05 2021 13:57:01 GMT+0800`. 131 | 132 | 133 | 134 | ## License 135 | 136 | The MIT License (MIT) 137 | 138 | Copyright (c) 2013 xdf 139 | -------------------------------------------------------------------------------- /lib/middleware/markdown/default.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <#=title#> 7 | 16 | 17 | 18 | 19 | 20 |
21 | <#=content#> 22 |
23 |
24 | 25 | 26 | 27 | 28 |
29 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /lib/middleware/markdown/slide.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <#=title#> 7 | 105 | 106 | 107 | 108 | 109 |
110 | <#=content#> 111 |
112 |
113 | 114 | 115 | 116 | 117 | 118 | 119 |
120 | 121 | 122 | 126 | 324 | 325 | 326 | -------------------------------------------------------------------------------- /README.md.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | server 7 | 99 | 100 | 101 | 102 | 103 |
104 |

startserver

105 |

NPM version 106 | build status 107 | Test coverage 108 | node version 109 | npm download

110 |
111 |

Yet another http server.

112 |
113 |

Installation

114 |

Node requirement

115 |

>= 0.10.x

116 |

Install from npm

117 |
$ npm i startserver -g
118 | 
119 |

Use it for generator support, add this alias to your .bash_profile:

120 |
alias node='node --harmony'
121 | 

Or you'd better use io.js as an alternative.

122 |

Quick Start

123 |
$ startserver
124 | 
125 |

It also can be use it like this:

126 |
$ startserver 6789
127 | 
128 |

There are more shorter alias for it:

129 |
$ ss
130 | 
131 |

Features

132 |

Server Everywhere

133 |

Run it at every directory under the root.

134 | 137 |

Slider Revolution

138 |

Generate slider with README.md file or other markdown file.

139 | 146 |

Others

147 | 151 |

Commands

152 |

plugins

153 |

show plugin list

154 |

generate

155 |
$ startsever generate README.md
156 | 
157 |

generate static slide file to markdown [sample]

158 |

Plugins

159 |

logo

160 |

plugins list

161 |

Options

162 |

-s, --silent

163 |

start http server without opening browser

164 |

-m, --markdown

165 |

auto parse and render markdown file

166 |

-f, --pdf

167 |

render markdown file with pdf style

168 |

-p, --port

169 |

port to use (8080 default)

170 |

-d, --disable

171 |

disable default index router

172 |

-v, --version

173 |

output version infomation

174 |

Help

175 |
$ startserver -h
176 | 
177 |

License

178 |

The MIT License (MIT)

179 |

Copyright (c) 2013 xdf

180 | 181 |
182 |
183 | 184 | 185 | 186 | 187 | 188 | 189 |
190 | 191 | 192 | 196 | 394 | 395 | 396 | --------------------------------------------------------------------------------