Compare commits

..

347 Commits

Author SHA1 Message Date
dependabot[bot] 0b03952404 build(deps): bump github/codeql-action from 3.29.7 to 4.31.2
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.7 to 4.31.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/51f77329afa6477de8c49fc9c7046c15b9a4e79d...0499de31b99561a6d14a36a5f662c2a54f91beee)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 06:16:37 +00:00
dependabot[bot] 364e273836 build(deps): bump github/codeql-action from 3.28.19 to 3.29.5 (#133)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 11:22:39 +02:00
dependabot[bot] c4cde95e31 build(deps): bump step-security/harden-runner from 2.12.0 to 2.13.0 (#132)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 11:21:02 +02:00
Sebastian Beltran b3cf4a76ce chore: add funding to package.json (#131) 2025-07-23 14:46:10 +02:00
dependabot[bot] 988bc1109c build(deps): bump ossf/scorecard-action from 2.4.0 to 2.4.2 (#123)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 12:13:38 +02:00
dependabot[bot] 411920523e build(deps): bump actions/upload-artifact from 4.5.0 to 4.6.2 (#122)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 12:13:06 +02:00
dependabot[bot] 068b999804 build(deps): bump coverallsapp/github-action from 1.2.5 to 2.3.6 (#121)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 12:12:25 +02:00
dependabot[bot] af5eb8e7c3 build(deps): bump github/codeql-action from 3.27.9 to 3.28.19 (#124)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 12:11:38 +02:00
Ulises Gascón 1696289791 ci: upgrade Node versions (#128) 2025-06-07 12:06:49 +02:00
Ulises Gascón d8efc0e652 ci: apply OSSF Scorecard security best practices (#119)
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
Co-authored-by: StepSecurity Bot <bot@stepsecurity.io>
2025-06-05 14:40:58 +02:00
Carlos Serrano 6729afa6d2 chore: upgrade scorecard workflow pinned action versions (#112) 2025-04-17 16:17:01 +02:00
Íñigo Marquínez Prado 544279a064 ci: migrate to GitHub actions and add missing Node.js versions (#109)
- PR-URL: https://github.com/expressjs/serve-index/pull/109
- This commit drops support for Travis
2024-05-14 18:28:45 +02:00
Íñigo Marquínez Prado 7b27f08fac chore: add support for OSSF scorecard reporting (#110)
PR-URL: https://github.com/expressjs/serve-index/pull/110
2024-05-14 18:18:01 +02:00
Douglas Christopher Wilson d2922cd872 deps: mime-types@~2.1.30 2021-04-02 22:20:37 -04:00
Douglas Christopher Wilson 51b376a02d build: use nyc for test coverage 2021-04-01 21:41:49 -04:00
Douglas Christopher Wilson 4e7785c620 Fix icons for MIME types with "+"
fixes #99
2021-04-01 16:40:42 -04:00
Douglas Christopher Wilson 7bc2da919b build: eslint@7.23.0 2021-04-01 16:35:32 -04:00
Douglas Christopher Wilson 1e07314bb3 build: Node.js@15.13 2021-03-31 20:54:39 -04:00
Douglas Christopher Wilson b14fff17ff build: Node.js@12.22 2021-03-31 20:48:19 -04:00
Douglas Christopher Wilson b74579da7b build: eslint-plugin-markdown@2.0.0 2021-03-17 19:57:35 -04:00
Douglas Christopher Wilson b827e29cc0 build: mocha@8.3.2 2021-03-17 19:44:33 -04:00
Douglas Christopher Wilson 783928d36d build: Node.js@15.11 2021-03-15 16:33:41 -04:00
Douglas Christopher Wilson c26a84ccdb build: eslint@7.21.0 2021-02-28 00:06:53 -05:00
Douglas Christopher Wilson 6410f231d2 build: mocha@8.3.0 2021-02-27 23:48:31 -05:00
Douglas Christopher Wilson 1230f3a2a4 build: Node.js@15.10 2021-02-27 23:38:30 -05:00
Douglas Christopher Wilson 5a6402bc62 deps: mime-types@~2.1.29 2021-02-27 23:31:53 -05:00
Douglas Christopher Wilson 55c94d53d7 build: eslint@7.20.0 2021-02-25 01:24:29 -05:00
Douglas Christopher Wilson 85a41c6169 build: Node.js@10.24 2021-02-25 01:22:03 -05:00
Douglas Christopher Wilson c22ac7619b build: Node.js@12.21 2021-02-24 20:10:12 -05:00
Douglas Christopher Wilson 410d2e0336 build: Node.js@14.16 2021-02-24 20:08:10 -05:00
Douglas Christopher Wilson 395c961e7d build: Node.js@15.8 2021-02-03 00:11:40 -05:00
Douglas Christopher Wilson 91a3cd9ac1 build: eslint@7.19.0 2021-02-02 23:51:13 -05:00
Douglas Christopher Wilson 0812a30474 build: Node.js@15.7 2021-01-27 19:36:40 -05:00
Douglas Christopher Wilson 6a3fdb0dae build: supertest@6.1.3 2021-01-27 19:33:08 -05:00
Douglas Christopher Wilson 8af3044675 build: eslint@7.17.0 2021-01-04 00:12:51 -05:00
Douglas Christopher Wilson be96563b6b build: Node.js@15.5 2021-01-04 00:09:54 -05:00
Douglas Christopher Wilson 22828c2005 deps: mime-types@~2.1.28 2021-01-03 23:55:38 -05:00
Douglas Christopher Wilson 9c8d7e70c3 build: eslint@7.15.0 2020-12-13 23:51:58 -05:00
Douglas Christopher Wilson ac3c870ad2 build: Node.js@15.3 2020-11-27 19:09:34 -05:00
Douglas Christopher Wilson 546b996dad build: supertest@6.0.1 2020-11-18 00:48:39 -05:00
Douglas Christopher Wilson de3bcd39c9 build: mocha@8.2.1 2020-11-18 00:42:18 -05:00
Douglas Christopher Wilson 4bd3a75480 build: Node.js@12.20 2020-11-17 20:04:08 -05:00
Douglas Christopher Wilson 0f4a3bd370 build: eslint@7.13.0 2020-11-16 17:59:52 -05:00
Douglas Christopher Wilson 438feabf50 build: support Node.js 15.x 2020-11-07 18:44:40 -05:00
Douglas Christopher Wilson 07e47df692 build: supertest@6.0.0 2020-11-01 21:22:22 -05:00
Douglas Christopher Wilson 81afa23b29 build: Node.js@14.15 2020-10-30 22:07:21 -04:00
Douglas Christopher Wilson cad6e104ef build: eslint@7.12.1 2020-10-29 22:09:34 -04:00
Douglas Christopher Wilson 947fc1bbc8 build: mocha@8.2.0 2020-10-27 20:41:15 -04:00
Douglas Christopher Wilson 30ae682927 build: Node.js@14.14 2020-10-24 00:32:57 -04:00
Douglas Christopher Wilson b9469ab500 build: Node.js@10.23 2020-10-24 00:29:06 -04:00
Douglas Christopher Wilson 2fb9ab3260 build: Node.js@12.19 2020-10-23 20:21:44 -04:00
Douglas Christopher Wilson 9f931d53f9 build: eslint@7.10.0 2020-10-05 00:51:51 -04:00
Douglas Christopher Wilson 3884c54c00 build: Node.js@14.13 2020-10-04 20:01:27 -04:00
Douglas Christopher Wilson e161893d4a build: eslint@7.8.1 2020-09-10 00:13:18 -04:00
Douglas Christopher Wilson 006327c26a build: mocha@8.1.3 2020-09-10 00:10:02 -04:00
Douglas Christopher Wilson ed99750527 build: Node.js@14.10 2020-09-09 23:08:59 -04:00
Douglas Christopher Wilson 5f5124a79a build: eslint@7.6.0 2020-08-05 22:40:12 -04:00
Douglas Christopher Wilson b7186ae2db build: Node.js@14.7 2020-08-03 20:16:43 -04:00
Douglas Christopher Wilson 6ed2cab63e build: mocha@8.1.0 2020-08-03 20:11:18 -04:00
Douglas Christopher Wilson 7b37172d09 build: Node.js@10.22 2020-07-18 19:44:10 -04:00
Douglas Christopher Wilson bfef3ecf2a deps: http-errors@~1.8.0 2020-07-17 00:07:45 -04:00
Douglas Christopher Wilson 4fbef9d380 build: eslint@7.4.0 2020-07-16 20:29:49 -04:00
Douglas Christopher Wilson c32caf8a37 build: Node.js@14.5 2020-07-16 20:25:24 -04:00
Douglas Christopher Wilson 8e68e57125 build: mocha@8.0.1 2020-06-13 22:01:34 -04:00
Douglas Christopher Wilson f3d18ab5ce build: Node.js@12.18 2020-06-11 23:07:26 -04:00
Douglas Christopher Wilson b7b10be97d build: Node.js@10.21 2020-06-10 19:08:41 -04:00
Douglas Christopher Wilson d416ee987e build: eslint@7.2.0 2020-06-06 17:48:33 -04:00
Douglas Christopher Wilson f15cbc8386 build: Node.js@14.4 2020-06-06 17:44:03 -04:00
Douglas Christopher Wilson 5c959be72f build: Node.js@12.17 2020-05-30 21:34:02 -04:00
Douglas Christopher Wilson a92a2a10d7 build: eslint@7.1.0 2020-05-25 22:19:22 -04:00
Douglas Christopher Wilson a4fe4bcf96 build: mocha@7.2.0 2020-05-23 19:09:35 -04:00
Douglas Christopher Wilson fe7aa00af8 build: Node.js@14.3 2020-05-23 19:04:19 -04:00
Douglas Christopher Wilson 7e737fb9f4 build: eslint@7.0.0 2020-05-17 23:08:37 -04:00
Douglas Christopher Wilson ba0c70c6b5 build: Node.js@14.1 2020-05-01 21:52:54 -04:00
Douglas Christopher Wilson ee51d44121 build: mocha@7.1.2 2020-04-30 20:28:53 -04:00
Douglas Christopher Wilson 313678fdc4 build: Node.js@13.14 2020-04-29 00:08:09 -04:00
Douglas Christopher Wilson 4921806568 build: support Node.js 14.x 2020-04-28 23:11:13 -04:00
Douglas Christopher Wilson bc57152e09 deps: mime-types@~2.1.27 2020-04-28 23:08:00 -04:00
Douglas Christopher Wilson 27facca566 build: Node.js@10.20 2020-04-20 21:58:49 -04:00
Douglas Christopher Wilson 13f7bc57a2 build: Node.js@13.13 2020-04-15 16:09:24 -04:00
Douglas Christopher Wilson c1db68ba39 build: mocha@7.1.1 2020-04-06 17:50:45 -04:00
Douglas Christopher Wilson cb8b5c60f4 build: eslint-plugin-markdown@1.0.2 2020-03-14 17:44:20 -04:00
Douglas Christopher Wilson 51372447e0 build: Node.js@13.10 2020-03-11 20:41:28 -04:00
Douglas Christopher Wilson 39e8b5782b build: mocha@7.1.0 2020-03-09 23:04:24 -04:00
Douglas Christopher Wilson fc7be1bfb5 build: Node.js@12.16 2020-02-14 22:59:12 -05:00
Douglas Christopher Wilson 38768868ea build: remove deprecated Travis CI directive 2020-02-14 22:58:50 -05:00
Douglas Christopher Wilson cd937079b3 build: Node.js@13.8 2020-02-08 20:06:33 -05:00
Douglas Christopher Wilson 4f0326af87 build: Node.js@12.15 2020-02-07 22:42:08 -05:00
Douglas Christopher Wilson d0e883a3df build: Node.js@10.19 2020-02-07 22:41:01 -05:00
Douglas Christopher Wilson 105ca237f6 build: Node.js@13.7 2020-02-05 19:52:35 -05:00
Douglas Christopher Wilson 4d34c9b736 build: mocha@7.0.1 2020-02-05 19:51:54 -05:00
Douglas Christopher Wilson 34fffcb92c build: Node.js@13.6 2020-01-12 01:28:42 -05:00
Douglas Christopher Wilson 86e87f8c5e deps: mime-types@~2.1.26 2020-01-12 01:21:44 -05:00
Douglas Christopher Wilson b08cbbabd4 build: mocha@7.0.0 2020-01-11 21:42:39 -05:00
Douglas Christopher Wilson 34aa14a456 build: Node.js@13.5 2019-12-24 15:27:14 -05:00
Douglas Christopher Wilson 9b69727ad2 build: eslint@6.8.0 2019-12-24 15:20:44 -05:00
Douglas Christopher Wilson 82b74e75bf build: Node.js@12.14 2019-12-19 00:28:26 -05:00
Douglas Christopher Wilson 544b6e49a2 build: Node.js@10.18 2019-12-19 00:21:13 -05:00
Douglas Christopher Wilson 84660635a2 build: Node.js@8.17 2019-12-18 21:54:37 -05:00
Douglas Christopher Wilson 8336655fcb build: Node.js@13.3 2019-12-05 18:44:26 -05:00
Douglas Christopher Wilson 6044ccbd7d build: eslint@6.7.2 2019-12-03 19:38:41 -05:00
Douglas Christopher Wilson 2f549f0bab build: Node.js@13.2 2019-11-22 00:10:20 -05:00
Douglas Christopher Wilson 87aa9ef47c deps: mime-types@~2.1.25 2019-11-21 23:39:17 -05:00
Douglas Christopher Wilson 327056b44b build: eslint-plugin-markdown@1.0.1 2019-11-19 22:54:51 -05:00
Douglas Christopher Wilson 6e1bcada54 build: Node.js@13.1 2019-11-09 20:44:00 -05:00
Douglas Christopher Wilson 4c64e7b791 build: mocha@6.2.2 2019-11-06 17:05:11 -05:00
Douglas Christopher Wilson 823d804e58 build: support Node.js 13.x 2019-11-04 19:52:20 -05:00
Douglas Christopher Wilson 8e1d258ad1 build: Node.js@12.13 2019-11-02 00:50:02 -04:00
Douglas Christopher Wilson c24512e404 build: Node.js@10.17 2019-11-02 00:43:03 -04:00
Douglas Christopher Wilson a15e3fbb0c build: eslint@6.6.0 2019-11-01 23:51:01 -04:00
Douglas Christopher Wilson 58bfa078c7 build: fix eslint uninstall failures 2019-09-23 00:41:20 -04:00
Douglas Christopher Wilson 9f0a8519f5 build: supertest@4.0.2 2019-09-22 23:07:53 -04:00
Douglas Christopher Wilson 214eea3175 build: Node.js@12.10 2019-09-12 21:50:06 -04:00
Douglas Christopher Wilson 018fa454e0 build: eslint@6.3.0 2019-09-10 19:45:54 -04:00
Douglas Christopher Wilson 4d8b4917e7 build: mocha@6.2.0 2019-09-10 19:40:18 -04:00
Douglas Christopher Wilson 2b82ea68fc build: Node.js@12.8 2019-08-10 16:50:24 -04:00
Douglas Christopher Wilson 3ab32ac667 build: eslint@6.1.0 2019-08-10 16:47:59 -04:00
Douglas Christopher Wilson 71b655e777 build: mocha@5.2.0 2019-08-08 23:19:32 -04:00
Douglas Christopher Wilson 2802ec6f2f build: supertest@3.4.2 2019-08-06 01:22:44 -04:00
Douglas Christopher Wilson 54e0eb7115 build: Node.js@12.7 2019-08-05 19:38:35 -04:00
Douglas Christopher Wilson fc6d3eadac build: mocha@4.1.0 2019-07-17 19:44:53 -04:00
Douglas Christopher Wilson e3ff991be9 build: supertest@2.0.0 2019-07-15 22:09:35 -04:00
Douglas Christopher Wilson 8fbb732bde build: mocha@3.5.3 2019-07-14 23:07:26 -04:00
Douglas Christopher Wilson 00f3ef67a6 build: restructure CI build steps 2019-07-13 21:55:53 -04:00
Douglas Christopher Wilson 5271288d1a build: eslint@5.16.0 2019-07-10 17:49:45 -04:00
Douglas Christopher Wilson bda73e36c2 build: eslint-plugin-markdown@1.0.0 2019-07-06 18:52:56 -04:00
Douglas Christopher Wilson 9c0287c406 deps: http-errors@~1.7.3 2019-07-05 00:44:00 -04:00
Douglas Christopher Wilson 136fe69c11 build: Node.js@10.16 2019-07-05 00:27:24 -04:00
Douglas Christopher Wilson 0dde93dc69 build: Node.js@12.6 2019-07-04 23:51:06 -04:00
Douglas Christopher Wilson 12a56a6f2e build: eslint@4.19.1 2019-07-04 19:43:32 -04:00
Douglas Christopher Wilson 9569cb6852 tests: move asserts so supertest can catch 2019-06-17 20:43:06 -04:00
Douglas Christopher Wilson 20e83c893b build: support Node.js 12.x 2019-05-02 23:37:45 -04:00
Douglas Christopher Wilson 4c8c716ded build: Node.js@11.15 2019-05-02 23:37:20 -04:00
Douglas Christopher Wilson 002a251122 Fix text and json responses to match html sorting 2019-05-02 23:35:13 -04:00
Douglas Christopher Wilson 5583f9ec06 build: Node.js@11.14 2019-04-29 23:55:03 -04:00
Douglas Christopher Wilson 4482ec9639 build: Node.js@8.16 2019-04-29 23:54:49 -04:00
Douglas Christopher Wilson e7ae813459 build: eslint-plugin-markdown@1.0.0-beta.8 2019-04-29 23:54:04 -04:00
Douglas Christopher Wilson fa3fe02a2f deps: accepts@~1.3.7 2019-04-29 23:52:19 -04:00
Douglas Christopher Wilson a30f269f99 deps: parseurl@~1.3.3 2019-04-28 00:56:30 -04:00
Douglas Christopher Wilson d8ff9b9e41 deps: mime-types@~2.1.24 2019-04-28 00:55:52 -04:00
Joonas Rouhiainen d3ee7d8f55 Use 400 error on URI decode failure instead of 500
closes #85
closes #88
2019-04-27 23:18:36 -04:00
Douglas Christopher Wilson 32b7a1adec deps: http-errors@~1.7.2 2019-04-01 00:45:20 -04:00
Douglas Christopher Wilson 173f71a6a1 deps: mime-types@~2.1.22 2019-04-01 00:38:48 -04:00
Douglas Christopher Wilson 4382b8f625 build: Node.js@11.13 2019-03-31 23:02:00 -04:00
Douglas Christopher Wilson 6636afe7fd build: Node.js@6.17 2019-03-31 22:54:48 -04:00
Douglas Christopher Wilson 156303efe0 deps: http-errors@~1.7.1 2019-01-04 21:40:16 -05:00
Douglas Christopher Wilson 9c56901718 build: support Node.js 11.x 2019-01-03 17:02:48 -05:00
Douglas Christopher Wilson 935ee09c82 build: Node.js@10.15 2018-12-30 00:14:12 -05:00
Douglas Christopher Wilson 05b0e21848 build: Node.js@8.15 2018-12-30 00:09:51 -05:00
Douglas Christopher Wilson fadb156765 build: Node.js@6.16 2018-12-30 00:02:39 -05:00
Douglas Christopher Wilson abedc7fcfe deps: mime-types@~2.1.21 2018-12-25 23:50:40 -05:00
Douglas Christopher Wilson 92d67676ff build: support Node.js 10.x 2018-06-09 19:26:38 -04:00
Douglas Christopher Wilson fcad6767c0 build: Node.js@9.11 2018-06-04 00:34:22 -04:00
Douglas Christopher Wilson 9bf00b76ad deps: http-errors@~1.6.3 2018-06-04 00:21:18 -04:00
Douglas Christopher Wilson 98107736e0 deps: accepts@~1.3.5 2018-06-03 23:39:21 -04:00
Douglas Christopher Wilson 77919cf57b build: Node.js@8.11 2018-05-29 12:07:12 -04:00
Douglas Christopher Wilson 010b8c9124 build: Node.js@6.14 2018-05-26 00:08:11 -04:00
Douglas Christopher Wilson b215084582 build: Node.js@4.9 2018-05-26 00:03:32 -04:00
Douglas Christopher Wilson 451dc90796 build: fix Node.js 0.8 appveyor build 2018-05-25 23:49:02 -04:00
Douglas Christopher Wilson 9d29665ce3 Show font icon for more font types 2018-02-21 23:24:56 -05:00
Douglas Christopher Wilson f31db046f6 deps: mime-types@~2.1.18 2018-02-21 23:01:11 -05:00
Douglas Christopher Wilson c67b828cbc lint: remove usages of "==" 2017-11-23 00:08:40 -05:00
Douglas Christopher Wilson 7fc9fe1f7b build: support Node.js 9.x 2017-11-23 00:02:38 -05:00
Douglas Christopher Wilson cec35eed49 build: Node.js@8.9 2017-11-22 23:59:29 -05:00
Douglas Christopher Wilson 561f159d00 build: Node.js@6.12 2017-11-22 23:58:48 -05:00
Douglas Christopher Wilson d66e8036f7 lint: add editorconfig and eslint to enforce 2017-11-22 23:54:40 -05:00
Douglas Christopher Wilson b0f8f33d4a docs: remove gratipay badge 2017-11-22 23:30:04 -05:00
Douglas Christopher Wilson a399faa180 1.9.1 2017-09-29 00:23:59 -04:00
Douglas Christopher Wilson fc6953383f deps: parseurl@~1.3.2 2017-09-29 00:22:44 -04:00
Douglas Christopher Wilson 45dbe4f219 docs: add express.static to the express example
closes #64
2017-09-29 00:20:54 -04:00
Daniel Tschinder 76e3c3af41 deps: debug@2.6.9
closes #65
2017-09-28 23:27:29 -04:00
Douglas Christopher Wilson da37631e2c build: Node.js@8.3 2017-09-02 20:01:11 -04:00
Douglas Christopher Wilson 3e09fb26f9 build: Node.js@6.11 2017-09-02 19:58:07 -04:00
Douglas Christopher Wilson 0894f8c9ff deps: mime-types@~2.1.17 2017-09-02 19:54:30 -04:00
Douglas Christopher Wilson a4f1ef3d5e deps: http-errors@~1.6.2 2017-09-02 19:48:04 -04:00
Douglas Christopher Wilson ccbeaebe69 deps: accepts@~1.3.4 2017-09-02 19:45:33 -04:00
Douglas Christopher Wilson bc077cbe29 1.9.0 2017-05-25 15:45:44 -04:00
Douglas Christopher Wilson 9ecc9a68f9 Set X-Content-Type-Options: nosniff header 2017-05-25 01:11:36 -04:00
Douglas Christopher Wilson 2b7755a28b Refactor responding to common function 2017-05-25 01:07:35 -04:00
Douglas Christopher Wilson 506626c751 lint: remove two unused variable declarations 2017-05-25 00:51:11 -04:00
Douglas Christopher Wilson 2349675b98 build: Node.js@7.10 2017-05-25 00:45:05 -04:00
Douglas Christopher Wilson c5a317fb45 deps: batch@0.6.1 2017-05-25 00:44:21 -04:00
Douglas Christopher Wilson 9284264604 deps: debug@2.6.8
closes #63
2017-05-25 00:42:30 -04:00
Douglas Christopher Wilson c2585e2c16 build: Node.js@7.9 2017-04-28 22:09:36 -04:00
Douglas Christopher Wilson e7d4062773 tests: remove unused file 2017-04-25 23:17:40 -04:00
Douglas Christopher Wilson 3e32e4ac3d build: Node.js@7.8 2017-04-25 23:13:11 -04:00
Lucian Buzzo 69137b760f deps: debug@2.6.4
closes #62
2017-04-25 23:11:41 -04:00
Douglas Christopher Wilson a8205c834a build: Node.js@7.7 2017-03-25 22:48:16 -04:00
Douglas Christopher Wilson d09bb5e774 deps: batch@0.6.0 2017-03-25 22:45:23 -04:00
Douglas Christopher Wilson 6024e991ca deps: debug@2.6.3 2017-03-25 22:35:02 -04:00
Douglas Christopher Wilson f7e0bb551d deps: mime-types@~2.1.15 2017-03-25 22:32:39 -04:00
Douglas Christopher Wilson 7215f76cca build: Node.js@7.6 2017-03-08 00:17:05 -05:00
Douglas Christopher Wilson c6b63e4666 build: Node.js@6.10 2017-03-08 00:15:37 -05:00
Douglas Christopher Wilson ec31019c9e build: Node.js@4.8 2017-03-08 00:09:12 -05:00
Douglas Christopher Wilson efe0c6fa7d deps: http-errors@~1.6.1 2017-03-08 00:04:36 -05:00
Douglas Christopher Wilson d2bebdbd87 deps: mime-types@~2.1.14 2017-03-02 22:33:47 -05:00
Douglas Christopher Wilson 3048085dc4 deps: debug@2.6.1 2017-03-02 22:31:26 -05:00
Douglas Christopher Wilson 1c8c8b9a59 build: Node.js@7.4 2017-02-08 00:37:09 -05:00
Douglas Christopher Wilson 2bbaaba5cc build: Node.js@4.7 2017-02-08 00:35:08 -05:00
Douglas Christopher Wilson 84285a0826 build: support Node.js 7.x 2016-11-21 20:31:31 -05:00
Douglas Christopher Wilson 11c2b39640 deps: mime-types@~2.1.13 2016-11-21 20:30:36 -05:00
Douglas Christopher Wilson 98ecd80f57 deps: http-errors@~1.5.1 2016-11-21 20:29:37 -05:00
Douglas Christopher Wilson 53a7b06bc6 deps: debug@2.3.3 2016-11-20 22:11:16 -05:00
Douglas Christopher Wilson 4ed20255be build: Node.js@6.9 2016-11-20 22:08:36 -05:00
Douglas Christopher Wilson 8201055cba build: Node.js@4.6 2016-11-20 22:05:47 -05:00
Douglas Christopher Wilson af6bec9cc3 docs: add preamble to install section 2016-09-15 19:16:03 +02:00
Douglas Christopher Wilson 2932faa568 build: istanbul@0.4.5 2016-09-15 19:10:35 +02:00
Douglas Christopher Wilson dc54507b68 build: after@0.8.2 2016-09-15 19:06:24 +02:00
Douglas Christopher Wilson 72b08d6395 build: Node.js@4.5 2016-09-15 19:04:36 +02:00
Douglas Christopher Wilson 1284ad0a9f build: istanbul@0.4.4 2016-08-20 21:48:50 -04:00
Douglas Christopher Wilson a275e385c4 build: Node.js@5.12 2016-08-20 21:43:35 -04:00
Douglas Christopher Wilson 357452911d 1.8.0 2016-06-17 12:13:08 -04:00
Douglas Christopher Wilson fb1c509620 build: support Node.js 6.x 2016-06-16 20:37:26 -04:00
Douglas Christopher Wilson 2e1836e614 build: Node.js@5.11 2016-06-16 20:37:07 -04:00
Douglas Christopher Wilson ed181e36ed deps: http-errors@~1.5.0 2016-06-16 19:46:25 -04:00
Douglas Christopher Wilson f99abfe86a build: mocha@2.5.3 2016-06-16 19:44:37 -04:00
Douglas Christopher Wilson e9ccd54a5d build: istanbul@0.4.3 2016-06-16 19:43:53 -04:00
Douglas Christopher Wilson 972e4d1ced deps: accepts@~1.3.3
fixes #56
2016-06-16 19:42:47 -04:00
Douglas Christopher Wilson 28e28c8352 deps: mime-types@~2.1.11
closes #47
2016-06-16 19:41:07 -04:00
Douglas Christopher Wilson cd7ab38593 build: cache node_modules on CI 2016-04-17 22:19:04 -04:00
Douglas Christopher Wilson a8c6d17b08 build: Node.js@5.10 2016-04-17 22:11:58 -04:00
Douglas Christopher Wilson 6cb3ab01a0 build: Node.js@4.4 2016-04-17 22:09:43 -04:00
Douglas Christopher Wilson 11b27ed4d3 build: mocha@2.4.5 2016-02-13 16:09:46 -05:00
Patrick Glynn 5afc181207 Make inline file search case-insensitive
closes #38
closes #45
2016-01-24 18:51:15 -05:00
Douglas Christopher Wilson 8195aa5d6a deps: accepts@~1.3.1 2016-01-24 18:45:30 -05:00
Douglas Christopher Wilson fc9db6b56a 1.7.3 2016-01-24 18:28:28 -05:00
Douglas Christopher Wilson ac1148a50f deps: mime-types@~2.1.9 2016-01-24 18:18:25 -05:00
Douglas Christopher Wilson edc865cb7d deps: parseurl@~1.3.1 2016-01-24 18:17:51 -05:00
Douglas Christopher Wilson 22e7f31395 build: istanbul@0.4.2 2016-01-24 18:16:50 -05:00
Douglas Christopher Wilson dd48453baa build: support Node.js 5.x 2016-01-03 15:18:22 -05:00
Douglas Christopher Wilson 26cc6d91c0 deps: mime-types@~2.1.8 2016-01-03 15:15:03 -05:00
Douglas Christopher Wilson 735de3e4c0 deps: escape-html@~1.0.3 2016-01-03 15:12:38 -05:00
Douglas Christopher Wilson 3ce952cfd5 deps: accepts@~1.2.13 2016-01-03 15:09:36 -05:00
Douglas Christopher Wilson 7820063014 deps: batch@0.5.3 2015-12-30 16:36:01 -05:00
Douglas Christopher Wilson 5101c6373c build: supertest@1.1.0 2015-12-30 16:28:10 -05:00
Douglas Christopher Wilson b326b6dfa4 build: mocha@2.3.4 2015-12-30 16:25:31 -05:00
Douglas Christopher Wilson 38a1ab69fa build: istanbul@0.4.1 2015-12-30 16:19:41 -05:00
Douglas Christopher Wilson 191f4b10a3 build: support Node.js 4.x 2015-12-30 16:16:37 -05:00
Douglas Christopher Wilson a93f8002a9 build: support io.js 3.x 2015-12-30 16:15:04 -05:00
Douglas Christopher Wilson d3025b9744 build: io.js@2.5 2015-12-30 16:13:59 -05:00
Douglas Christopher Wilson ac6ea7515a build: reduce runtime versions to one per major 2015-12-30 16:12:44 -05:00
Douglas Christopher Wilson b95016be37 build: skip istanbul coverage on Node.js 0.8 2015-12-30 16:09:36 -05:00
Douglas Christopher Wilson f39ad778ed 1.7.2 2015-07-31 00:06:52 -04:00
Douglas Christopher Wilson 73413c4caa deps: mime-types@~2.1.4 2015-07-30 23:42:56 -04:00
Douglas Christopher Wilson 6711bfa431 deps: accepts@~1.2.12 2015-07-30 23:42:19 -04:00
Douglas Christopher Wilson f17363a45d 1.7.1 2015-07-05 23:32:34 -04:00
Douglas Christopher Wilson db2163892e deps: mime-types@~2.1.2 2015-07-05 23:30:07 -04:00
Douglas Christopher Wilson 5c74d1e44a deps: accepts@~1.2.10 2015-07-05 23:29:30 -04:00
Douglas Christopher Wilson bd6f5553a5 build: io.js@2.3 2015-07-05 23:28:22 -04:00
Douglas Christopher Wilson 63b45e6208 1.7.0 2015-06-15 16:50:22 -04:00
Douglas Christopher Wilson d680e6302d Accept function value for template option
closes #20
closes #30
2015-06-14 23:07:52 -04:00
Douglas Christopher Wilson 420d159be7 Stat parent directory when necessary 2015-06-14 22:29:58 -04:00
Douglas Christopher Wilson 6d3160a32c Use Date.prototype.toLocaleDateString to format date 2015-06-14 22:01:22 -04:00
Douglas Christopher Wilson 4056d3375c deps: mime-types@~2.1.1 2015-06-14 21:17:01 -04:00
Douglas Christopher Wilson 7566e487b0 deps: accepts@~1.2.9 2015-06-14 21:14:45 -04:00
Douglas Christopher Wilson 3ae1ea56d3 perf: remove argument reassignment 2015-06-14 21:13:27 -04:00
Douglas Christopher Wilson d60c7a0055 Send non-chunked response for OPTIONS 2015-06-14 21:01:42 -04:00
Douglas Christopher Wilson 6781d9536b perf: enable strict mode 2015-06-14 21:00:45 -04:00
Douglas Christopher Wilson ea919ec568 deps: escape-html@1.0.2 2015-06-14 20:59:38 -04:00
Douglas Christopher Wilson 07611f42ad build: supertest@1.0.1 2015-06-14 20:56:13 -04:00
Douglas Christopher Wilson e4355f0e97 build: mocha@2.2.5 2015-06-14 20:55:24 -04:00
Douglas Christopher Wilson 5275b835a7 build: io.js@2.1 2015-06-14 20:54:28 -04:00
Douglas Christopher Wilson 636a8ae7bc 1.6.4 2015-05-12 21:28:44 -04:00
Douglas Christopher Wilson 320226f35a deps: mime-types@~2.0.11 2015-05-12 21:00:31 -04:00
Douglas Christopher Wilson 2003c3f743 deps: mocha@~2.2.4 2015-05-12 20:59:49 -04:00
Douglas Christopher Wilson e385089155 deps: debug@~2.2.0 2015-05-12 20:59:15 -04:00
Douglas Christopher Wilson 5462db3946 deps: accepts@~1.2.7 2015-05-12 20:58:24 -04:00
Douglas Christopher Wilson cb60444cb1 deps: istanbul@0.3.9 2015-05-12 20:57:39 -04:00
Douglas Christopher Wilson 6691f1b75e build: support io.js 2.x 2015-05-12 14:46:31 -04:00
Douglas Christopher Wilson 5cb8bcc448 build: io.js@1.8 2015-05-12 13:14:15 -04:00
Douglas Christopher Wilson e11f253226 1.6.3 2015-03-14 01:22:30 -04:00
Douglas Christopher Wilson 7381a1d145 Properly escape file names in HTML
fixes #28
2015-03-13 22:34:00 -04:00
Douglas Christopher Wilson 6a4a87edcd build: support io.js 1.x 2015-03-13 22:25:52 -04:00
Douglas Christopher Wilson 99e8ec512a deps: mime-types@~2.0.10 2015-03-13 22:24:20 -04:00
Douglas Christopher Wilson 197d9f6a0f deps: accepts@~1.2.5 2015-03-13 22:23:39 -04:00
Douglas Christopher Wilson 41fe20602a deps: debug@~2.1.3 2015-03-13 21:33:11 -04:00
Douglas Christopher Wilson b5c886f36d deps: mocha@~2.2.1 2015-03-12 19:03:04 -04:00
Douglas Christopher Wilson 6da896f6d8 deps: istanbul@0.3.7 2015-03-11 22:43:09 -04:00
Douglas Christopher Wilson 5ec9698c08 1.6.2 2015-02-16 22:50:32 -05:00
Douglas Christopher Wilson 4da905e4d3 deps: http-errors@~1.3.1 2015-02-16 19:50:16 -05:00
Douglas Christopher Wilson 62ac6f345b deps: mime-types@~2.0.9 2015-02-16 19:49:09 -05:00
Douglas Christopher Wilson 9afe1a7816 deps: accepts@~1.2.4 2015-02-16 19:48:10 -05:00
Douglas Christopher Wilson 0368c20a58 docs: update badges 2015-02-16 19:46:45 -05:00
Douglas Christopher Wilson 66a13d0814 build: support Node.js 0.12 2015-02-16 19:45:12 -05:00
Douglas Christopher Wilson b8d8f4524e build: use Travis CI container infrastructure 2015-02-16 19:44:34 -05:00
Douglas Christopher Wilson 75efc2d68e build: use faster AppVeyor Node.js switching 2015-02-16 19:44:04 -05:00
Douglas Christopher Wilson 0bdab07acf 1.6.1 2015-02-01 02:51:12 -05:00
Douglas Christopher Wilson a6f02f060d build: add AppVeyor 2015-02-01 02:43:24 -05:00
Douglas Christopher Wilson 90252c37b9 deps: accepts@~1.2.3 2015-02-01 02:34:57 -05:00
Douglas Christopher Wilson 94fcb7092e deps: mime-types@~2.0.8 2015-02-01 02:34:03 -05:00
Douglas Christopher Wilson 803e4154b2 1.6.0 2015-01-01 22:15:51 -05:00
Douglas Christopher Wilson 267913151b deps: accepts@~1.2.2 2015-01-01 22:07:12 -05:00
Douglas Christopher Wilson b1e36ccbf5 deps: batch@0.5.2 2015-01-01 22:05:06 -05:00
Douglas Christopher Wilson 55e914660b deps: mocha@~2.1.0 2015-01-01 21:54:23 -05:00
Douglas Christopher Wilson 0ff43c75a8 deps: mime-types@~2.0.7 2015-01-01 21:53:37 -05:00
Douglas Christopher Wilson d15b5f1387 deps: debug@~2.1.1 2015-01-01 21:52:27 -05:00
Douglas Christopher Wilson d5a4b93449 Add link to root directory
closes #23
2014-12-10 23:40:44 -05:00
Douglas Christopher Wilson 244f8a5541 1.5.3 2014-12-10 22:41:49 -05:00
Douglas Christopher Wilson ed11812d3f deps: http-errors@~1.2.8 2014-12-10 22:40:54 -05:00
Douglas Christopher Wilson 4881fb27da deps: mime-types@~2.0.4 2014-12-10 22:40:20 -05:00
Douglas Christopher Wilson 07e47b1e78 deps: accepts@~1.1.4 2014-12-10 22:39:42 -05:00
Douglas Christopher Wilson 55a5939596 deps: istanbul@0.3.5 2014-12-10 22:38:21 -05:00
Douglas Christopher Wilson 12d7cb4a7d 1.5.2 2014-12-03 16:32:59 -05:00
Douglas Christopher Wilson c602a95d4c deps: istanbul@0.3.3 2014-12-03 16:30:33 -05:00
李文富 bf15ad0e12 Fix icon name background alignment on mobile view
closes #25
2014-11-28 12:13:09 -05:00
Douglas Christopher Wilson 9a77df8c77 1.5.1 2014-11-22 23:21:30 -05:00
Douglas Christopher Wilson 856450da8f tests: use assert instead of should 2014-11-22 23:01:45 -05:00
Douglas Christopher Wilson 1bda9f95e4 deps: supertest@~0.15.0 2014-11-22 22:26:12 -05:00
Douglas Christopher Wilson f0652de409 deps: mocha@~2.0.1 2014-11-22 22:25:24 -05:00
Douglas Christopher Wilson f0579f7093 deps: mime-types@~2.0.3 2014-11-22 22:24:52 -05:00
Douglas Christopher Wilson 970b567f48 deps: accepts@~1.1.3 2014-11-22 22:22:37 -05:00
Douglas Christopher Wilson 47b3884bbe 1.5.0 2014-10-17 01:31:52 -04:00
Douglas Christopher Wilson 5b2ff74554 Create errors with http-errors 2014-10-16 23:16:04 -04:00
Douglas Christopher Wilson 7dc0ff4d97 deps: debug@~2.1.0 2014-10-16 23:10:10 -04:00
Douglas Christopher Wilson 1bdd53c05d deps: mime-types@~2.0.2 2014-10-16 23:08:47 -04:00
Douglas Christopher Wilson dce12186f8 docs: Gittip is now Gratipay 2014-10-16 23:07:23 -04:00
Douglas Christopher Wilson 838ee4cc02 1.4.1 2014-10-15 23:22:23 -04:00
Douglas Christopher Wilson f332e6e576 deps: accepts@~1.1.2 2014-10-15 23:21:53 -04:00
Douglas Christopher Wilson b866f9690a deps: mocha@~1.21.5 2014-10-15 23:20:50 -04:00
Douglas Christopher Wilson ac94303941 1.4.0 2014-10-03 12:02:53 -04:00
Evgenus cececc8c2e Add dir argument to filter function
fixes #11
closes #18
2014-10-03 11:45:59 -04:00
Evgenus 5e050a7d23 Support using tokens multiple times
closes #19
2014-10-03 11:17:30 -04:00
Douglas Christopher Wilson 5731ebee6b 1.3.1 2014-10-01 11:36:15 -04:00
Douglas Christopher Wilson c6b9d3bdbf docs: fix non-https badge 2014-10-01 11:29:54 -04:00
Douglas Christopher Wilson 4686c18e1d deps: supertest@~0.14.0 2014-10-01 11:07:38 -04:00
Douglas Christopher Wilson 8a06bb7e19 deps: accepts@~1.1.1 2014-10-01 11:05:24 -04:00
Douglas Christopher Wilson effbe1a4b0 Fix incorrect 403 on Windows and Node.js 0.11
fixes #17
2014-10-01 11:05:11 -04:00
Douglas Christopher Wilson 351c226736 1.3.0 2014-09-21 00:19:40 -04:00
Douglas Christopher Wilson 1b098fb723 build: minor code tweaks 2014-09-21 00:12:05 -04:00
Douglas Christopher Wilson 8880a8b80a Lookup icon by mime type for greater icon support 2014-09-21 00:10:45 -04:00
Van Nguyen 276b52782a Add icon for mkv files
closes #16
2014-09-20 22:53:11 -04:00
Douglas Christopher Wilson 591fd9ca13 1.2.1 2014-09-06 00:02:42 -04:00
Douglas Christopher Wilson 857b37aad4 deps: accepts@~1.1.0 2014-09-05 23:32:41 -04:00
Douglas Christopher Wilson 40a38e688d deps: istanbul@0.3.2 2014-09-05 23:31:51 -04:00
Douglas Christopher Wilson 874d32748b deps: debug@~2.0.0 2014-09-05 23:31:13 -04:00
Douglas Christopher Wilson 63456cccf2 1.2.0 2014-08-25 19:13:55 -04:00
Douglas Christopher Wilson 6ce2fd216b Add debug messages 2014-08-25 18:56:27 -04:00
Douglas Christopher Wilson 83de752968 Resolve relative paths at middleware setup
fixes #15
2014-08-25 18:51:14 -04:00
Douglas Christopher Wilson 86db218bdb docs: readme tweaks 2014-08-25 18:36:10 -04:00
Douglas Christopher Wilson 0375dd3638 tests: check for global leaks 2014-08-25 18:33:22 -04:00
Douglas Christopher Wilson 005dbad307 build: change readme/history capitalization 2014-08-25 18:32:20 -04:00
Douglas Christopher Wilson a189d6b5e5 docs: link to license from readme 2014-08-25 18:30:59 -04:00
Douglas Christopher Wilson a01c0286df docs: update badges 2014-08-25 18:29:49 -04:00
Douglas Christopher Wilson 6e4da1e8d6 build: remove npmignore file 2014-08-25 18:27:54 -04:00
Douglas Christopher Wilson d88117fad9 docs: expand examples
closes #14
2014-08-17 13:38:00 -04:00
Douglas Christopher Wilson 194663a06f 1.1.6 2014-08-10 17:45:04 -04:00
Douglas Christopher Wilson fd277c695c Fix URL parsing 2014-08-10 17:40:45 -04:00
35 changed files with 2006 additions and 689 deletions
+11
View File
@@ -0,0 +1,11 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[{*.js,*.json,*.yml}]
indent_size = 2
indent_style = space
+3
View File
@@ -0,0 +1,3 @@
.nyc_output
coverage
node_modules
+13
View File
@@ -0,0 +1,13 @@
root: true
extends:
- plugin:markdown/recommended
plugins:
- markdown
overrides:
- files: '**/*.md'
processor: 'markdown/markdown'
rules:
eol-last: error
eqeqeq: [error, allow-null]
indent: [error, 2, { SwitchCase: 1 }]
no-trailing-spaces: error
+15
View File
@@ -0,0 +1,15 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
- package-ecosystem: npm
directory: /
schedule:
interval: monthly
open-pull-requests-limit: 10
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
+241
View File
@@ -0,0 +1,241 @@
name: ci
on:
- pull_request
- push
permissions:
contents: read
jobs:
test:
permissions:
checks: write # for coverallsapp/github-action to create new checks
contents: read # for actions/checkout to fetch code
runs-on: ubuntu-latest
strategy:
matrix:
name:
- Node.js 0.8
- Node.js 0.10
- Node.js 0.12
- io.js 1.x
- io.js 2.x
- io.js 3.x
- Node.js 4.x
- Node.js 5.x
- Node.js 6.x
- Node.js 7.x
- Node.js 8.x
- Node.js 9.x
- Node.js 10.x
- Node.js 11.x
- Node.js 12.x
- Node.js 13.x
- Node.js 14.x
- Node.js 15.x
- Node.js 16.x
- Node.js 17.x
- Node.js 18.x
- Node.js 19.x
- Node.js 20.x
- Node.js 21.x
- Node.js 22.x
- Node.js 23.x
- Node.js 24.x
include:
- name: Node.js 0.8
node-version: "0.8"
npm-i: mocha@2.5.3 supertest@1.1.0
npm-rm: nyc
- name: Node.js 0.10
node-version: "0.10"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: Node.js 0.12
node-version: "0.12"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: io.js 1.x
node-version: "1"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: io.js 2.x
node-version: "2"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: io.js 3.x
node-version: "3"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: Node.js 4.x
node-version: "4"
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
- name: Node.js 5.x
node-version: "5"
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
- name: Node.js 6.x
node-version: "6"
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.3
- name: Node.js 7.x
node-version: "7"
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.3
- name: Node.js 8.x
node-version: "8"
npm-i: mocha@7.1.2 nyc@14.1.1 supertest@6.1.3
- name: Node.js 9.x
node-version: "9"
npm-i: mocha@7.1.2 nyc@14.1.1 supertest@6.1.3
- name: Node.js 10.x
node-version: "10"
npm-i: mocha@8.4.0 supertest@6.1.3
- name: Node.js 11.x
node-version: "11"
npm-i: mocha@8.4.0 supertest@6.1.3
- name: Node.js 12.x
node-version: "12"
npm-i: mocha@9.2.2 supertest@6.1.3
- name: Node.js 13.x
node-version: "13"
npm-i: mocha@9.2.2 supertest@6.1.3
- name: Node.js 14.x
node-version: "14"
npm-i: supertest@6.1.3
- name: Node.js 15.x
node-version: "15"
npm-i: supertest@6.1.3
- name: Node.js 16.x
node-version: "16"
npm-i: supertest@6.1.3
- name: Node.js 17.x
node-version: "17"
npm-i: supertest@6.1.3
- name: Node.js 18.x
node-version: "18"
- name: Node.js 19.x
node-version: "19"
- name: Node.js 20.x
node-version: "20"
- name: Node.js 21.x
node-version: "21"
- name: Node.js 22.x
node-version: "22"
- name: Node.js 23.x
node-version: "23"
- name: Node.js 24.x
node-version: "24"
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install Node.js ${{ matrix.node-version }}
shell: bash -eo pipefail -l {0}
run: |
nvm install --default ${{ matrix.node-version }}
if [[ "${{ matrix.node-version }}" == 0.* && "$(cut -d. -f2 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then
nvm install --alias=npm 0.10
nvm use ${{ matrix.node-version }}
if [[ "$(npm -v)" == 1.1.* ]]; then
nvm exec npm npm install -g npm@1.1
ln -fs "$(which npm)" "$(dirname "$(nvm which npm)")/npm"
else
sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")"
fi
npm config set strict-ssl false
fi
dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
- name: Configure npm
run: |
if [[ "$(npm config get package-lock)" == "true" ]]; then
npm config set package-lock false
else
npm config set shrinkwrap false
fi
- name: Remove npm module(s) ${{ matrix.npm-rm }}
if: matrix.npm-rm != ''
run: npm rm --silent --save-dev ${{ matrix.npm-rm }}
- name: Install npm module(s) ${{ matrix.npm-i }}
if: matrix.npm-i != ''
run: npm install --save-dev ${{ matrix.npm-i }}
- name: Setup Node.js version-specific dependencies
shell: bash
run: |
# eslint for linting
# - remove on Node.js < 12
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
grep -E '^eslint(-|$)' | \
sort -r | \
xargs -n1 npm rm --silent --save-dev
fi
- name: Install Node.js dependencies
run: npm install
- name: List environment
id: list_env
shell: bash
run: |
echo "node@$(node -v)"
echo "npm@$(npm -v)"
npm -s ls ||:
(npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT"
- name: Run tests
shell: bash
run: |
if npm -ps ls nyc | grep -q nyc; then
npm run test-ci
else
npm test
fi
- name: Lint code
if: steps.list_env.outputs.eslint != ''
run: npm run lint
- name: Collect code coverage
uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # master
if: steps.list_env.outputs.nyc != ''
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
flag-name: run-${{ matrix.test_number }}
parallel: true
coverage:
permissions:
checks: write # for coverallsapp/github-action to create new checks
needs: test
runs-on: ubuntu-latest
steps:
- name: Upload code coverage
uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
+78
View File
@@ -0,0 +1,78 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: ["master"]
pull_request:
# The branches below must be a subset of the branches above
branches: ["master"]
schedule:
- cron: "0 0 * * 1"
permissions:
contents: read
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ["javascript"]
# CodeQL supports [ $supported-codeql-languages ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
with:
category: "/language:${{matrix.language}}"
+73
View File
@@ -0,0 +1,73 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '16 21 * * 1'
push:
branches: [ "master" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
with:
sarif_file: results.sarif
+2
View File
@@ -1,3 +1,5 @@
.nyc_output/
coverage coverage
node_modules node_modules
npm-debug.log npm-debug.log
package-lock.json
-3
View File
@@ -1,3 +0,0 @@
coverage
test
.travis.yml
-11
View File
@@ -1,11 +0,0 @@
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.11"
matrix:
allow_failures:
- node_js: "0.11"
fast_finish: true
script: "npm run-script test-travis"
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
+326
View File
@@ -0,0 +1,326 @@
unreleased
==========
* Fix icons for MIME types with `+`
* Fix text and json responses to match html sorting
* Show font icon for more font types
* Use 400 error on URI decode failure instead of 500
* deps: accepts@~1.3.7
- deps: mime-types@~2.1.24
- deps: negotiator@0.6.2
* deps: http-errors@~1.8.0
- Set constructor name when possible
- deps: depd@~1.1.2
- deps: inherits@2.0.4
- deps: setprototypeof@1.2.0
- deps: statuses@'>= 1.5.0 < 2'
* deps: mime-types@~2.1.30
- Add new mime types
- deps: mime-db@~1.47.0
* deps: parseurl@~1.3.3
1.9.1 / 2017-09-28
==================
* deps: accepts@~1.3.4
- deps: mime-types@~2.1.16
* deps: debug@2.6.9
* deps: http-errors@~1.6.2
- deps: depd@1.1.1
* deps: mime-types@~2.1.17
- Add new mime types
- deps: mime-db@~1.30.0
* deps: parseurl@~1.3.2
- perf: reduce overhead for full URLs
- perf: unroll the "fast-path" `RegExp`
1.9.0 / 2017-05-25
==================
* Set `X-Content-Type-Options: nosniff` header
* deps: batch@0.6.1
* deps: debug@2.6.8
- Allow colors in workers
- Deprecated `DEBUG_FD` environment variable set to `3` or higher
- Fix `DEBUG_MAX_ARRAY_LENGTH`
- Fix error when running under React Native
- Use same color for same namespace
- deps: ms@2.0.0
* deps: http-errors@~1.6.1
- Make `message` property enumerable for `HttpError`s
- deps: inherits@2.0.3
- deps: setprototypeof@1.0.3
- deps: statuses@'>= 1.3.1 < 2'
* deps: mime-types@~2.1.15
- Add new mime types
- Add `audio/mp3`
1.8.0 / 2016-06-17
==================
* Make inline file search case-insensitive
* deps: accepts@~1.3.3
- deps: mime-types@~2.1.11
- deps: negotiator@0.6.1
- perf: improve header parsing speed
* deps: http-errors@~1.5.0
- Use `setprototypeof` module to replace `__proto__` setting
- deps: inherits@2.0.1
- deps: statuses@'>= 1.3.0 < 2'
- perf: enable strict mode
* deps: mime-types@~2.1.11
- Add new mime types
- Update primary extension for `audio/mp4`
- deps: mime-db@~1.23.0
1.7.3 / 2016-01-24
==================
* deps: accepts@~1.2.13
- deps: mime-types@~2.1.6
* deps: batch@0.5.3
- Fix invalid dependency for browserify
* deps: escape-html@~1.0.3
- perf: enable strict mode
- perf: optimize string replacement
- perf: use faster string coercion
* deps: mime-types@~2.1.9
- Add new mime types
* deps: parseurl@~1.3.1
- perf: enable strict mode
1.7.2 / 2015-07-30
==================
* deps: accepts@~1.2.12
- deps: mime-types@~2.1.4
* deps: mime-types@~2.1.4
- Add new mime types
1.7.1 / 2015-07-05
==================
* deps: accepts@~1.2.10
- deps: mime-types@~2.1.2
* deps: mime-types@~2.1.2
- Add new mime types
1.7.0 / 2015-06-15
==================
* Accept `function` value for `template` option
* Send non-chunked response for `OPTIONS`
* Stat parent directory when necessary
* Use `Date.prototype.toLocaleDateString` to format date
* deps: accepts@~1.2.9
- deps: mime-types@~2.1.1
- deps: negotiator@0.5.3
- perf: avoid argument reassignment & argument slice
- perf: avoid negotiator recursive construction
- perf: enable strict mode
- perf: remove unnecessary bitwise operator
* deps: escape-html@1.0.2
* deps: mime-types@~2.1.1
- Add new mime types
* perf: enable strict mode
* perf: remove argument reassignment
1.6.4 / 2015-05-12
==================
* deps: accepts@~1.2.7
- deps: mime-types@~2.0.11
- deps: negotiator@0.5.3
* deps: debug@~2.2.0
- deps: ms@0.7.1
* deps: mime-types@~2.0.11
- Add new mime types
1.6.3 / 2015-03-13
==================
* Properly escape file names in HTML
* deps: accepts@~1.2.5
- deps: mime-types@~2.0.10
* deps: debug@~2.1.3
- Fix high intensity foreground color for bold
- deps: ms@0.7.0
* deps: escape-html@1.0.1
* deps: mime-types@~2.0.10
- Add new mime types
1.6.2 / 2015-02-16
==================
* deps: accepts@~1.2.4
- deps: mime-types@~2.0.9
- deps: negotiator@0.5.1
* deps: http-errors@~1.3.1
- Construct errors using defined constructors from `createError`
- Fix error names that are not identifiers
- Set a meaningful `name` property on constructed errors
* deps: mime-types@~2.0.9
- Add new mime types
- deps: mime-db@~1.7.0
1.6.1 / 2015-01-31
==================
* deps: accepts@~1.2.3
- deps: mime-types@~2.0.8
* deps: mime-types@~2.0.8
- Add new mime types
- deps: mime-db@~1.6.0
1.6.0 / 2015-01-01
==================
* Add link to root directory
* deps: accepts@~1.2.2
- deps: mime-types@~2.0.7
- deps: negotiator@0.5.0
* deps: batch@0.5.2
* deps: debug@~2.1.1
* deps: mime-types@~2.0.7
- Add new mime types
- Fix missing extensions
- Fix various invalid MIME type entries
- Remove example template MIME types
- deps: mime-db@~1.5.0
1.5.3 / 2014-12-10
==================
* deps: accepts@~1.1.4
- deps: mime-types@~2.0.4
* deps: http-errors@~1.2.8
- Fix stack trace from exported function
* deps: mime-types@~2.0.4
- Add new mime types
- deps: mime-db@~1.3.0
1.5.2 / 2014-12-03
==================
* Fix icon name background alignment on mobile view
1.5.1 / 2014-11-22
==================
* deps: accepts@~1.1.3
- deps: mime-types@~2.0.3
* deps: mime-types@~2.0.3
- Add new mime types
- deps: mime-db@~1.2.0
1.5.0 / 2014-10-16
==================
* Create errors with `http-errors`
* deps: debug@~2.1.0
- Implement `DEBUG_FD` env variable support
* deps: mime-types@~2.0.2
- deps: mime-db@~1.1.0
1.4.1 / 2014-10-15
==================
* deps: accepts@~1.1.2
- Fix error when media type has invalid parameter
- deps: negotiator@0.4.9
1.4.0 / 2014-10-03
==================
* Add `dir` argument to `filter` function
* Support using tokens multiple times
1.3.1 / 2014-10-01
==================
* Fix incorrect 403 on Windows and Node.js 0.11
* deps: accepts@~1.1.1
- deps: mime-types@~2.0.2
- deps: negotiator@0.4.8
1.3.0 / 2014-09-20
==================
* Add icon for mkv files
* Lookup icon by mime type for greater icon support
1.2.1 / 2014-09-05
==================
* deps: accepts@~1.1.0
* deps: debug@~2.0.0
1.2.0 / 2014-08-25
==================
* Add `debug` messages
* Resolve relative paths at middleware setup
1.1.6 / 2014-08-10
==================
* Fix URL parsing
* deps: parseurl@~1.3.0
1.1.5 / 2014-07-27
==================
* Fix Content-Length calculation for multi-byte file names
* deps: accepts@~1.0.7
- deps: negotiator@0.4.7
1.1.4 / 2014-06-20
==================
* deps: accepts@~1.0.5
1.1.3 / 2014-06-20
==================
* deps: accepts@~1.0.4
- use `mime-types`
1.1.2 / 2014-06-19
==================
* deps: batch@0.5.1
1.1.1 / 2014-06-11
==================
* deps: accepts@1.0.3
1.1.0 / 2014-05-29
==================
* Fix content negotiation when no `Accept` header
* Properly support all HTTP methods
* Support vanilla node.js http servers
* Treat `ENAMETOOLONG` as code 414
* Use accepts for negotiation
1.0.3 / 2014-05-20
==================
* Fix error from non-statable files in HTML view
1.0.2 / 2014-04-28
==================
* Add `stylesheet` option
* deps: negotiator@0.4.3
1.0.1 / 2014-03-05
==================
* deps: negotiator@0.4.2
1.0.0 / 2014-03-05
==================
* Genesis from connect
-57
View File
@@ -1,57 +0,0 @@
1.1.5 / 2014-07-27
==================
* Fix Content-Length calculation for multi-byte file names
* deps: accepts@~1.0.7
- deps: negotiator@0.4.7
1.1.4 / 2014-06-20
==================
* deps: accepts@~1.0.5
1.1.3 / 2014-06-20
==================
* deps: accepts@~1.0.4
- use `mime-types`
1.1.2 / 2014-06-19
==================
* deps: batch@0.5.1
1.1.1 / 2014-06-11
==================
* deps: accepts@1.0.3
1.1.0 / 2014-05-29
==================
* Fix content negotiation when no `Accept` header
* Properly support all HTTP methods
* Support vanilla node.js http servers
* Treat `ENAMETOOLONG` as code 414
* Use accepts for negotiation
1.0.3 / 2014-05-20
==================
* Fix error from non-statable files in HTML view
1.0.2 / 2014-04-28
==================
* Add `stylesheet` option
* deps: negotiator@0.4.3
1.0.1 / 2014-03-05
==================
* deps: negotiator@0.4.2
1.0.0 / 2014-03-05
==================
* Genesis from connect
+1 -1
View File
@@ -3,7 +3,7 @@
Copyright (c) 2010 Sencha Inc. Copyright (c) 2010 Sencha Inc.
Copyright (c) 2011 LearnBoost Copyright (c) 2011 LearnBoost
Copyright (c) 2011 TJ Holowaychuk Copyright (c) 2011 TJ Holowaychuk
Copyright (c) 2014 Douglas Christopher Wilson Copyright (c) 2014-2015 Douglas Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
+151
View File
@@ -0,0 +1,151 @@
# serve-index
[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![Linux Build Status][ci-image]][ci-url]
[![Windows Build][appveyor-image]][appveyor-url]
[![Coverage Status][coveralls-image]][coveralls-url]
Serves pages that contain directory listings for a given path.
## Install
This is a [Node.js](https://nodejs.org/en/) module available through the
[npm registry](https://www.npmjs.com/). Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
```sh
$ npm install serve-index
```
## API
```js
var serveIndex = require('serve-index')
```
### serveIndex(path, options)
Returns middlware that serves an index of the directory in the given `path`.
The `path` is based off the `req.url` value, so a `req.url` of `'/some/dir`
with a `path` of `'public'` will look at `'public/some/dir'`. If you are using
something like `express`, you can change the URL "base" with `app.use` (see
the express example).
#### Options
Serve index accepts these properties in the options object.
##### filter
Apply this filter function to files. Defaults to `false`. The `filter` function
is called for each file, with the signature `filter(filename, index, files, dir)`
where `filename` is the name of the file, `index` is the array index, `files` is
the array of files and `dir` is the absolute path the file is located (and thus,
the directory the listing is for).
##### hidden
Display hidden (dot) files. Defaults to `false`.
##### icons
Display icons. Defaults to `false`.
##### stylesheet
Optional path to a CSS stylesheet. Defaults to a built-in stylesheet.
##### template
Optional path to an HTML template or a function that will render a HTML
string. Defaults to a built-in template.
When given a string, the string is used as a file path to load and then the
following tokens are replaced in templates:
* `{directory}` with the name of the directory.
* `{files}` with the HTML of an unordered list of file links.
* `{linked-path}` with the HTML of a link to the directory.
* `{style}` with the specified stylesheet and embedded images.
When given as a function, the function is called as `template(locals, callback)`
and it needs to invoke `callback(error, htmlString)`. The following are the
provided locals:
* `directory` is the directory being displayed (where `/` is the root).
* `displayIcons` is a Boolean for if icons should be rendered or not.
* `fileList` is a sorted array of files in the directory. The array contains
objects with the following properties:
- `name` is the relative name for the file.
- `stat` is a `fs.Stats` object for the file.
* `path` is the full filesystem path to `directory`.
* `style` is the default stylesheet or the contents of the `stylesheet` option.
* `viewName` is the view name provided by the `view` option.
##### view
Display mode. `tiles` and `details` are available. Defaults to `tiles`.
## Examples
### Serve directory indexes with vanilla node.js http server
```js
var finalhandler = require('finalhandler')
var http = require('http')
var serveIndex = require('serve-index')
var serveStatic = require('serve-static')
// Serve directory indexes for public/ftp folder (with icons)
var index = serveIndex('public/ftp', {'icons': true})
// Serve up public/ftp folder files
var serve = serveStatic('public/ftp')
// Create server
var server = http.createServer(function onRequest(req, res){
var done = finalhandler(req, res)
serve(req, res, function onNext(err) {
if (err) return done(err)
index(req, res, done)
})
})
// Listen
server.listen(3000)
```
### Serve directory indexes with express
```js
var express = require('express')
var serveIndex = require('serve-index')
var app = express()
// Serve URLs like /ftp/thing as public/ftp/thing
// The express.static serves the file contents
// The serveIndex is this module serving the directory
app.use('/ftp', express.static('public/ftp'), serveIndex('public/ftp', {'icons': true}))
// Listen
app.listen(3000)
```
## License
[MIT](LICENSE). The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons
are created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/).
[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/serve-index/master.svg?label=windows
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/serve-index
[ci-image]: https://badgen.net/github/checks/expressjs/serve-index/master?label=ci
[ci-url]: https://github.com/expressjs/serve-index/actions/workflows/ci.yml
[coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-index/master.svg
[coveralls-url]: https://coveralls.io/r/expressjs/serve-index?branch=master
[downloads-image]: https://img.shields.io/npm/dm/serve-index.svg
[downloads-url]: https://npmjs.org/package/serve-index
[npm-image]: https://img.shields.io/npm/v/serve-index.svg
[npm-url]: https://npmjs.org/package/serve-index
-65
View File
@@ -1,65 +0,0 @@
# serve-index
[![NPM version](https://badge.fury.io/js/serve-index.svg)](http://badge.fury.io/js/serve-index)
[![Build Status](https://travis-ci.org/expressjs/serve-index.svg?branch=master)](https://travis-ci.org/expressjs/serve-index)
[![Coverage Status](https://img.shields.io/coveralls/expressjs/serve-index.svg?branch=master)](https://coveralls.io/r/expressjs/serve-index)
[![Gittip](http://img.shields.io/gittip/dougwilson.svg)](https://www.gittip.com/dougwilson/)
Serves pages that contain directory listings for a given path.
## API
```js
var express = require('express')
var serveIndex = require('serve-index')
var app = express()
app.use(serveIndex('public/ftp', {'icons': true}))
app.listen()
```
### serveIndex(path, options)
Returns middlware that serves an index of the directory in the given `path`.
#### Options
- `hidden` - display hidden (dot) files. Defaults to `false`.
- `view` - display mode. `tiles` and `details` are available. Defaults to `tiles`.
- `icons` - display icons. Defaults to `false`.
- `filter` - Apply this filter function to files. Defaults to `false`.
- `stylesheet` - Optional path to a CSS stylesheet. Defaults to a built-in stylesheet.
- `template` - Optional path to an HTML template. Defaults to a built-in template.
- The following tokens are replaced in templates:
- `{directory}` with the name of the directory.
- `{files}` with the HTML of an unordered list of file links.
- `{linked-path}` with the HTML of a link to the directory.
- `{style}` with the specified stylesheet and embedded images.
## License
The MIT License (MIT)
Copyright (c) 2014 Douglas Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons are created
by/copyright of [FAMFAMFAM](http://www.famfamfam.com/).
+92
View File
@@ -0,0 +1,92 @@
environment:
matrix:
- nodejs_version: "0.8"
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "1.8"
- nodejs_version: "2.5"
- nodejs_version: "3.3"
- nodejs_version: "4.9"
- nodejs_version: "5.12"
- nodejs_version: "6.17"
- nodejs_version: "7.10"
- nodejs_version: "8.17"
- nodejs_version: "9.11"
- nodejs_version: "10.24"
- nodejs_version: "11.15"
- nodejs_version: "12.22"
- nodejs_version: "13.14"
- nodejs_version: "14.16"
- nodejs_version: "15.13"
cache:
- node_modules
install:
# Install Node.js
- ps: >-
try { Install-Product node $env:nodejs_version -ErrorAction Stop }
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) }
# Configure npm
- ps: |
# Skip updating shrinkwrap / lock
npm config set shrinkwrap false
# Skip SSL validation on Node.js < 0.10
if ([int]$env:nodejs_version.split(".")[0] -eq 0 -and [int]$env:nodejs_version.split(".")[1] -lt 10) {
npm config set strict-ssl false
}
# Remove all non-test dependencies
- ps: |
# Remove coverage dependency
npm rm --silent --save-dev nyc
# Remove lint dependencies
cmd.exe /c "node -pe `"Object.keys(require('./package').devDependencies).join('\n')`"" | `
sls "^eslint(-|$)" | `
%{ npm rm --silent --save-dev $_ }
# Setup Node.js version-specific dependencies
- ps: |
# mocha for testing
# - use 2.x for Node.js < 0.10
# - use 3.x for Node.js < 4
# - use 5.x for Node.js < 6
# - use 6.x for Node.js < 8
# - use 7.x for Node.js < 10
if ([int]$env:nodejs_version.split(".")[0] -eq 0 -and [int]$env:nodejs_version.split(".")[1] -lt 10) {
npm install --silent --save-dev mocha@2.5.3
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev mocha@3.5.3
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
npm install --silent --save-dev mocha@5.2.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
npm install --silent --save-dev mocha@6.2.2
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
npm install --silent --save-dev mocha@7.2.0
}
- ps: |
# mocha for testing
# - use 1.1.0 for Node.js < 0.10
# - use 2.0.0 for Node.js < 4
# - use 3.4.2 for Node.js < 6
if ([int]$env:nodejs_version.split(".")[0] -eq 0 -and [int]$env:nodejs_version.split(".")[1] -lt 10) {
npm install --silent --save-dev supertest@1.1.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev supertest@2.0.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
npm install --silent --save-dev supertest@3.4.2
}
# Update Node.js modules
- ps: |
# Prune & rebuild node_modules
if (Test-Path -Path node_modules) {
npm prune
npm rebuild
}
# Install Node.js modules
- npm install
build: off
test_script:
# Output version data
- ps: |
node --version
npm --version
# Run test script
- npm test
version: "{build}"
+442 -269
View File
@@ -1,29 +1,39 @@
/*! /*!
* serve-index * serve-index
* Copyright(c) 2011 Sencha Inc. * Copyright(c) 2011 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk * Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2014 Douglas Christopher Wilson * Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed * MIT Licensed
*/ */
// TODO: arrow key navigation 'use strict';
// TODO: make icons extensible
/** /**
* Module dependencies. * Module dependencies.
* @private
*/ */
var accepts = require('accepts'); var accepts = require('accepts');
var http = require('http') var createError = require('http-errors');
, fs = require('fs') var debug = require('debug')('serve-index');
, parse = require('url').parse var escapeHtml = require('escape-html');
var fs = require('fs')
, path = require('path') , path = require('path')
, normalize = path.normalize , normalize = path.normalize
, sep = path.sep , sep = path.sep
, extname = path.extname , extname = path.extname
, join = path.join; , join = path.join;
var Batch = require('batch'); var Batch = require('batch');
var mime = require('mime-types');
var parseUrl = require('parseurl');
var resolve = require('path').resolve;
/**
* Module exports.
* @public
*/
module.exports = serveIndex;
/*! /*!
* Icon cache. * Icon cache.
@@ -64,50 +74,66 @@ var mediaType = {
* *
* See Readme.md for documentation of options. * See Readme.md for documentation of options.
* *
* @param {String} path * @param {String} root
* @param {Object} options * @param {Object} options
* @return {Function} middleware * @return {Function} middleware
* @api public * @public
*/ */
exports = module.exports = function serveIndex(root, options){ function serveIndex(root, options) {
options = options || {}; var opts = options || {};
// root required // root required
if (!root) throw new TypeError('serveIndex() root path required'); if (!root) {
throw new TypeError('serveIndex() root path required');
}
var hidden = options.hidden // resolve root to absolute and normalize
, icons = options.icons var rootPath = normalize(resolve(root) + sep);
, view = options.view || 'tiles'
, filter = options.filter
, root = normalize(root + sep)
, template = options.template || defaultTemplate
, stylesheet = options.stylesheet || defaultStylesheet;
return function serveIndex(req, res, next) { var filter = opts.filter;
var hidden = opts.hidden;
var icons = opts.icons;
var stylesheet = opts.stylesheet || defaultStylesheet;
var template = opts.template || defaultTemplate;
var view = opts.view || 'tiles';
return function (req, res, next) {
if (req.method !== 'GET' && req.method !== 'HEAD') { if (req.method !== 'GET' && req.method !== 'HEAD') {
res.statusCode = 'OPTIONS' === req.method res.statusCode = 'OPTIONS' === req.method ? 200 : 405;
? 200
: 405;
res.setHeader('Allow', 'GET, HEAD, OPTIONS'); res.setHeader('Allow', 'GET, HEAD, OPTIONS');
res.setHeader('Content-Length', '0');
res.end(); res.end();
return; return;
} }
var url = parse(req.url) // get dir
, dir = decodeURIComponent(url.pathname) var dir = getRequestedDir(req)
, path = normalize(join(root, dir))
, originalUrl = parse(req.originalUrl || req.url) // bad request
, originalDir = decodeURIComponent(originalUrl.pathname) if (dir === null) return next(createError(400))
, showUp = path != root;
// parse URLs
var originalUrl = parseUrl.original(req);
var originalDir = decodeURIComponent(originalUrl.pathname);
// join / normalize from root dir
var path = normalize(join(rootPath, dir));
// null byte(s), bad request // null byte(s), bad request
if (~path.indexOf('\0')) return next(createError(400)); if (~path.indexOf('\0')) return next(createError(400));
// malicious path, forbidden // malicious path
if (0 != path.indexOf(root)) return next(createError(403)); if ((path + sep).substr(0, rootPath.length) !== rootPath) {
debug('malicious path "%s"', path);
return next(createError(403));
}
// determine ".." display
var showUp = normalize(resolve(path) + sep) !== rootPath;
// check if we have a directory // check if we have a directory
debug('stat "%s"', path);
fs.stat(path, function(err, stat){ fs.stat(path, function(err, stat){
if (err && err.code === 'ENOENT') { if (err && err.code === 'ENOENT') {
return next(); return next();
@@ -123,19 +149,22 @@ exports = module.exports = function serveIndex(root, options){
if (!stat.isDirectory()) return next(); if (!stat.isDirectory()) return next();
// fetch files // fetch files
debug('readdir "%s"', path);
fs.readdir(path, function(err, files){ fs.readdir(path, function(err, files){
if (err) return next(err); if (err) return next(err);
if (!hidden) files = removeHidden(files); if (!hidden) files = removeHidden(files);
if (filter) files = files.filter(filter); if (filter) files = files.filter(function(filename, index, list) {
return filter(filename, index, list, path);
});
files.sort(); files.sort();
// content-negotiation // content-negotiation
var accept = accepts(req); var accept = accepts(req);
var type = accept.types(mediaTypes); var type = accept.type(mediaTypes);
// not acceptable // not acceptable
if (!type) return next(createError(406)); if (!type) return next(createError(406));
exports[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template, stylesheet); serveIndex[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template, stylesheet);
}); });
}); });
}; };
@@ -145,26 +174,40 @@ exports = module.exports = function serveIndex(root, options){
* Respond with text/html. * Respond with text/html.
*/ */
exports.html = function(req, res, files, next, dir, showUp, icons, path, view, template, stylesheet){ serveIndex.html = function _html(req, res, files, next, dir, showUp, icons, path, view, template, stylesheet) {
fs.readFile(template, 'utf8', function(err, str){ var render = typeof template !== 'function'
if (err) return next(err); ? createHtmlRender(template)
fs.readFile(stylesheet, 'utf8', function(err, style){ : template
if (err) return next(err);
stat(path, files, function(err, stats){
if (err) return next(err);
files = files.map(function(file, i){ return { name: file, stat: stats[i] }; });
files.sort(fileSort);
if (showUp) files.unshift({ name: '..' });
str = str
.replace('{style}', style.concat(iconStyle(files, icons)))
.replace('{files}', html(files, dir, icons, view))
.replace('{directory}', dir)
.replace('{linked-path}', htmlPath(dir));
var buf = new Buffer(str, 'utf8'); if (showUp) {
res.setHeader('Content-Type', 'text/html; charset=utf-8'); files.unshift('..');
res.setHeader('Content-Length', buf.length); }
res.end(buf);
// stat all files
stat(path, files, function (err, fileList) {
if (err) return next(err);
// sort file list
fileList.sort(fileSort);
// read stylesheet
fs.readFile(stylesheet, 'utf8', function (err, style) {
if (err) return next(err);
// create locals for rendering
var locals = {
directory: dir,
displayIcons: Boolean(icons),
fileList: fileList,
path: path,
style: style,
viewName: view
};
// render html
render(locals, function (err, body) {
if (err) return next(err);
send(res, 'text/html', body)
}); });
}); });
}); });
@@ -174,74 +217,242 @@ exports.html = function(req, res, files, next, dir, showUp, icons, path, view, t
* Respond with application/json. * Respond with application/json.
*/ */
exports.json = function(req, res, files){ serveIndex.json = function _json (req, res, files, next, dir, showUp, icons, path) {
var body = JSON.stringify(files); // stat all files
var buf = new Buffer(body, 'utf8'); stat(path, files, function (err, fileList) {
if (err) return next(err)
res.setHeader('Content-Type', 'application/json; charset=utf-8'); // sort file list
res.setHeader('Content-Length', buf.length); fileList.sort(fileSort)
res.end(buf);
// serialize
var body = JSON.stringify(fileList.map(function (file) {
return file.name
}))
send(res, 'application/json', body)
})
}; };
/** /**
* Respond with text/plain. * Respond with text/plain.
*/ */
exports.plain = function(req, res, files){ serveIndex.plain = function _plain (req, res, files, next, dir, showUp, icons, path) {
var body = files.join('\n') + '\n'; // stat all files
var buf = new Buffer(body, 'utf8'); stat(path, files, function (err, fileList) {
if (err) return next(err)
res.setHeader('Content-Type', 'text/plain; charset=utf-8'); // sort file list
res.setHeader('Content-Length', buf.length); fileList.sort(fileSort)
res.end(buf);
// serialize
var body = fileList.map(function (file) {
return file.name
}).join('\n') + '\n'
send(res, 'text/plain', body)
})
}; };
/** /**
* Generate an `Error` from the given status `code` * Map html `files`, returning an html unordered list.
* and optional `msg`. * @private
*
* @param {Number} code
* @param {String} msg
* @return {Error}
* @api private
*/ */
function createError(code, msg) { function createHtmlFileList(files, dir, useIcons, view) {
var err = new Error(msg || http.STATUS_CODES[code]); var html = '<ul id="files" class="view-' + escapeHtml(view) + '">'
err.status = code; + (view === 'details' ? (
return err; '<li class="header">'
}; + '<span class="name">Name</span>'
+ '<span class="size">Size</span>'
+ '<span class="date">Modified</span>'
+ '</li>') : '');
html += files.map(function (file) {
var classes = [];
var isDir = file.stat && file.stat.isDirectory();
var path = dir.split('/').map(function (c) { return encodeURIComponent(c); });
if (useIcons) {
classes.push('icon');
if (isDir) {
classes.push('icon-directory');
} else {
var ext = extname(file.name);
var icon = iconLookup(file.name);
classes.push('icon');
classes.push('icon-' + ext.substring(1));
if (classes.indexOf(icon.className) === -1) {
classes.push(icon.className);
}
}
}
path.push(encodeURIComponent(file.name));
var date = file.stat && file.name !== '..'
? file.stat.mtime.toLocaleDateString() + ' ' + file.stat.mtime.toLocaleTimeString()
: '';
var size = file.stat && !isDir
? file.stat.size
: '';
return '<li><a href="'
+ escapeHtml(normalizeSlashes(normalize(path.join('/'))))
+ '" class="' + escapeHtml(classes.join(' ')) + '"'
+ ' title="' + escapeHtml(file.name) + '">'
+ '<span class="name">' + escapeHtml(file.name) + '</span>'
+ '<span class="size">' + escapeHtml(size) + '</span>'
+ '<span class="date">' + escapeHtml(date) + '</span>'
+ '</a></li>';
}).join('\n');
html += '</ul>';
return html;
}
/**
* Create function to render html.
*/
function createHtmlRender(template) {
return function render(locals, callback) {
// read template
fs.readFile(template, 'utf8', function (err, str) {
if (err) return callback(err);
var body = str
.replace(/\{style\}/g, locals.style.concat(iconStyle(locals.fileList, locals.displayIcons)))
.replace(/\{files\}/g, createHtmlFileList(locals.fileList, locals.directory, locals.displayIcons, locals.viewName))
.replace(/\{directory\}/g, escapeHtml(locals.directory))
.replace(/\{linked-path\}/g, htmlPath(locals.directory));
callback(null, body);
});
};
}
/** /**
* Sort function for with directories first. * Sort function for with directories first.
*/ */
function fileSort(a, b) { function fileSort(a, b) {
// sort ".." to the top
if (a.name === '..' || b.name === '..') {
return a.name === b.name ? 0
: a.name === '..' ? -1 : 1;
}
return Number(b.stat && b.stat.isDirectory()) - Number(a.stat && a.stat.isDirectory()) || return Number(b.stat && b.stat.isDirectory()) - Number(a.stat && a.stat.isDirectory()) ||
String(a.name).toLocaleLowerCase().localeCompare(String(b.name).toLocaleLowerCase()); String(a.name).toLocaleLowerCase().localeCompare(String(b.name).toLocaleLowerCase());
} }
/**
* Get the requested directory from request.
*
* @param req
* @return {string}
* @api private
*/
function getRequestedDir (req) {
try {
return decodeURIComponent(parseUrl(req).pathname)
} catch (e) {
return null
}
}
/** /**
* Map html `dir`, returning a linked path. * Map html `dir`, returning a linked path.
*/ */
function htmlPath(dir) { function htmlPath(dir) {
var curr = []; var parts = dir.split('/');
return dir.split('/').map(function(part){ var crumb = new Array(parts.length);
curr.push(encodeURIComponent(part));
return part ? '<a href="' + curr.join('/') + '">' + part + '</a>' : ''; for (var i = 0; i < parts.length; i++) {
}).join(' / '); var part = parts[i];
if (part) {
parts[i] = encodeURIComponent(part);
crumb[i] = '<a href="' + escapeHtml(parts.slice(0, i + 1).join('/')) + '">' + escapeHtml(part) + '</a>';
}
}
return crumb.join(' / ');
}
/**
* Get the icon data for the file name.
*/
function iconLookup(filename) {
var ext = extname(filename);
// try by extension
if (icons[ext]) {
return {
className: 'icon-' + ext.substring(1),
fileName: icons[ext]
};
}
var mimetype = mime.lookup(ext);
// default if no mime type
if (mimetype === false) {
return {
className: 'icon-default',
fileName: icons.default
};
}
// try by mime type
if (icons[mimetype]) {
return {
className: 'icon-' + mimetype.replace('/', '-').replace('+', '_'),
fileName: icons[mimetype]
};
}
var suffix = mimetype.split('+')[1];
if (suffix && icons['+' + suffix]) {
return {
className: 'icon-' + suffix,
fileName: icons['+' + suffix]
};
}
var type = mimetype.split('/')[0];
// try by type only
if (icons[type]) {
return {
className: 'icon-' + type,
fileName: icons[type]
};
}
return {
className: 'icon-default',
fileName: icons.default
};
} }
/** /**
* Load icon images, return css string. * Load icon images, return css string.
*/ */
function iconStyle (files, useIcons) { function iconStyle(files, useIcons) {
if (!useIcons) return ''; if (!useIcons) return '';
var className;
var i; var i;
var icon;
var list = []; var list = [];
var rules = {}; var rules = {};
var selector; var selector;
@@ -251,78 +462,33 @@ function iconStyle (files, useIcons) {
for (i = 0; i < files.length; i++) { for (i = 0; i < files.length; i++) {
var file = files[i]; var file = files[i];
var isDir = '..' == file.name || (file.stat && file.stat.isDirectory()); var isDir = file.stat && file.stat.isDirectory();
icon = isDir ? icons.folder : icons[extname(file.name)] || icons.default; var icon = isDir
? { className: 'icon-directory', fileName: icons.folder }
: iconLookup(file.name);
var iconName = icon.fileName;
var ext = extname(file.name); selector = '#files .' + icon.className + ' .name';
className = 'icon-' + (isDir ? 'directory' : (icons[ext] ? ext.substring(1) : 'default'));
selector = '#files .' + className + ' .name';
if (!rules[icon]) { if (!rules[iconName]) {
rules[icon] = 'background-image: url(data:image/png;base64,' + load(icon) + ');' rules[iconName] = 'background-image: url(data:image/png;base64,' + load(iconName) + ');'
selectors[icon] = []; selectors[iconName] = [];
list.push(icon); list.push(iconName);
} }
if (!~selectors[icon].indexOf(selector)) { if (selectors[iconName].indexOf(selector) === -1) {
selectors[icon].push(selector); selectors[iconName].push(selector);
} }
} }
for (i = 0; i < list.length; i++) { for (i = 0; i < list.length; i++) {
icon = list[i]; iconName = list[i];
style += selectors[icon].join(',\n') + ' {\n ' + rules[icon] + '\n}\n'; style += selectors[iconName].join(',\n') + ' {\n ' + rules[iconName] + '\n}\n';
} }
return style; return style;
} }
/**
* Map html `files`, returning an html unordered list.
*/
function html(files, dir, useIcons, view) {
return '<ul id="files" class="view-'+view+'">'
+ (view == 'details' ? (
'<li class="header">'
+ '<span class="name">Name</span>'
+ '<span class="size">Size</span>'
+ '<span class="date">Modified</span>'
+ '</li>') : '')
+ files.map(function(file){
var isDir = '..' == file.name || (file.stat && file.stat.isDirectory())
, classes = []
, path = dir.split('/').map(function (c) { return encodeURIComponent(c); });
if (useIcons) {
var ext = extname(file.name);
ext = isDir ? '.directory' : (icons[ext] ? ext : '.default');
classes.push('icon');
classes.push('icon-' + ext.substring(1));
}
path.push(encodeURIComponent(file.name));
var date = file.stat && file.name !== '..'
? file.stat.mtime.toDateString() + ' ' + file.stat.mtime.toLocaleTimeString()
: '';
var size = file.stat && !isDir
? file.stat.size
: '';
return '<li><a href="'
+ normalizeSlashes(normalize(path.join('/')))
+ '" class="'
+ classes.join(' ') + '"'
+ ' title="' + file.name + '">'
+ '<span class="name">'+file.name+'</span>'
+ '<span class="size">'+size+'</span>'
+ '<span class="date">'+date+'</span>'
+ '</a></li>';
}).join('\n') + '</ul>';
}
/** /**
* Load and cache the given `icon`. * Load and cache the given `icon`.
* *
@@ -360,13 +526,34 @@ function normalizeSlashes(path) {
function removeHidden(files) { function removeHidden(files) {
return files.filter(function(file){ return files.filter(function(file){
return '.' != file[0]; return file[0] !== '.'
}); });
} }
/** /**
* Stat all files and return array of stat * Send a response.
* in same order. * @private
*/
function send (res, type, body) {
// security header for content sniffing
res.setHeader('X-Content-Type-Options', 'nosniff')
// standard headers
res.setHeader('Content-Type', type + '; charset=utf-8')
res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))
// body
res.end(body, 'utf8')
}
/**
* Stat all files and return array of objects in the form
* `{ name, stat }`.
*
* @param {Array} files
* @return {Array}
* @api private
*/ */
function stat(dir, files, cb) { function stat(dir, files, cb) {
@@ -377,12 +564,13 @@ function stat(dir, files, cb) {
files.forEach(function(file){ files.forEach(function(file){
batch.push(function(done){ batch.push(function(done){
fs.stat(join(dir, file), function(err, stat){ fs.stat(join(dir, file), function(err, stat){
if (err && err.code !== 'ENOENT') { if (err && err.code !== 'ENOENT') return done(err);
// pass ENOENT as null stat, not error
return done(err);
}
done(null, stat || null); // pass ENOENT as null stat, not error
done(null, {
name: file,
stat: stat || null
})
}); });
}); });
}); });
@@ -395,125 +583,110 @@ function stat(dir, files, cb) {
*/ */
var icons = { var icons = {
'.js': 'page_white_code_red.png' // base icons
, '.json': 'page_white_code.png' 'default': 'page_white.png',
, '.c': 'page_white_c.png' 'folder': 'folder.png',
, '.h': 'page_white_h.png'
, '.cc': 'page_white_cplusplus.png'
, '.php': 'page_white_php.png'
, '.rb': 'page_white_ruby.png'
, '.erb': 'page_white_ruby.png'
, '.cpp': 'page_white_cplusplus.png'
, '.as': 'page_white_actionscript.png'
, '.cfm': 'page_white_coldfusion.png'
, '.cs': 'page_white_csharp.png'
, '.java': 'page_white_cup.png'
, '.jsp': 'page_white_cup.png'
, '.dll': 'page_white_gear.png'
, '.ini': 'page_white_gear.png'
, '.asp': 'page_white_code.png'
, '.aspx': 'page_white_code.png'
, '.clj': 'page_white_code.png'
, '.css': 'page_white_code.png'
, '.sass': 'page_white_code.png'
, '.scss': 'page_white_code.png'
, '.less': 'page_white_code.png'
, '.htm': 'page_white_code.png'
, '.html': 'page_white_code.png'
, '.xhtml': 'page_white_code.png'
, '.lua': 'page_white_code.png'
, '.m': 'page_white_code.png'
, '.pl': 'page_white_code.png'
, '.py': 'page_white_code.png'
, '.vb': 'page_white_code.png'
, '.vbs': 'page_white_code.png'
, '.xml': 'page_white_code.png'
, '.yaws': 'page_white_code.png'
, '.map': 'map.png'
, '.app': 'application_xp.png' // generic mime type icons
, '.exe': 'application_xp.png' 'font': 'font.png',
, '.bat': 'application_xp_terminal.png' 'image': 'image.png',
, '.cgi': 'application_xp_terminal.png' 'text': 'page_white_text.png',
, '.sh': 'application_xp_terminal.png' 'video': 'film.png',
, '.avi': 'film.png' // generic mime suffix icons
, '.flv': 'film.png' '+json': 'page_white_code.png',
, '.mv4': 'film.png' '+xml': 'page_white_code.png',
, '.mov': 'film.png' '+zip': 'box.png',
, '.mp4': 'film.png'
, '.mpeg': 'film.png'
, '.mpg': 'film.png'
, '.ogv': 'film.png'
, '.rm': 'film.png'
, '.webm': 'film.png'
, '.wmv': 'film.png'
, '.fnt': 'font.png'
, '.otf': 'font.png'
, '.ttf': 'font.png'
, '.woff': 'font.png'
, '.bmp': 'image.png'
, '.gif': 'image.png'
, '.ico': 'image.png'
, '.jpeg': 'image.png'
, '.jpg': 'image.png'
, '.png': 'image.png'
, '.psd': 'page_white_picture.png'
, '.xcf': 'page_white_picture.png'
, '.pdf': 'page_white_acrobat.png'
, '.swf': 'page_white_flash.png'
, '.ai': 'page_white_vector.png'
, '.eps': 'page_white_vector.png'
, '.ps': 'page_white_vector.png'
, '.svg': 'page_white_vector.png'
, '.ods': 'page_white_excel.png' // specific mime type icons
, '.xls': 'page_white_excel.png' 'application/javascript': 'page_white_code_red.png',
, '.xlsx': 'page_white_excel.png' 'application/json': 'page_white_code.png',
, '.odp': 'page_white_powerpoint.png' 'application/msword': 'page_white_word.png',
, '.ppt': 'page_white_powerpoint.png' 'application/pdf': 'page_white_acrobat.png',
, '.pptx': 'page_white_powerpoint.png' 'application/postscript': 'page_white_vector.png',
, '.md': 'page_white_text.png' 'application/rtf': 'page_white_word.png',
, '.srt': 'page_white_text.png' 'application/vnd.ms-excel': 'page_white_excel.png',
, '.txt': 'page_white_text.png' 'application/vnd.ms-powerpoint': 'page_white_powerpoint.png',
, '.doc': 'page_white_word.png' 'application/vnd.oasis.opendocument.presentation': 'page_white_powerpoint.png',
, '.docx': 'page_white_word.png' 'application/vnd.oasis.opendocument.spreadsheet': 'page_white_excel.png',
, '.odt': 'page_white_word.png' 'application/vnd.oasis.opendocument.text': 'page_white_word.png',
, '.rtf': 'page_white_word.png' 'application/x-7z-compressed': 'box.png',
'application/x-sh': 'application_xp_terminal.png',
'application/x-msaccess': 'page_white_database.png',
'application/x-shockwave-flash': 'page_white_flash.png',
'application/x-sql': 'page_white_database.png',
'application/x-tar': 'box.png',
'application/x-xz': 'box.png',
'application/xml': 'page_white_code.png',
'application/zip': 'box.png',
'image/svg+xml': 'page_white_vector.png',
'text/css': 'page_white_code.png',
'text/html': 'page_white_code.png',
'text/less': 'page_white_code.png',
, '.dmg': 'drive.png' // other, extension-specific icons
, '.iso': 'cd.png' '.accdb': 'page_white_database.png',
, '.7z': 'box.png' '.apk': 'box.png',
, '.apk': 'box.png' '.app': 'application_xp.png',
, '.bz2': 'box.png' '.as': 'page_white_actionscript.png',
, '.cab': 'box.png' '.asp': 'page_white_code.png',
, '.deb': 'box.png' '.aspx': 'page_white_code.png',
, '.gz': 'box.png' '.bat': 'application_xp_terminal.png',
, '.jar': 'box.png' '.bz2': 'box.png',
, '.lz': 'box.png' '.c': 'page_white_c.png',
, '.lzma': 'box.png' '.cab': 'box.png',
, '.msi': 'box.png' '.cfm': 'page_white_coldfusion.png',
, '.pkg': 'box.png' '.clj': 'page_white_code.png',
, '.rar': 'box.png' '.cc': 'page_white_cplusplus.png',
, '.rpm': 'box.png' '.cgi': 'application_xp_terminal.png',
, '.tar': 'box.png' '.cpp': 'page_white_cplusplus.png',
, '.tbz2': 'box.png' '.cs': 'page_white_csharp.png',
, '.tgz': 'box.png' '.db': 'page_white_database.png',
, '.tlz': 'box.png' '.dbf': 'page_white_database.png',
, '.xz': 'box.png' '.deb': 'box.png',
, '.zip': 'box.png' '.dll': 'page_white_gear.png',
'.dmg': 'drive.png',
, '.accdb': 'page_white_database.png' '.docx': 'page_white_word.png',
, '.db': 'page_white_database.png' '.erb': 'page_white_ruby.png',
, '.dbf': 'page_white_database.png' '.exe': 'application_xp.png',
, '.mdb': 'page_white_database.png' '.fnt': 'font.png',
, '.pdb': 'page_white_database.png' '.gam': 'controller.png',
, '.sql': 'page_white_database.png' '.gz': 'box.png',
'.h': 'page_white_h.png',
, '.gam': 'controller.png' '.ini': 'page_white_gear.png',
, '.rom': 'controller.png' '.iso': 'cd.png',
, '.sav': 'controller.png' '.jar': 'box.png',
'.java': 'page_white_cup.png',
, 'folder': 'folder.png' '.jsp': 'page_white_cup.png',
, 'default': 'page_white.png' '.lua': 'page_white_code.png',
'.lz': 'box.png',
'.lzma': 'box.png',
'.m': 'page_white_code.png',
'.map': 'map.png',
'.msi': 'box.png',
'.mv4': 'film.png',
'.pdb': 'page_white_database.png',
'.php': 'page_white_php.png',
'.pl': 'page_white_code.png',
'.pkg': 'box.png',
'.pptx': 'page_white_powerpoint.png',
'.psd': 'page_white_picture.png',
'.py': 'page_white_code.png',
'.rar': 'box.png',
'.rb': 'page_white_ruby.png',
'.rm': 'film.png',
'.rom': 'controller.png',
'.rpm': 'box.png',
'.sass': 'page_white_code.png',
'.sav': 'controller.png',
'.scss': 'page_white_code.png',
'.srt': 'page_white_text.png',
'.tbz2': 'box.png',
'.tgz': 'box.png',
'.tlz': 'box.png',
'.vb': 'page_white_code.png',
'.vbs': 'page_white_code.png',
'.xcf': 'page_white_picture.png',
'.xlsx': 'page_white_excel.png',
'.yaws': 'page_white_code.png'
}; };
+28 -10
View File
@@ -1,26 +1,44 @@
{ {
"name": "serve-index", "name": "serve-index",
"description": "Serve directory listings", "description": "Serve directory listings",
"version": "1.1.5", "version": "1.9.1",
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>", "author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
"license": "MIT", "license": "MIT",
"repository": "expressjs/serve-index", "repository": "expressjs/serve-index",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
},
"dependencies": { "dependencies": {
"accepts": "~1.0.7", "accepts": "~1.3.7",
"batch": "0.5.1" "batch": "0.6.1",
"debug": "2.6.9",
"escape-html": "~1.0.3",
"http-errors": "~1.8.0",
"mime-types": "~2.1.29",
"parseurl": "~1.3.3"
}, },
"devDependencies": { "devDependencies": {
"istanbul": "0.3.0", "after": "0.8.2",
"mocha": "~1.21.1", "eslint": "7.23.0",
"should": "~4.0.0", "eslint-plugin-markdown": "2.0.0",
"supertest": "~0.13.0" "mocha": "10.4.0",
"nyc": "15.1.0",
"supertest": "7.0.0"
}, },
"files": [
"public/",
"LICENSE",
"HISTORY.md",
"index.js"
],
"engines": { "engines": {
"node": ">= 0.8.0" "node": ">= 0.8.0"
}, },
"scripts": { "scripts": {
"test": "mocha --reporter dot", "lint": "eslint .",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot", "test": "mocha --reporter spec --bail --check-leaks test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec" "test-ci": "nyc --reporter=lcov --reporter=text npm test",
"test-cov": "nyc --reporter=html --reporter=text npm test"
} }
} }
+4 -4
View File
@@ -52,11 +52,11 @@
} }
function search() { function search() {
var str = $('search').value var str = $('search').value.toLowerCase();
, links = $('files').all('a'); var links = $('files').all('a');
links.each(function(link){ links.each(function(link){
var text = link.textContent; var text = link.textContent.toLowerCase();
if ('..' == text) return; if ('..' == text) return;
if (str.length && ~text.indexOf(str)) { if (str.length && ~text.indexOf(str)) {
@@ -75,7 +75,7 @@
<body class="directory"> <body class="directory">
<input id="search" type="text" placeholder="Search" autocomplete="off" /> <input id="search" type="text" placeholder="Search" autocomplete="off" />
<div id="wrapper"> <div id="wrapper">
<h1>{linked-path}</h1> <h1><a href="/">~</a>{linked-path}</h1>
{files} {files}
</div> </div>
</body> </body>
+1 -1
View File
@@ -249,7 +249,7 @@ ul#files.view-details li.header {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
text-indent: 0; text-indent: 0;
background-position: 0 0; background-position: 0 50%;
} }
#files .icon .name { #files .icon .name {
text-indent: 41px; text-indent: 41px;
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
+1
View File
@@ -0,0 +1 @@
ferret
-26
View File
@@ -1,26 +0,0 @@
var bytes = require('bytes');
exports['default request body'] = function(app){
it('should default to {}', function(done){
app.request()
.post('/')
.end(function(res){
res.body.should.equal('{}');
done();
})
})
};
exports['limit body to'] = function(size, type, app){
it('should accept a limit option', function(done){
app.request()
.post('/')
.set('Content-Length', bytes(size) + 1)
.set('Content-Type', type)
.end(function(res){
res.should.have.status(413);
done();
})
})
}
+1
View File
@@ -6,6 +6,7 @@
</head> </head>
<body class="directory"> <body class="directory">
<h1>This is the test template</h1> <h1>This is the test template</h1>
<h2>directory {directory}</h2>
<div id="wrapper"> <div id="wrapper">
<h1>{linked-path}</h1> <h1>{linked-path}</h1>
{files} {files}
+390 -109
View File
@@ -1,13 +1,20 @@
var after = require('after');
var assert = require('assert');
var http = require('http'); var http = require('http');
var fs = require('fs'); var fs = require('fs');
var path = require('path');
var request = require('supertest'); var request = require('supertest');
var should = require('should');
var serveIndex = require('..'); var serveIndex = require('..');
var fixtures = path.join(__dirname, '/fixtures');
var relative = path.relative(process.cwd(), fixtures);
var skipRelative = ~relative.indexOf('..') || path.resolve(relative) === relative;
describe('serveIndex(root)', function () { describe('serveIndex(root)', function () {
it('should require root', function () { it('should require root', function () {
serveIndex.should.throw(/root path required/) assert.throws(serveIndex, /root path required/)
}) })
it('should serve text/html without Accept header', function (done) { it('should serve text/html without Accept header', function (done) {
@@ -19,6 +26,15 @@ describe('serveIndex(root)', function () {
.expect(200, done) .expect(200, done)
}) })
it('should include security header', function (done) {
var server = createServer()
request(server)
.get('/')
.expect('X-Content-Type-Options', 'nosniff')
.expect(200, done)
})
it('should serve a directory index', function (done) { it('should serve a directory index', function (done) {
var server = createServer() var server = createServer()
@@ -32,7 +48,9 @@ describe('serveIndex(root)', function () {
request(server) request(server)
.head('/') .head('/')
.expect(200, '', done) .expect(200)
.expect(shouldNotHaveBody())
.end(done)
}) })
it('should work with OPTIONS requests', function (done) { it('should work with OPTIONS requests', function (done) {
@@ -60,6 +78,14 @@ describe('serveIndex(root)', function () {
.expect(400, done) .expect(400, done)
}) })
it('should deny path that does not decode', function (done) {
var server = createServer()
request(server)
.head('/%FF')
.expect(400, done)
})
it('should deny path outside root', function (done) { it('should deny path outside root', function (done) {
var server = createServer() var server = createServer()
@@ -77,11 +103,11 @@ describe('serveIndex(root)', function () {
}) })
it('should treat an ENAMETOOLONG as a 414', function (done) { it('should treat an ENAMETOOLONG as a 414', function (done) {
var path = Array(11000).join('foobar') var dir = path.join(fixtures, Array(10000).join('/foobar'))
var server = createServer() var server = createServer(dir)
request(server) request(server)
.get('/' + path) .get('/')
.expect(414, done) .expect(414, done)
}) })
@@ -102,7 +128,7 @@ describe('serveIndex(root)', function () {
.get('/') .get('/')
.set('Accept', 'application/json') .set('Accept', 'application/json')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(/g# %3 o %2525 %37 dir/) .expect(/g# %3 o & %2525 %37 dir/)
.expect(/users/) .expect(/users/)
.expect(/file #1\.txt/) .expect(/file #1\.txt/)
.expect(/nums/) .expect(/nums/)
@@ -110,6 +136,36 @@ describe('serveIndex(root)', function () {
.expect(/さくら\.txt/) .expect(/さくら\.txt/)
.expect(200, done) .expect(200, done)
}); });
it('should include security header', function (done) {
var server = createServer()
request(server)
.get('/')
.set('Accept', 'application/json')
.expect('X-Content-Type-Options', 'nosniff')
.expect(200, done)
})
it('should sort folders first', function (done) {
request(createServer())
.get('/')
.set('Accept', 'application/json')
.expect(200)
.expect('Content-Type', 'application/json; charset=utf-8')
.expect([
'#directory',
'collect',
'g# %3 o & %2525 %37 dir',
'users',
'file #1.txt',
'foo & bar',
'nums',
'todo.txt',
'さくら.txt'
])
.end(done)
})
}); });
describe('when Accept: text/html is given', function () { describe('when Accept: text/html is given', function () {
@@ -121,12 +177,36 @@ describe('serveIndex(root)', function () {
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(200) .expect(200)
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir"/) .expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir"/)
.expect(/<a href="\/users"/) .expect(/<a href="\/users"/)
.expect(/<a href="\/file%20%231.txt"/) .expect(/<a href="\/file%20%231.txt"/)
.expect(/<a href="\/todo.txt"/) .expect(/<a href="\/todo.txt"/)
.expect(/<a href="\/%E3%81%95%E3%81%8F%E3%82%89\.txt"/) .expect(/<a href="\/%E3%81%95%E3%81%8F%E3%82%89\.txt"/)
.end(done); .end(done)
});
it('should include security header', function (done) {
var server = createServer()
request(server)
.get('/')
.set('Accept', 'text/html')
.expect('X-Content-Type-Options', 'nosniff')
.expect(200, done)
})
it('should property escape file names', function (done) {
var server = createServer()
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200)
.expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/foo%20%26%20bar"/)
.expect(/foo &amp; bar/)
.expect(bodyDoesNotContain('foo & bar'))
.end(done)
}); });
it('should sort folders first', function (done) { it('should sort folders first', function (done) {
@@ -137,21 +217,23 @@ describe('serveIndex(root)', function () {
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(200) .expect(200)
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.end(function (err, res) { .expect(function (res) {
if (err) throw err; var urls = res.text
var urls = res.text.split(/<a href="([^"]*)"/).filter(function(s, i){ return i%2; }); .split('</h1>')[1]
urls.should.eql([ .split(/<a href="([^"]*)"/).filter(function (s, i) { return i % 2 })
assert.deepEqual(urls, [
'/%23directory', '/%23directory',
'/g%23%20%253%20o%20%252525%20%2537%20dir', '/collect',
'/g%23%20%253%20o%20%26%20%252525%20%2537%20dir',
'/users', '/users',
'/file%20%231.txt', '/file%20%231.txt',
'/foo%20bar', '/foo%20%26%20bar',
'/nums', '/nums',
'/todo.txt', '/todo.txt',
'/%E3%81%95%E3%81%8F%E3%82%89.txt' '/%E3%81%95%E3%81%8F%E3%82%89.txt'
]); ])
done(); })
}); .end(done)
}); });
}); });
@@ -165,12 +247,43 @@ describe('serveIndex(root)', function () {
.expect(200) .expect(200)
.expect('Content-Type', 'text/plain; charset=utf-8') .expect('Content-Type', 'text/plain; charset=utf-8')
.expect(/users/) .expect(/users/)
.expect(/g# %3 o %2525 %37 dir/) .expect(/g# %3 o & %2525 %37 dir/)
.expect(/file #1.txt/) .expect(/file #1.txt/)
.expect(/todo.txt/) .expect(/todo.txt/)
.expect(/さくら\.txt/) .expect(/さくら\.txt/)
.end(done); .end(done)
}); });
it('should include security header', function (done) {
var server = createServer()
request(server)
.get('/')
.set('Accept', 'text/plain')
.expect('X-Content-Type-Options', 'nosniff')
.expect(200, done)
})
it('should sort folders first', function (done) {
request(createServer())
.get('/')
.set('Accept', 'text/plain')
.expect(200)
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect([
'#directory',
'collect',
'g# %3 o & %2525 %37 dir',
'users',
'file #1.txt',
'foo & bar',
'nums',
'todo.txt',
'さくら.txt',
''
].join('\n'))
.end(done)
})
}); });
describe('when Accept: application/x-bogus is given', function () { describe('when Accept: application/x-bogus is given', function () {
@@ -191,11 +304,8 @@ describe('serveIndex(root)', function () {
request(server) request(server)
.get('/') .get('/')
.expect(200, function (err, res) { .expect(bodyDoesNotContain('.hidden'))
if (err) return done(err) .expect(200, done)
res.text.should.not.containEql('.hidden')
done()
});
}); });
it('should filter hidden files', function (done) { it('should filter hidden files', function (done) {
@@ -203,11 +313,8 @@ describe('serveIndex(root)', function () {
request(server) request(server)
.get('/') .get('/')
.expect(200, function (err, res) { .expect(bodyDoesNotContain('.hidden'))
if (err) return done(err) .expect(200, done)
res.text.should.not.containEql('.hidden')
done()
});
}); });
it('should not filter hidden files', function (done) { it('should not filter hidden files', function (done) {
@@ -221,64 +328,216 @@ describe('serveIndex(root)', function () {
describe('with "filter" option', function () { describe('with "filter" option', function () {
it('should custom filter files', function (done) { it('should custom filter files', function (done) {
var seen = false var cb = after(2, done)
var server = createServer('test/fixtures', {'filter': filter}) var server = createServer(fixtures, {'filter': filter})
function filter(name) { function filter(name) {
if (name.indexOf('foo') === -1) return true if (name.indexOf('foo') === -1) return true
seen = true cb()
return false return false
} }
request(server) request(server)
.get('/') .get('/')
.expect(200, function (err, res) { .expect(bodyDoesNotContain('foo'))
if (err) return done(err) .expect(200, cb)
seen.should.be.true
res.text.should.not.containEql('foo')
done()
});
}); });
it('should filter after hidden filter', function (done) { it('should filter after hidden filter', function (done) {
var seen = false var server = createServer(fixtures, {'filter': filter, 'hidden': false})
var server = createServer('test/fixtures', {'filter': filter, 'hidden': false})
function filter(name) { function filter(name) {
seen = seen || name.indexOf('.') === 0 if (name.indexOf('.') === 0) {
done(new Error('unexpected hidden file'))
}
return true return true
} }
request(server) request(server)
.get('/') .get('/')
.expect(200, function (err, res) { .expect(200, done)
if (err) return done(err)
seen.should.be.false
done()
}); });
it('should filter directory paths', function (done) {
var cb = after(4, done)
var server = createServer(fixtures, {'filter': filter})
function filter(name, index, list, dir) {
if (path.normalize(dir) === path.normalize(path.join(fixtures, '/users'))) {
cb()
}
return true
}
request(server)
.get('/users')
.expect(200, cb)
}); });
}); });
describe('with "icons" option', function () { describe('with "icons" option', function () {
it('should include icons for html', function (done) { it('should include icons for html', function (done) {
var server = createServer('test/fixtures', {'icons': true}) var server = createServer(fixtures, {'icons': true})
request(server) request(server)
.get('/') .get('/collect')
.expect(/data:image\/png/) .expect(/data:image\/png/)
.expect(/icon-default/) .expect(/icon-default/)
.expect(/icon-directory/) .expect(/icon-directory/)
.expect(/icon-image/)
.expect(/icon-image-svg_xml/)
.expect(/icon-txt/) .expect(/icon-txt/)
.expect(/icon-application-pdf/)
.expect(/icon-video/)
.expect(/icon-xml/)
.expect(200, done) .expect(200, done)
}); });
}); });
describe('with "template" option', function () {
describe('when setting a custom template file', function () {
var server;
before(function () {
server = createServer(fixtures, {'template': __dirname + '/shared/template.html'});
});
it('should respond with file list', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir"/)
.expect(/<a href="\/users"/)
.expect(/<a href="\/file%20%231.txt"/)
.expect(/<a href="\/todo.txt"/)
.expect(200, done)
});
it('should respond with testing template sentence', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, /This is the test template/, done)
});
it('should have default styles', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, /ul#files/, done)
});
it('should list directory twice', function (done) {
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(function (res) {
var occurances = res.text.match(/directory \/users\//g)
if (occurances && occurances.length === 2) return
throw new Error('directory not listed twice')
})
.expect(200, done)
});
});
describe('when setting a custom template function', function () {
it('should invoke function to render', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, 'This is a template.');
}});
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, 'This is a template.', done)
});
it('should handle render errors', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(new Error('boom!'));
}});
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(500, 'boom!', done)
});
it('should provide "directory" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.directory));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, '"/users/"', done)
});
it('should provide "displayIcons" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.displayIcons));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, 'false', done)
});
it('should provide "fileList" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.fileList.map(function (file) {
file.stat = file.stat instanceof fs.Stats;
return file;
})));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect('[{"name":"..","stat":true},{"name":"#dir","stat":true},{"name":"index.html","stat":true},{"name":"tobi.txt","stat":true}]')
.expect(200, done)
});
it('should provide "path" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.path));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, JSON.stringify(path.join(fixtures, 'users/')), done)
});
it('should provide "style" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.style));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, /#files \.icon \.name/, done)
});
it('should provide "viewName" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.viewName));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, '"tiles"', done)
});
});
});
describe('when using custom handler', function () { describe('when using custom handler', function () {
describe('exports.html', function () { describe('exports.html', function () {
var orig = serveIndex.html alterProperty(serveIndex, 'html', serveIndex.html)
after(function () {
serveIndex.html = orig
})
it('should get called with Accept: text/html', function (done) { it('should get called with Accept: text/html', function (done) {
var server = createServer() var server = createServer()
@@ -373,10 +632,7 @@ describe('serveIndex(root)', function () {
}); });
describe('exports.plain', function () { describe('exports.plain', function () {
var orig = serveIndex.plain alterProperty(serveIndex, 'plain', serveIndex.plain)
after(function () {
serveIndex.plain = orig
})
it('should get called with Accept: text/plain', function (done) { it('should get called with Accept: text/plain', function (done) {
var server = createServer() var server = createServer()
@@ -394,10 +650,7 @@ describe('serveIndex(root)', function () {
}); });
describe('exports.json', function () { describe('exports.json', function () {
var orig = serveIndex.json alterProperty(serveIndex, 'json', serveIndex.json)
after(function () {
serveIndex.json = orig
})
it('should get called with Accept: application/json', function (done) { it('should get called with Accept: application/json', function (done) {
var server = createServer() var server = createServer()
@@ -426,7 +679,27 @@ describe('serveIndex(root)', function () {
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/users\/index.html"/) .expect(/<a href="\/users\/index.html"/)
.expect(/<a href="\/users\/tobi.txt"/) .expect(/<a href="\/users\/tobi.txt"/)
.end(done); .end(done)
});
it('should include link to parent directory', function (done) {
var server = createServer()
request(server)
.get('/users')
.expect(200)
.expect(function (res) {
var urls = res.text
.split('</h1>')[1]
.split(/<a href="([^"]*)"/).filter(function (s, i) { return i % 2 })
assert.deepEqual(urls, [
'/',
'/users/%23dir',
'/users/index.html',
'/users/tobi.txt'
])
})
.end(done)
}); });
it('should work for directory with #', function (done) { it('should work for directory with #', function (done) {
@@ -439,20 +712,34 @@ describe('serveIndex(root)', function () {
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/%23directory"/) .expect(/<a href="\/%23directory"/)
.expect(/<a href="\/%23directory\/index.html"/) .expect(/<a href="\/%23directory\/index.html"/)
.end(done); .end(done)
}); });
it('should work for directory with special chars', function (done) { it('should work for directory with special chars', function (done) {
var server = createServer() var server = createServer()
request(server) request(server)
.get('/g%23%20%253%20o%20%252525%20%2537%20dir/') .get('/g%23%20%253%20o%20%26%20%252525%20%2537%20dir/')
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(200) .expect(200)
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir"/) .expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir"/)
.expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir\/empty.txt"/) .expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir\/empty.txt"/)
.end(done); .end(done)
});
it('should property escape directory names', function (done) {
var server = createServer()
request(server)
.get('/g%23%20%253%20o%20%26%20%252525%20%2537%20dir/')
.set('Accept', 'text/html')
.expect(200)
.expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir"/)
.expect(/g# %3 o &amp; %2525 %37 dir/)
.expect(bodyDoesNotContain('g# %3 o & %2525 %37 dir'))
.end(done)
}); });
it('should not work for outside root', function (done) { it('should not work for outside root', function (done) {
@@ -461,46 +748,14 @@ describe('serveIndex(root)', function () {
request(server) request(server)
.get('/../support/') .get('/../support/')
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(403, done); .expect(403, done)
});
});
describe('when setting a custom template', function () {
var server;
before(function () {
server = createServer('test/fixtures', {'template': __dirname + '/shared/template.html'});
});
it('should respond with file list', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir"/)
.expect(/<a href="\/users"/)
.expect(/<a href="\/file%20%231.txt"/)
.expect(/<a href="\/todo.txt"/)
.expect(200, done)
});
it('should respond with testing template sentence', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, /This is the test template/, done)
});
it('should have default styles', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, /ul#files/, done)
}); });
}); });
describe('when setting a custom stylesheet', function () { describe('when setting a custom stylesheet', function () {
var server; var server;
before(function () { before(function () {
server = createServer('test/fixtures', {'stylesheet': __dirname + '/shared/styles.css'}); server = createServer(fixtures, {'stylesheet': __dirname + '/shared/styles.css'});
}); });
it('should respond with appropriate embedded styles', function (done) { it('should respond with appropriate embedded styles', function (done) {
@@ -510,14 +765,14 @@ describe('serveIndex(root)', function () {
.expect(200) .expect(200)
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/color: #00ff00;/) .expect(/color: #00ff00;/)
.end(done); .end(done)
}); });
}); });
describe('when set with trailing slash', function () { describe('when set with trailing slash', function () {
var server; var server;
before(function () { before(function () {
server = createServer('test/fixtures/'); server = createServer(fixtures + '/');
}); });
it('should respond with file list', function (done) { it('should respond with file list', function (done) {
@@ -533,20 +788,22 @@ describe('serveIndex(root)', function () {
}); });
}); });
describe('when set to \'.\'', function () { (skipRelative ? describe.skip : describe)('when set to \'.\'', function () {
var server; var server;
before(function () { before(function () {
server = createServer('.'); server = createServer('.');
}); });
it('should respond with file list', function (done) { it('should respond with file list', function (done) {
var dest = relative.split(path.sep).join('/');
request(server) request(server)
.get('/') .get('/' + dest + '/')
.set('Accept', 'application/json') .set('Accept', 'application/json')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(/LICENSE/) .expect(/users/)
.expect(/public/) .expect(/file #1\.txt/)
.expect(/test/) .expect(/nums/)
.expect(/todo\.txt/)
.expect(200, done) .expect(200, done)
}); });
@@ -554,13 +811,25 @@ describe('serveIndex(root)', function () {
request(server) request(server)
.get('/../') .get('/../')
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(403, done); .expect(403, done)
}); });
}); });
}); });
function alterProperty(obj, prop, val) {
var prev
beforeEach(function () {
prev = obj[prop]
obj[prop] = val
})
afterEach(function () {
obj[prop] = prev
})
}
function createServer(dir, opts) { function createServer(dir, opts) {
dir = dir || 'test/fixtures' dir = dir || fixtures
var _serveIndex = serveIndex(dir, opts) var _serveIndex = serveIndex(dir, opts)
@@ -571,3 +840,15 @@ function createServer(dir, opts) {
}) })
}) })
} }
function bodyDoesNotContain(text) {
return function (res) {
assert.equal(res.text.indexOf(text), -1)
}
}
function shouldNotHaveBody () {
return function (res) {
assert.ok(res.text === '' || res.text === undefined)
}
}