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
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
12 years ago
10 years ago
10 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. }