You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
4.8 KiB

11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
12 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. #!/usr/bin/env node
  2. "use strict";
  3. var dope = require("console-dope");
  4. var http = require("http");
  5. var cliArgs = require("command-line-args");
  6. var o = require("object-tools");
  7. var t = require("typical");
  8. var path = require("path");
  9. var loadConfig = require("config-master");
  10. var homePath = require("home-path");
  11. var logStats = require("stream-log-stats");
  12. var connect = require("connect");
  13. var morgan = require("morgan");
  14. var serveStatic = require("serve-static");
  15. var directory = require("serve-index");
  16. var compress = require("compression");
  17. var cliOptions = require("../lib/cli-options");
  18. var url = require("url");
  19. /* specify the command line arg definitions and usage forms */
  20. var cli = cliArgs(cliOptions);
  21. var usage = cli.getUsage({
  22. title: "local-web-server",
  23. description: "Lightweight static web server, zero configuration.",
  24. footer: "Project home: [underline]{https://github.com/75lb/local-web-server}",
  25. usage: {
  26. forms: [
  27. "$ ws <server options>",
  28. "$ ws --config",
  29. "$ ws --help"
  30. ]
  31. },
  32. groups: {
  33. server: "Server",
  34. misc: "Misc"
  35. }
  36. });
  37. /* parse command line args */
  38. try {
  39. var wsOptions = cli.parse();
  40. } catch(err){
  41. halt(err.message);
  42. }
  43. /* Load and merge together options from
  44. - ~/.local-web-server.json
  45. - {cwd}/.local-web-server.json
  46. - the `local-web-server` property of {cwd}/package.json
  47. */
  48. var storedConfig = loadConfig(
  49. path.join(homePath(), ".local-web-server.json"),
  50. path.join(process.cwd(), ".local-web-server.json"),
  51. { jsonPath: path.join(process.cwd(), "package.json"), configProperty: "local-web-server" }
  52. );
  53. var builtInDefaults = {
  54. port: 8000,
  55. directory: process.cwd(),
  56. "refresh-rate": 500,
  57. mime: {}
  58. };
  59. /* override built-in defaults with stored config and then command line args */
  60. wsOptions.server = o.extend(builtInDefaults, storedConfig, wsOptions.server);
  61. /* user input validation */
  62. if (!t.isNumber(wsOptions.server.port)) {
  63. halt("please supply a numeric port value");
  64. }
  65. if (wsOptions.misc.config){
  66. dope.log("Stored config: ");
  67. dope.log(storedConfig);
  68. process.exit(0);
  69. } else if (wsOptions.misc.help){
  70. dope.log(usage);
  71. } else {
  72. process.on("SIGINT", function(){
  73. dope.showCursor();
  74. dope.log();
  75. process.exit(0);
  76. });
  77. dope.hideCursor();
  78. launchServer();
  79. /* write launch information to stderr (stdout is reserved for web log output) */
  80. if (path.resolve(wsOptions.server.directory) === process.cwd()){
  81. dope.error("serving at %underline{%s}", "http://localhost:" + wsOptions.server.port);
  82. } else {
  83. dope.error("serving %underline{%s} at %underline{%s}", wsOptions.server.directory, "http://localhost:" + wsOptions.server.port);
  84. }
  85. }
  86. function halt(message){
  87. dope.red.log("Error: %s", message);
  88. dope.log(usage);
  89. process.exit(1);
  90. }
  91. function launchServer(){
  92. var app = connect();
  93. /* enable cross-origin requests on all resources */
  94. app.use(function(req, res, next){
  95. res.setHeader("Access-Control-Allow-Origin", "*");
  96. next();
  97. });
  98. app.use(getLogger());
  99. /* --compress enables compression */
  100. if (wsOptions.server.compress) app.use(compress());
  101. /* set the mime-type overrides specified in the config */
  102. serveStatic.mime.define(wsOptions.server.mime);
  103. /* enable static file server, including directory browsing support */
  104. app.use(serveStatic(path.resolve(wsOptions.server.directory)))
  105. .use(directory(path.resolve(wsOptions.server.directory), { icons: true }));
  106. /* launch server */
  107. http.createServer(app)
  108. .on("error", function(err){
  109. if (err.code === "EADDRINUSE"){
  110. halt("port " + wsOptions.server.port + " is already is use");
  111. } else {
  112. halt(err.message);
  113. }
  114. })
  115. .listen(wsOptions.server.port);
  116. }
  117. function getLogger(){
  118. /* log using --log-format (if supplied) */
  119. var logFormat = wsOptions.server["log-format"];
  120. if(logFormat) {
  121. if (logFormat === "none"){
  122. // do nothing, no logging required
  123. } else {
  124. if (logFormat === "logstalgia"){
  125. /* customised logger :date token, purely to satisfy Logstalgia. */
  126. morgan.token("date", function(){
  127. var d = new Date();
  128. return (d.getDate() + "/" + d.getUTCMonth() + "/" + d.getFullYear() + ":" + d.toTimeString())
  129. .replace("GMT", "").replace(" (BST)", "");
  130. });
  131. logFormat = "combined";
  132. }
  133. return morgan(logFormat);
  134. }
  135. /* if no `--log-format` was specified, pipe the default format output
  136. into `log-stats`, which prints statistics to the console */
  137. } else {
  138. return morgan("common", { stream: logStats({ refreshRate: wsOptions.server["refresh-rate"] }) });
  139. }
  140. }