├── .gitignore ├── ss.png ├── example ├── outer │ └── msg.png ├── public │ ├── msg.png │ ├── msg.js │ ├── msg.css │ ├── index.html │ └── dir │ │ └── default.html ├── .eslintrc.json └── test.js ├── .eslintrc.json ├── lib ├── .eslintrc.json └── node-static-alias.js ├── package.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | -------------------------------------------------------------------------------- /ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anseki/node-static-alias/HEAD/ss.png -------------------------------------------------------------------------------- /example/outer/msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anseki/node-static-alias/HEAD/example/outer/msg.png -------------------------------------------------------------------------------- /example/public/msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anseki/node-static-alias/HEAD/example/public/msg.png -------------------------------------------------------------------------------- /example/public/msg.js: -------------------------------------------------------------------------------- 1 | 2 | document.getElementById('js-msg').innerHTML = 'Hello world! from [public/msg.js]'; 3 | 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "../../_common/files/eslintrc.json", 4 | "env": {"node": true} 5 | } 6 | -------------------------------------------------------------------------------- /example/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-var": "off", 4 | "prefer-arrow-callback": "off", 5 | "object-shorthand": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-var": "off", 4 | "prefer-arrow-callback": "off", 5 | "no-underscore-dangle": [2, {"allow": ["_log"]}] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /example/public/msg.css: -------------------------------------------------------------------------------- 1 | 2 | #css-msg { 3 | font-size: 0; 4 | } 5 | 6 | #css-msg:before { 7 | content: 'Hello world! from [public/msg.css]'; 8 | font-size: 16px; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-static-alias", 3 | "version": "1.1.2", 4 | "title": "node-static-alias", 5 | "description": "Serve static file which is not requested file. (e.g. `file.min.js` is requested, serve `file.js`)", 6 | "keywords": [ 7 | "http", 8 | "httpd", 9 | "test", 10 | "static", 11 | "file", 12 | "server", 13 | "alias", 14 | "mod_rewrite", 15 | "directoryindex", 16 | "min", 17 | "log" 18 | ], 19 | "main": "./lib/node-static-alias.js", 20 | "files": [ 21 | "lib/!(.eslintrc.json)", 22 | "example/!(.eslintrc.json)", 23 | "ss.png" 24 | ], 25 | "engines": { 26 | "node": ">= 0.8.0" 27 | }, 28 | "dependencies": { 29 | "node-static": "~0.7.2" 30 | }, 31 | "homepage": "https://github.com/anseki/node-static-alias", 32 | "repository": { 33 | "type": "git", 34 | "url": "git://github.com/anseki/node-static-alias.git" 35 | }, 36 | "bugs": "https://github.com/anseki/node-static-alias/issues", 37 | "license": "MIT", 38 | "author": { 39 | "name": "anseki", 40 | "url": "https://github.com/anseki" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 anseki 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. -------------------------------------------------------------------------------- /example/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var staticAlias = require('../lib/node-static-alias'); 4 | 5 | // var log4js = require('log4js'); 6 | // var logger = log4js.getLogger('node-static-alias'); 7 | // logger.setLevel(log4js.levels.INFO); 8 | 9 | process.chdir(__dirname); 10 | 11 | var fileServer = new staticAlias.Server('./public', { 12 | alias: [ 13 | 14 | // file.min.ext --> file.ext 15 | { 16 | match: /\.min\.(?:js|css)$/, 17 | serve: function(params) { 18 | return params.absDir + '/' + params.basename.replace(/\.min$/, '.') + params.suffix; 19 | } 20 | }, 21 | 22 | // outside of document-root 23 | { 24 | match: ['suffix=png', 'suffix=jpg'], 25 | serve: '../outer/<% fileName %>', 26 | allowOutside: true 27 | }, 28 | 29 | // directoryIndex 30 | { 31 | match: /\/$/, 32 | serve: '<% absPath %>/default.html' 33 | } 34 | 35 | ], 36 | // logger: logger 37 | logger: console 38 | }); 39 | 40 | console.log('START (http://127.0.0.1:8080/)'); 41 | 42 | require('http').createServer(function(request, response) { 43 | request.addListener('end', function() { 44 | fileServer.serve(request, response); 45 | }).resume(); 46 | }).listen(8080); 47 | 48 | -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PATH: public/index.html 5 | 6 | 22 | 23 | 24 | 25 |

This file: public/index.html (Compare this to the browser's path)

26 | 27 |
28 | Message from CSS (Request: [public/msg.min.css]) (Compare this to the path in message) 29 |
NO message.
30 |
31 | 32 |
33 | Message from JS (Request: [public/msg.min.js]) (Compare this to the path in message) 34 |
NO message.
35 | 36 |
37 | 38 |
39 | Message from IMG (Request: [public/msg.png]) (Compare this to the path in message) 40 |
NO message.
41 |
42 | 43 |

To [dir/]

44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /example/public/dir/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PATH: public/dir/default.html 5 | 6 | 22 | 23 | 24 | 25 |

This file: public/dir/default.html (Compare this to the browser's path)

26 | 27 |
28 | Message from CSS (Request: [public/msg.min.css]) (Compare this to the path in message) 29 |
NO message.
30 |
31 | 32 |
33 | Message from JS (Request: [public/msg.min.js]) (Compare this to the path in message) 34 |
NO message.
35 | 36 |
37 | 38 |
39 | Message from IMG (Request: [public/msg.png]) (Compare this to the path in message) 40 |
NO message.
41 |
42 | 43 |

To [../]

44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lib/node-static-alias.js: -------------------------------------------------------------------------------- 1 | /* 2 | * node-static-alias 3 | * https://github.com/anseki/node-static-alias 4 | * 5 | * Copyright (c) 2018 anseki 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | var staticSuper = require('node-static'), 12 | fs = require('fs'), 13 | events = require('events'), 14 | path = require('path'), 15 | url = require('url'), 16 | querystring = require('querystring'), 17 | platform = process.platform; 18 | 19 | function Server() { 20 | var that = this; 21 | staticSuper.Server.apply(that, arguments); // Super class constructor 22 | 23 | if (that.options.alias) { // To Array 24 | if (!Array.isArray(that.options.alias)) { 25 | that.options.alias = [that.options.alias]; 26 | } 27 | that.options.alias.forEach(function(alias) { 28 | alias.match = !alias.match ? [] : 29 | !Array.isArray(alias.match) ? [alias.match] : 30 | alias.match; 31 | alias.serve = !alias.serve ? ['<% absPath %>'] : 32 | !Array.isArray(alias.serve) ? [alias.serve] : 33 | alias.serve; 34 | }); 35 | } 36 | 37 | if (that.options.logger) { 38 | ['info', 'log'].some(function(methodName) { 39 | if (typeof that.options.logger[methodName] === 'function') { 40 | that._log = function() { 41 | that.options.logger[methodName].apply(that.options.logger, arguments); 42 | }; 43 | return true; 44 | } 45 | return false; 46 | }); 47 | } 48 | that._log = that._log || function() {}; 49 | } 50 | 51 | // util.inherits() 52 | Server.prototype = Object.create(staticSuper.Server.prototype); 53 | Server.prototype.constructor = Server; 54 | 55 | Server.prototype.servePath = function(pathname, status, headers, req, res, finish) { 56 | var that = this, 57 | servePath = that.parsePath(pathname, req, res), 58 | promise = new events.EventEmitter(); 59 | 60 | if (servePath) { 61 | fs.stat(servePath, function(e, stat) { 62 | if (e) { 63 | finish(404, {}); 64 | } else if (stat.isFile()) { // Stream a single file. 65 | that.respond(null, status, headers, [servePath], stat, req, res, finish); 66 | } else if (stat.isDirectory()) { // Stream a directory of files. 67 | that.serveDir(servePath, req, res, finish); 68 | } else { 69 | finish(400, {}); 70 | } 71 | }); 72 | } else { 73 | // Forbidden 74 | finish(403, {}); 75 | } 76 | return promise; 77 | }; 78 | 79 | Server.prototype.parsePath = function(pathname, req, res) { 80 | var that = this, 81 | params = {absPath: that.resolve(pathname)}, 82 | allowOutside = false, 83 | key, query, servePath; 84 | 85 | if (!that.options.alias) { return params.absPath; } 86 | 87 | if (req.headers) { 88 | // The strange HTTP header like a 'reqPath' may come from client. But, ignore it. 89 | for (key in req.headers) { 90 | if (key !== 'absPath') { params[key] = req.headers[key] + ''; } 91 | } 92 | } 93 | 94 | params.reqPath = pathname; 95 | params.reqDir = path.dirname(params.reqPath); 96 | params.absDir = path.dirname(params.absPath); 97 | params.fileName = path.basename(params.reqPath); 98 | // params.suffix = path.extname(params.reqPath).replace(/^\./, ''); 99 | params.basename = params.fileName.replace(/^([^.].*)\.([^.]*)$/, 100 | function(s, basename, suffix) { 101 | params.suffix = suffix; 102 | return basename; 103 | }); 104 | params.suffix = params.suffix != null ? params.suffix : ''; 105 | 106 | if ((params.reqUrl = req.url) && (params.reqQuery = url.parse(params.reqUrl).query)) { 107 | query = querystring.parse(params.reqQuery); 108 | Object.keys(query).forEach(function(key) { 109 | if (typeof query[key] === 'string') { 110 | params['query_' + key] = query[key]; 111 | } else { // it should be Array 112 | query[key].forEach(function(value, i) { 113 | params['query_' + key + '[' + i + ']'] = value + ''; 114 | }); 115 | } 116 | }); 117 | } 118 | 119 | that._log('(%s) Requested: "%s"', params.reqPath, params.absPath); 120 | 121 | function inRoot(servePath) { 122 | return (platform === 'win32' 123 | ? servePath.toLowerCase().indexOf(that.root.toLowerCase()) : // Windows 124 | servePath.indexOf(that.root) // Others 125 | ) === 0 ? servePath : false; 126 | } 127 | 128 | function parseTemplate(template) { 129 | return template.replace(/<%\s*(.+?)\s*%>/g, function(s, key) { 130 | return params[key] != null ? params[key] : ''; 131 | }); 132 | } 133 | 134 | return that.options.alias.some(function(alias, iAlias) { 135 | var iMatch; 136 | if (!alias.match.some(function(match, i) { 137 | var key, value; 138 | if (typeof match === 'string') { 139 | value = match.replace(/^(?:(.*?)=)?(.*)$/, function(s, pKey, pValue) { 140 | key = pKey; 141 | return pValue; 142 | }); 143 | if (params[key || 'reqPath'] === value) { 144 | iMatch = i; 145 | return true; 146 | } 147 | return false; 148 | } 149 | if (typeof match === 'object' && match instanceof RegExp && match.test(params.reqPath) || 150 | typeof match === 'function' && match.call(that, params, req, res)) { 151 | iMatch = i; 152 | return true; 153 | } 154 | return false; 155 | })) { 156 | return false; 157 | } 158 | // matched 159 | return alias.serve.some(function(serve, iServe) { 160 | var absPath = // Not that.resolve() because it's not uri. 161 | typeof serve === 'string' ? path.resolve(that.root, parseTemplate(serve)) : 162 | typeof serve === 'function' ? path.resolve(that.root, serve.call(that, params, req, res)) : 163 | params.absPath; 164 | if (alias.force || fs.existsSync(absPath)) { 165 | servePath = absPath; 166 | allowOutside = alias.allowOutside; 167 | that._log('(%s) For Serve: "%s" alias[%d] match[%d] serve[%d]', 168 | params.reqPath, servePath, iAlias, iMatch, iServe); 169 | return true; 170 | } 171 | return false; 172 | }); 173 | }) ? (allowOutside ? servePath : inRoot(servePath)) : 174 | inRoot(params.absPath); 175 | }; 176 | 177 | exports.Server = Server; 178 | exports.mime = staticSuper.mime; 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-static-alias 2 | 3 | [![npm](https://img.shields.io/npm/v/node-static-alias.svg)](https://www.npmjs.com/package/node-static-alias) [![GitHub issues](https://img.shields.io/github/issues/anseki/node-static-alias.svg)](https://github.com/anseki/node-static-alias/issues) [![David](https://img.shields.io/david/anseki/node-static-alias.svg)](package.json) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 4 | 5 | Serve static file which is not requested file. (e.g. `file.min.js` is requested, serve `file.js`) 6 | node-static-alias wraps (inherits) the useful module [node-static](https://github.com/cloudhead/node-static/), and this adds the [`alias`](#alias) option to that. 7 | This works like the [Alias](http://httpd.apache.org/docs/2.4/mod/mod_alias.html) mapping or the [mod_rewrite 8 | ](http://httpd.apache.org/docs/2.4/mod/mod_rewrite.html) of Apache. It looks like [DirectoryIndex](http://httpd.apache.org/docs/2.4/mod/mod_dir.html#directoryindex) too. And this can check the file exists or not. 9 | 10 | + Serve `file.js` instead of `file.min.js` which is not made yet, in the test phase. 11 | + Serve the outside files of the document-root which are shared by multiple web sites in one machine. 12 | + Serve the default page which is not `index.html` when *`/` is requested. 13 | 14 | ## Synopsis 15 | 16 | ```js 17 | var staticAlias = require('node-static-alias'); 18 | 19 | // Document-Root: './public' directory 20 | var fileServer = new staticAlias.Server('./public', { 21 | alias: { 22 | match: '/path/to/file.min.js', 23 | serve: '/path/to/file.js' 24 | } 25 | }); 26 | 27 | require('http').createServer(function(request, response) { 28 | request.addListener('end', function() { 29 | fileServer.serve(request, response); 30 | }).resume(); 31 | }).listen(8080); 32 | ``` 33 | 34 | ## Usage 35 | 36 | The [`alias`](#alias) option is added to the node-static via using `require('node-static-alias')` instead of `require('node-static')`. See [node-static](https://github.com/cloudhead/node-static/) to use it. 37 | 38 | ### `alias` 39 | 40 | The `alias` included in the constructor options is an Alias-Rule Object, or an array which includes multiple Alias-Rule Objects. 41 | 42 | ```js 43 | alias: { 44 | match: /file\.min\.(?:js|css)$/, 45 | serve: '/path/to/file.<% suffix %>' 46 | } 47 | ``` 48 | 49 | Or 50 | 51 | ```js 52 | alias: [ 53 | { 54 | match: '/path/to/file.min.js', 55 | serve: '/path/to/file.js' 56 | }, 57 | { 58 | match: 'suffix=png', 59 | serve: '../outer/<% fileName %>', 60 | allowOutside: true 61 | } 62 | ] 63 | ``` 64 | 65 | The Alias-Rule Object can have following properties. 66 | 67 | ### `match` 68 | 69 | **Type:** string, RegExp, function or Array 70 | 71 | Specify one of below or an Array which includes multiple things of those. 72 | If one or more things match, the [`serve`](#serve) is parsed. If anything doesn't match, it goes to next an Alias-Rule. If all Alias-Rules don't match, it tries to serve the requested file. 73 | 74 | + **string:** 75 | If the requested path is equal to this string, it's matched. 76 | Or, this can be `parameter=value` format (e.g. `suffix=png`). See [Parameters](#parameters). If the `value` is equal to the specified parameter, it's matched. 77 | 78 | ```js 79 | alias: [ 80 | { 81 | match: '/path/to/file.min.js', 82 | serve: '/path/to/file.js' 83 | }, 84 | // Image files are not made yet. 85 | { 86 | match: 'suffix=png', 87 | serve: '/path/to/dummy.png' 88 | } 89 | ] 90 | ``` 91 | 92 | + **RegExp:** 93 | The RegExp which tests the requested path. 94 | 95 | ```js 96 | // These image files are not made yet. 97 | alias: { 98 | match: /\/(?:foo|bar)\.png$/, 99 | serve: '/path/to/dummy.png' 100 | } 101 | ``` 102 | 103 | + **function:** 104 | The function which returns `true` or `false`. 105 | The Object which has parameters is passed to this function. See [Parameters](#parameters). Also, current `request` instance and `response` instance are passed. 106 | In the function, `this` refers to own instance. 107 | 108 | ```js 109 | // Kick direct access from outside web site. 110 | alias: { 111 | match: function(params, request, response) { 112 | console.log(request.url + ' was requested, in the path ' + this.root); 113 | return params.suffix === 'jpg' && 114 | params.referer.indexOf('http://mysite.com/') !== 0; 115 | }, 116 | serve: '/path/to/denial.jpg' 117 | } 118 | ``` 119 | 120 | ### `serve` 121 | 122 | **Type:** string, function or Array 123 | 124 | Specify one of below or an Array which includes multiple things of those. 125 | By default, the first file which exists is chosen to try to serve. See [force](#force). If anything doesn't exist, it goes to next an Alias-Rule. If all files of Alias-Rules don't exist, it tries to serve the requested file. 126 | 127 | + **string:** 128 | The absolute path or relative path from the document-root of the file to serve. 129 | This can include parameters like `<% parameter %>`. See [Parameters](#parameters). 130 | 131 | ```js 132 | // directoryIndex isn't index.html 133 | alias: { 134 | match: /\/$/, 135 | serve: '<% absPath %>/default.html' 136 | } 137 | ``` 138 | 139 | *NOTE:* If the first character of this string is `/` (it might be parameter), this string is absolute path. This `/` doesn't point the document-root. It is the root of the local filesystem. If you want the relative path from the document-root, don't specify leading `/`, or add `.` to left of leading `/`. 140 | 141 | + **function:** 142 | The function which returns the absolute path or relative path from the document-root of the file to serve. 143 | The Object which has parameters is passed to this function. See [Parameters](#parameters). Also, current `request` instance and `response` instance are passed. 144 | In the function, `this` refers to own instance. 145 | 146 | ```js 147 | // Minified files are not made yet. 148 | alias: { 149 | match: /\.min\.(?:js|css)$/, 150 | serve: function(params, request, response) { 151 | response.setHeader('X-serve-from', this.root); 152 | return params.absDir + '/' + 153 | params.basename.replace(/\.min$/, '.') + params.suffix; 154 | } 155 | } 156 | ``` 157 | 158 | ```js 159 | // Compile unwatched SASS now. 160 | alias: { 161 | match: 'suffix=css', 162 | serve: function(params) { 163 | require('exec-sync')('sass ' + 164 | params.absDir + '/' + params.basename + '.scss:' + params.absPath); 165 | return params.absPath; 166 | } 167 | } 168 | ``` 169 | 170 | ### `force` 171 | 172 | **Type:** boolean 173 | 174 | If `true` is specified, the first file in the [`serve`](#serve) is chosen to try to serve without checking it's existing or not. And if it doesn't exist, a 404 error occurs. Default is `false`. 175 | This is used to prevent another file from being chosen unintentionally. 176 | 177 | ### `allowOutside` 178 | 179 | If `true` is specified, serving the outside files of the document-root is allowed. Default is `false`. 180 | 181 | ```js 182 | // Shared files. 183 | alias: { 184 | match: /^\/common_lib/, 185 | serve: '/path/to/lib/<% fileName %>', 186 | allowOutside: true 187 | } 188 | ``` 189 | 190 | *NOTE:* If you specify `true` in the public server, you should specify the absolute path to the [`serve`](#serve). Otherwise the user might access to the file that must be hidden from them. 191 | 192 | ## Parameters 193 | 194 | The string `parameter=value` can be specified to the [`match`](#match), and the string `<% parameter %>` can be specified to the [`serve`](#serve). 195 | And also, the Object which has parameters is passed to function which specified to the [`match`](#match) and the [`serve`](#serve). 196 | These parameters are below. 197 | 198 | + `reqUrl` 199 | The URL which is requested by the user. e.g. `/path/to/file.ext?key1=value1&key2=value2` 200 | + `reqPath` 201 | The path which is requested by the user. e.g. `/path/to/file.ext` 202 | This might be a directory. e.g. `/` 203 | + `reqDir` 204 | The path to a directory which is part of `reqPath`. e.g. `/path/to` 205 | + `absPath` 206 | The absolute path to a requested file. e.g. `/var/www/public/path/to/file.ext` 207 | + `absDir` 208 | The absolute path to a directory which is part of `absPath`. e.g. `/var/www/public/path/to` 209 | + `fileName` 210 | The file name of a requested file. e.g. `file.ext` 211 | This might be a directory name e.g. `to` 212 | If the document-root is requested, this is empty string. 213 | + `basename` 214 | The part of the file name except file-suffix. (`.` isn't included) e.g. `file` 215 | + `suffix` 216 | The part of the file name which is extracted file-suffix. (`.` isn't included) e.g. `ext` 217 | + `reqQuery` 218 | The URL query string which is part of `reqUrl`. e.g. `key1=value1&key2=value2` 219 | + Parsed query string 220 | Pairs of a key beginning with a `query_` and a value. e.g. `query_key1`: `value1`, `query_key2`: `value2` from `reqQuery` above 221 | An array is extracted and each key is given `[INDEX]`. e.g. `query_key[0]`: `value1`, `query_key[1]`: `value2` from `key=value1&key=value2` 222 | + Request Headers 223 | The HTTP Request Headers from the client. These are lower-cased. e.g. `referer`, `user-agent`, etc. 224 | 225 | ## Logging 226 | 227 | The `logger` included in constructor options is a Logger instance of the standard Logging Library (e.g. [log4js](https://github.com/nomiddlename/log4js-node)) which has `info` method or `log` method. 228 | 229 | ```js 230 | var log4js = require('log4js'); 231 | var logger = log4js.getLogger('node-static-alias'); 232 | logger.setLevel(log4js.levels.INFO); 233 | 234 | var fileServer = new staticAlias.Server('./public' { 235 | alias: { ... }, 236 | logger: logger 237 | }); 238 | ``` 239 | 240 | You can specify a simple Object which has `info` method or `log` method (e.g. `console` or `util`). 241 | Most easy: 242 | 243 | ```js 244 | var fileServer = new staticAlias.Server('./public' { 245 | alias: { ... }, 246 | logger: console 247 | //logger: require('util') // Add timestamp 248 | }); 249 | ``` 250 | 251 | Add project name: (But, you probably use your favorite library.) 252 | 253 | ```js 254 | var fileServer = new staticAlias.Server('./public' { 255 | alias: { ... }, 256 | logger: {log: function() { 257 | var util = require('util'); 258 | console.log('[node-static-alias] ' + util.format.apply(util, arguments)); 259 | }} 260 | }); 261 | ``` 262 | 263 | Log message example: 264 | 265 | ```console 266 | (/) Requested: "/var/public" 267 | (/file.min.css) Requested: "/var/public/file.min.css" 268 | (/file.min.css) For Serve: "/var/public/file.css" alias[3] match[1] serve[0] 269 | (/file.min.js) Requested: "/var/public/file.min.js" 270 | (/file.min.js) For Serve: "/var/public/file.js" alias[2] match[0] serve[1] 271 | ``` 272 | 273 | The `(path)` is the path which is requested by the user. The `[number]` means an index of an Array. 274 | 275 | ## Files list in the Directory 276 | 277 | This example generates a list of files and directories in requested directory when the user accessed the directory. This works like the [mod_autoindex](http://httpd.apache.org/docs/2.4/mod/mod_autoindex.html) of Apache. 278 | 279 | That looks like: 280 | ![ss](ss.png) 281 | 282 | The [statsFilelist](https://www.npmjs.com/package/stats-filelist) is required. 283 | 284 | ``` 285 | npm install stats-filelist 286 | ``` 287 | 288 | Load that and some core modules. 289 | 290 | ```js 291 | var filelist = require('stats-filelist'), 292 | path = require('path'), 293 | fs = require('fs'); 294 | ``` 295 | 296 | Specify for `alias`: 297 | 298 | ```js 299 | alias: { 300 | match: function(params) { 301 | try { 302 | return fs.statSync(params.absPath).isDirectory(); 303 | } catch (error) { 304 | return false; // Ignore "Not found" etc. 305 | } 306 | }, 307 | serve: function(params) { 308 | var indexPath = path.join(params.absPath, '.index.html'); 309 | fs.writeFileSync(indexPath, 310 | ''); 317 | return indexPath; 318 | } 319 | } 320 | ``` 321 | --------------------------------------------------------------------------------