diff --git a/eslint.common.config.js b/eslint.common.config.js index 7484e443eb8..6be4e9ee442 100644 --- a/eslint.common.config.js +++ b/eslint.common.config.js @@ -12,6 +12,7 @@ export default [{ "test/fixtures/", "**/docs/", "**/jsdocs/", + "packages/" ], }, js.configs.recommended, google, ava.configs["flat/recommended"], { name: "Common ESLint config used for all UI5 CLI repos", diff --git a/packages/server/.chglog/CHANGELOG.tpl.md b/packages/server/.chglog/CHANGELOG.tpl.md new file mode 100755 index 00000000000..15cc6d34bb7 --- /dev/null +++ b/packages/server/.chglog/CHANGELOG.tpl.md @@ -0,0 +1,431 @@ +# Changelog +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +{{ if .Versions -}} +A list of unreleased changes can be found [here]({{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD). +{{ end -}} + +{{ range .Versions }} + +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} +{{ range .CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [`{{ .Hash.Short }}`]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }}) +{{ end }} +{{ end -}} + +{{- if .RevertCommits -}} +### Reverts +{{ range .RevertCommits -}} +- {{ .Revert.Header }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + + +## [v3.2.1] - 2024-05-15 +### Bug Fixes +- **middleware/versionInfo:** Only process dependencies of type 'library' [`4c99455`](https://github.com/SAP/ui5-server/commit/4c99455cf21539c2d1403cca81c603ead7124351) + + + +## [v3.2.0] - 2024-04-24 +### Features +- **serveResources:** Dynamically generate missing library manifest.json [`d31f2c5`](https://github.com/SAP/ui5-server/commit/d31f2c57aa0b0c72fb033c41587ff257312838d1) + + + +## [v3.1.5] - 2023-12-12 +### Bug Fixes +- Unsafe jQuery plugin ([#618](https://github.com/SAP/ui5-server/issues/618)) [`ff1dc29`](https://github.com/SAP/ui5-server/commit/ff1dc297a9221f534173ec44e38e5a6eec6b9ba5) + + + +## [v3.1.4] - 2023-11-20 + + +## [v3.1.3] - 2023-06-06 +### Bug Fixes +- **middleware/testRunner:** Update resources from OpenUI5 [`f0c7291`](https://github.com/SAP/ui5-server/commit/f0c7291d2dc1d753e04184fdf2127c278810f0c4) + + + +## [v3.1.2] - 2023-04-12 +### Bug Fixes +- Fix JSDoc names of typedefs in MiddlewareUtil. [`dbd6fe1`](https://github.com/SAP/ui5-server/commit/dbd6fe19c229471ba2b8621b97b8f5a9bca56a78) + + + +## [v3.1.1] - 2023-03-16 +### Bug Fixes +- Allow serving of propertyfiles of non component project types [`1bc6ec7`](https://github.com/SAP/ui5-server/commit/1bc6ec72a15ae7df558c4938b2670d0e78af710b) + + + +## [v3.1.0] - 2023-03-01 +### Features +- **CSP:** Increase defaultPolicy2 to sap-target-level-3 ([#580](https://github.com/SAP/ui5-server/issues/580)) [`5a981a1`](https://github.com/SAP/ui5-server/commit/5a981a1d7f2d2aaffbcd2f68e02206a8bdb0494b) + + + +## [v3.0.1] - 2023-02-16 +### Bug Fixes +- **MiddlewareUtil:** Provide framework configuration getters to custom tasks ([#579](https://github.com/SAP/ui5-server/issues/579)) [`58bf4f5`](https://github.com/SAP/ui5-server/commit/58bf4f5953aed3a1fc6f4f4353e37fe6c3b7094f) + + + +## [v3.0.0] - 2023-02-09 +### Breaking Changes +- Transform to ES Modules ([#501](https://github.com/SAP/ui5-server/issues/501)) [`05e3013`](https://github.com/SAP/ui5-server/commit/05e3013605e28e9ab5a785aa57616473d40e5710) +- Remove "/proxy" endpoint ([#550](https://github.com/SAP/ui5-server/issues/550)) [`4bdf839`](https://github.com/SAP/ui5-server/commit/4bdf839e96f67ddbc4cb2a18216921d54df4006e) +- Require Project Graph ([#479](https://github.com/SAP/ui5-server/issues/479)) [`d62f85a`](https://github.com/SAP/ui5-server/commit/d62f85a193115a587dbf58225e8130318a475023) +- Require Node.js >= 16.18.0 / npm >= 8 [`63d216a`](https://github.com/SAP/ui5-server/commit/63d216a3ba34e8e50acc6621d43a78c3a0804d67) + +### BREAKING CHANGE +This package has been transformed to ES Modules. Therefore it no longer provides a CommonJS export. +If your project uses CommonJS, it needs to be converted to ESM or use a dynamic import. + +For more information see also: + +- https://sap.github.io/ui5-tooling/updates/migrate-v3/ +- https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +This removes the "/proxy" endpoint and the corresponding +"connectUi5Proxy" middleware from the standard ui5-server. +Internally, this middleware made use of the connect-openui5 proxy +implementation (https://github.com/SAP/connect-openui5#proxy). + +More sophisticated proxy solutions for ui5-server are already available +in the form of custom middleware extensions from the UI5-community. + +The UI5 Team might provide a dedicated custom middleware extension, +with similar functionality, in the future. + +- Server now requires a Project Graph instance instead. +- Standard middleware now rely on Project instances being available on Resources (see https://github.com/SAP/ui5-fs/pull/381) +- MiddlewareRepository#addMiddleware has been removed. Custom middleware need to be added to the project graph instead + +Support for older Node.js and npm releases has been dropped. +Only Node.js v16.18.0 and npm v8 or higher are supported. + +### Features +- **MiddlewareUtil:** Add getProject/getDependencies/resourceFactory API to interface ([#547](https://github.com/SAP/ui5-server/issues/547)) [`ab28f78`](https://github.com/SAP/ui5-server/commit/ab28f789ba929ef1319b6e562267e9717cc9937b) + + + +## [v2.4.1] - 2022-11-30 +### Dependency Updates +- Bump connect-openui5 from 0.10.2 to 0.10.3 [`20b6fc8`](https://github.com/SAP/ui5-server/commit/20b6fc8fbad69265bae3e8f7efd320f6297ac4c4) + + + +## [v2.4.0] - 2021-10-19 +### Features +- Enhance versionInfo middleware to serve sap-ui-version.json ([#420](https://github.com/SAP/ui5-server/issues/420)) [`c6f83f5`](https://github.com/SAP/ui5-server/commit/c6f83f5472eb2fe6a8d4eca10ecdc5f4b522bc3c) + + + +## [v2.3.1] - 2021-07-23 + + +## [v2.3.0] - 2021-07-01 +### Features +- **server:** Expose configuration options for SAP CSP policies [`55d6a96`](https://github.com/SAP/ui5-server/commit/55d6a96cc1a3c762af8173d9fb9588fe742a302d) + + + +## [v2.2.10] - 2021-06-01 + + +## [v2.2.9] - 2021-03-11 +### Dependency Updates +- Bump connect-openui5 from 0.10.1 to 0.10.2 [`849957d`](https://github.com/SAP/ui5-server/commit/849957d39a45c22c5c94f787d66eeccc58eb4d2d) + + + +## [v2.2.8] - 2021-02-09 + + +## [v2.2.7] - 2020-11-06 + + +## [v2.2.6] - 2020-10-22 +### Bug Fixes +- Improve parallel theme request handling [`88bc0d6`](https://github.com/SAP/ui5-server/commit/88bc0d6d4e5ca8bb191029335451713579360e1c) +- **nonReadRequests middleware:** Use response API [`2d2325f`](https://github.com/SAP/ui5-server/commit/2d2325f638820d25738ddbd56afe0d104e37f2e0) + + + +## [v2.2.5] - 2020-10-06 +### Bug Fixes +- Discovery middleware shouldn't fail when lib names overlap ([#362](https://github.com/SAP/ui5-server/issues/362)) [`f5067ce`](https://github.com/SAP/ui5-server/commit/f5067ce38b89442948ce096e5d89651e8970bdb9) + + + +## [v2.2.4] - 2020-09-10 + + +## [v2.2.3] - 2020-09-02 +### Bug Fixes +- **middleware/testRunner:** Update resources from OpenUI5 [`55b1fe7`](https://github.com/SAP/ui5-server/commit/55b1fe742be29b176e55985a0315dcead684d257) + + + +## [v2.2.2] - 2020-08-11 + + +## [v2.2.1] - 2020-07-14 +### Bug Fixes +- **MiddlewareManager:** Provide MiddlewareUtil to custom middleware using specVersion 2.1 [`3e249fa`](https://github.com/SAP/ui5-server/commit/3e249fa4333fb6afa0e512201959bfcbcee196d0) +- **Node.js API:** TypeScript type definition support ([#334](https://github.com/SAP/ui5-server/issues/334)) [`b66f9cc`](https://github.com/SAP/ui5-server/commit/b66f9cc10b35b2997f5e9b3840ef92dd504c8a33) + + + +## [v2.2.0] - 2020-07-01 +### Bug Fixes +- **MiddlewareManager:** Update SAP Target CSP Policies [`269c22c`](https://github.com/SAP/ui5-server/commit/269c22c80a6682a3d680c47e768f69a20ecabcd0) + +### Features +- **CSP:** Add ignorePaths option ([#331](https://github.com/SAP/ui5-server/issues/331)) [`27a962e`](https://github.com/SAP/ui5-server/commit/27a962eb80fd7de95c6076f7d307e0dd06dac057) + + + +## [v2.1.0] - 2020-06-15 +### Features +- **csp:** enable tracking and serving of csp reports ([#323](https://github.com/SAP/ui5-server/issues/323)) [`e0a0c5e`](https://github.com/SAP/ui5-server/commit/e0a0c5e022c2c0041c6cf52631ef834cafe1f873) + + + +## [v2.0.3] - 2020-05-14 + + +## [v2.0.2] - 2020-04-30 +### Bug Fixes +- **CSP Middleware:** Use res.getHeader/setHeader methods ([#312](https://github.com/SAP/ui5-server/issues/312)) [`c53525c`](https://github.com/SAP/ui5-server/commit/c53525ca4bb5825d241d0f137ce3912d681e6548) + + + +## [v2.0.1] - 2020-04-15 +### Dependency Updates +- Bump devcert-sanscache from 0.4.6 to 0.4.8 ([#306](https://github.com/SAP/ui5-server/issues/306)) [`2a9d517`](https://github.com/SAP/ui5-server/commit/2a9d51776e967362d959eef45ce9533a9a27650c) + + + +## [v2.0.0] - 2020-03-31 +### Breaking Changes +- Require Node.js >= 10 [`a8c7a13`](https://github.com/SAP/ui5-server/commit/a8c7a13f68426012e5ff9cfddb365bb32c46f9dc) +- **serveResources middleware:** Expect *.properties files in UTF-8 by default [`af7f9ad`](https://github.com/SAP/ui5-server/commit/af7f9ad52aa834f63c163b99eb4fbc8d1bb05079) + +### Bug Fixes +- Handle encoding in request paths correctly [`256b3f0`](https://github.com/SAP/ui5-server/commit/256b3f037880aad077b0158e3551e10ce8a3dbc7) + +### Features +- Add MiddlewareUtil providing convenience functions to all middleware [`b8ab775`](https://github.com/SAP/ui5-server/commit/b8ab775039635a25109797b92fe34358057ea5e8) +- Add test runner middleware [`ea77e20`](https://github.com/SAP/ui5-server/commit/ea77e201e20545fca7494fc581aa42adbcb2c1d7) + +### BREAKING CHANGE + +If the project a "*.properties" resource originates from cannot be +determined, or if the project does not define a +propertiesFileSourceEncoding configuration or uses a legacy specVersion +(<2.0), the serveResources middleware assumes that the resource is UTF-8 +encoded instead of ISO-8859-1. + +Support for older Node.js releases has been dropped. +Only Node.js v10 or higher is supported. + + + +## [v1.6.0] - 2020-02-24 +### Bug Fixes +- **versionInfo:** Fix pattern to glob for .library files [`3621f78`](https://github.com/SAP/ui5-server/commit/3621f7868dec891f8746ca4b66cf43c4d5d9782b) + +### Features +- **serveThemes:** Support experimental CSS variables and skeleton build ([#278](https://github.com/SAP/ui5-server/issues/278)) [`47d4b55`](https://github.com/SAP/ui5-server/commit/47d4b55986fd84ef85f4b42e9c91f16017183c16) + + + +## [v1.5.4] - 2020-02-10 +### Bug Fixes +- Ensure proper handling of multi-byte characters in streams ([#280](https://github.com/SAP/ui5-server/issues/280)) [`fe652e4`](https://github.com/SAP/ui5-server/commit/fe652e410bd0eab506fc42036ad2cfa374fa5a6c) +- **serveIndex:** Add missing dependency to "graceful-fs" [`e09c472`](https://github.com/SAP/ui5-server/commit/e09c472eb20ed3d0b914c8b2e1d5f22bb8476dca) + + + +## [v1.5.3] - 2020-01-24 +### Bug Fixes +- **sslUtils:** Fix Invalid Common Name error [`db06db7`](https://github.com/SAP/ui5-server/commit/db06db7a371ea7254c408f01cf231de9367c8b0d) + + + +## [v1.5.2] - 2019-12-16 +### Bug Fixes +- Resolve ERR_CERT_REVOKED error for newly generated SSL certs [`f2e1522`](https://github.com/SAP/ui5-server/commit/f2e15229569e68990f63cd38849eb937d2ad9cb8) + + + +## [v1.5.1] - 2019-11-19 +### Dependency Updates +- Bump connect-openui5 from 0.8.0 to 0.9.0 [`0c6d502`](https://github.com/SAP/ui5-server/commit/0c6d50263c4828f5070404ac9dfa337667b24371) + + + +## [v1.5.0] - 2019-11-07 +### Features +- **serveIndex:** use serve-index for serving the application index [`d6ea507`](https://github.com/SAP/ui5-server/commit/d6ea507bdd649653a865f01d4e076caa4313639f) + + + +## [v1.4.0] - 2019-10-24 +### Features +- **Custom Middleware Extensibility:** Allow multiple definitions of the same custom middleware ([#246](https://github.com/SAP/ui5-server/issues/246)) [`55a24ef`](https://github.com/SAP/ui5-server/commit/55a24ef134b01b43683bc21fb24b46d4e472232d) + + + +## [v1.3.0] - 2019-07-31 +### Features +- Properties File Escaping ([#214](https://github.com/SAP/ui5-server/issues/214)) [`dd4844d`](https://github.com/SAP/ui5-server/commit/dd4844d53b787dc14bc5eecae2bc5674425200b7) + + + +## [v1.2.0] - 2019-07-10 +### Features +- **Server:** Add handling for custom middleware ([#200](https://github.com/SAP/ui5-server/issues/200)) [`037b3bc`](https://github.com/SAP/ui5-server/commit/037b3bc001b86061c807e78584e69c53e89d8b96) + + + +## [v1.1.3] - 2019-06-24 +### Bug Fixes +- **serveResources:** Correctly encode non UTF-8 resources [`1ee6723`](https://github.com/SAP/ui5-server/commit/1ee6723b5e5dac653c76a5078ee4afd6af96f8ac) + + + +## [v1.1.2] - 2019-06-03 +### Bug Fixes +- **Middleware:** Allow usage without express server [`4d971b4`](https://github.com/SAP/ui5-server/commit/4d971b4babade56fef154dc4a7a524d6ffa8ad1b) + + + +## [v1.1.1] - 2019-05-13 +### Bug Fixes +- Makes CSP middleware work in an environment without express server ([#184](https://github.com/SAP/ui5-server/issues/184)) [`c3089ad`](https://github.com/SAP/ui5-server/commit/c3089adeee030f4ace899c01944006583146e32e) + + + +## [v1.1.0] - 2019-04-25 +### Dependency Updates +- Bump [@ui5](https://github.com/ui5)/fs from 1.0.1 to 1.0.2 ([#166](https://github.com/SAP/ui5-server/issues/166)) [`5ff4765`](https://github.com/SAP/ui5-server/commit/5ff476504254baf304c2cb9db83746438a10be92) +- Bump [@ui5](https://github.com/ui5)/logger from 1.0.0 to 1.0.1 ([#165](https://github.com/SAP/ui5-server/issues/165)) [`21be52a`](https://github.com/SAP/ui5-server/commit/21be52a109abd5096daefc54ce038a95bd437f6f) +- Bump [@ui5](https://github.com/ui5)/builder from 1.0.0 to 1.0.1 ([#126](https://github.com/SAP/ui5-server/issues/126)) [`e22c118`](https://github.com/SAP/ui5-server/commit/e22c1185e2e5fc718b50704b6a64a121413b3f93) +- Bump [@ui5](https://github.com/ui5)/fs from 1.0.0 to 1.0.1 [`255766a`](https://github.com/SAP/ui5-server/commit/255766a62981af2e5ef584015d2951d39189ef3a) + +### Features +- Add Server Option to Send SAP's Target CSPs by default ([#179](https://github.com/SAP/ui5-server/issues/179)) [`4f05967`](https://github.com/SAP/ui5-server/commit/4f059670306c97ab5f34d82bb335f5ee21d73c72) + + + +## [v1.0.0] - 2019-01-10 +### Dependency Updates +- Bump [@ui5](https://github.com/ui5)/project from 0.2.5 to 1.0.0 ([#109](https://github.com/SAP/ui5-server/issues/109)) [`84d31a5`](https://github.com/SAP/ui5-server/commit/84d31a5340f77fc6ec54e9c5829c8ad656b2adb1) +- Bump [@ui5](https://github.com/ui5)/builder from 0.2.9 to 1.0.0 ([#108](https://github.com/SAP/ui5-server/issues/108)) [`8e39375`](https://github.com/SAP/ui5-server/commit/8e393754f71efe14e9cd5daf345012f2b1d7926d) +- Bump [@ui5](https://github.com/ui5)/fs from 0.2.0 to 1.0.0 ([#107](https://github.com/SAP/ui5-server/issues/107)) [`93e39af`](https://github.com/SAP/ui5-server/commit/93e39afc3e728ff2e829865d7de3c635a43241f0) +- Bump [@ui5](https://github.com/ui5)/logger from 0.2.2 to 1.0.0 ([#106](https://github.com/SAP/ui5-server/issues/106)) [`3687ad6`](https://github.com/SAP/ui5-server/commit/3687ad6b224cf9c37359de30917bc711fe7b239a) + + + +## [v0.2.2] - 2018-10-29 + + +## [v0.2.1] - 2018-07-17 + + +## [v0.2.0] - 2018-07-12 + + +## [v0.1.2] - 2018-07-10 +### Bug Fixes +- **CSP Middleware:** Export middleware, add report-uri [`2091c0c`](https://github.com/SAP/ui5-server/commit/2091c0cc093f9997c582e301ad52bbe74d4651d6) + + + +## [v0.1.1] - 2018-07-10 +### Features +- simplistic serveIndex with additional properties [`fa04ee2`](https://github.com/SAP/ui5-server/commit/fa04ee227cf5d4af4a8ba5d4d3fa594cee417da0) +- Add CSP middleware ([#3](https://github.com/SAP/ui5-server/issues/3)) [`f9ec3ee`](https://github.com/SAP/ui5-server/commit/f9ec3eeb43708462c2d683a80beb1816beeddc92) + + + +## [v0.1.0] - 2018-06-26 + + +## v0.0.1 - 2018-06-06 + + +{{- if .Versions }} +{{ range .Versions -}} +{{ if and .Tag.Previous (ne .Tag.Name "v3.0.0") -}} +[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} +{{ end -}} +[v3.2.1]: https://github.com/SAP/ui5-server/compare/v3.2.0...v3.2.1 +[v3.2.0]: https://github.com/SAP/ui5-server/compare/v3.1.5...v3.2.0 +[v3.1.5]: https://github.com/SAP/ui5-server/compare/v3.1.4...v3.1.5 +[v3.1.4]: https://github.com/SAP/ui5-server/compare/v3.1.3...v3.1.4 +[v3.1.3]: https://github.com/SAP/ui5-server/compare/v3.1.2...v3.1.3 +[v3.1.2]: https://github.com/SAP/ui5-server/compare/v3.1.1...v3.1.2 +[v3.1.1]: https://github.com/SAP/ui5-server/compare/v3.1.0...v3.1.1 +[v3.1.0]: https://github.com/SAP/ui5-server/compare/v3.0.1...v3.1.0 +[v3.0.1]: https://github.com/SAP/ui5-server/compare/v3.0.0...v3.0.1 +[v3.0.0]: https://github.com/SAP/ui5-server/compare/v2.4.0...v3.0.0 +[v2.4.1]: https://github.com/SAP/ui5-server/compare/v2.4.0...v2.4.1 +[v2.4.0]: https://github.com/SAP/ui5-server/compare/v2.3.1...v2.4.0 +[v2.3.1]: https://github.com/SAP/ui5-server/compare/v2.3.0...v2.3.1 +[v2.3.0]: https://github.com/SAP/ui5-server/compare/v2.2.10...v2.3.0 +[v2.2.10]: https://github.com/SAP/ui5-server/compare/v2.2.9...v2.2.10 +[v2.2.9]: https://github.com/SAP/ui5-server/compare/v2.2.8...v2.2.9 +[v2.2.8]: https://github.com/SAP/ui5-server/compare/v2.2.7...v2.2.8 +[v2.2.7]: https://github.com/SAP/ui5-server/compare/v2.2.6...v2.2.7 +[v2.2.6]: https://github.com/SAP/ui5-server/compare/v2.2.5...v2.2.6 +[v2.2.5]: https://github.com/SAP/ui5-server/compare/v2.2.4...v2.2.5 +[v2.2.4]: https://github.com/SAP/ui5-server/compare/v2.2.3...v2.2.4 +[v2.2.3]: https://github.com/SAP/ui5-server/compare/v2.2.2...v2.2.3 +[v2.2.2]: https://github.com/SAP/ui5-server/compare/v2.2.1...v2.2.2 +[v2.2.1]: https://github.com/SAP/ui5-server/compare/v2.2.0...v2.2.1 +[v2.2.0]: https://github.com/SAP/ui5-server/compare/v2.1.0...v2.2.0 +[v2.1.0]: https://github.com/SAP/ui5-server/compare/v2.0.3...v2.1.0 +[v2.0.3]: https://github.com/SAP/ui5-server/compare/v2.0.2...v2.0.3 +[v2.0.2]: https://github.com/SAP/ui5-server/compare/v2.0.1...v2.0.2 +[v2.0.1]: https://github.com/SAP/ui5-server/compare/v2.0.0...v2.0.1 +[v2.0.0]: https://github.com/SAP/ui5-server/compare/v1.6.0...v2.0.0 +[v1.6.0]: https://github.com/SAP/ui5-server/compare/v1.5.4...v1.6.0 +[v1.5.4]: https://github.com/SAP/ui5-server/compare/v1.5.3...v1.5.4 +[v1.5.3]: https://github.com/SAP/ui5-server/compare/v1.5.2...v1.5.3 +[v1.5.2]: https://github.com/SAP/ui5-server/compare/v1.5.1...v1.5.2 +[v1.5.1]: https://github.com/SAP/ui5-server/compare/v1.5.0...v1.5.1 +[v1.5.0]: https://github.com/SAP/ui5-server/compare/v1.4.0...v1.5.0 +[v1.4.0]: https://github.com/SAP/ui5-server/compare/v1.3.0...v1.4.0 +[v1.3.0]: https://github.com/SAP/ui5-server/compare/v1.2.0...v1.3.0 +[v1.2.0]: https://github.com/SAP/ui5-server/compare/v1.1.3...v1.2.0 +[v1.1.3]: https://github.com/SAP/ui5-server/compare/v1.1.2...v1.1.3 +[v1.1.2]: https://github.com/SAP/ui5-server/compare/v1.1.1...v1.1.2 +[v1.1.1]: https://github.com/SAP/ui5-server/compare/v1.1.0...v1.1.1 +[v1.1.0]: https://github.com/SAP/ui5-server/compare/v1.0.0...v1.1.0 +[v1.0.0]: https://github.com/SAP/ui5-server/compare/v0.2.2...v1.0.0 +[v0.2.2]: https://github.com/SAP/ui5-server/compare/v0.2.1...v0.2.2 +[v0.2.1]: https://github.com/SAP/ui5-server/compare/v0.2.0...v0.2.1 +[v0.2.0]: https://github.com/SAP/ui5-server/compare/v0.1.2...v0.2.0 +[v0.1.2]: https://github.com/SAP/ui5-server/compare/v0.1.1...v0.1.2 +[v0.1.1]: https://github.com/SAP/ui5-server/compare/v0.1.0...v0.1.1 +[v0.1.0]: https://github.com/SAP/ui5-server/compare/v0.0.1...v0.1.0 diff --git a/packages/server/.chglog/RELEASE.tpl.md b/packages/server/.chglog/RELEASE.tpl.md new file mode 100755 index 00000000000..efc482c385d --- /dev/null +++ b/packages/server/.chglog/RELEASE.tpl.md @@ -0,0 +1,33 @@ +{{ range .Versions }} +{{ range .CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [`{{ .Hash.Short }}`]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }}) +{{ end }} +{{ end -}} + +{{- if .RevertCommits -}} +### Reverts +{{ range .RevertCommits -}} +- {{ .Revert.Header }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} + +{{ if .Tag.Previous }} +### All changes +[`{{ .Tag.Previous.Name }}...{{ .Tag.Name }}`] +{{ end }} + +{{ if .Tag.Previous -}} +[`{{ .Tag.Previous.Name }}...{{ .Tag.Name }}`]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} diff --git a/packages/server/.chglog/config.yml b/packages/server/.chglog/config.yml new file mode 100755 index 00000000000..182f49d5452 --- /dev/null +++ b/packages/server/.chglog/config.yml @@ -0,0 +1,33 @@ +style: github +template: CHANGELOG.tpl.md +info: + title: CHANGELOG + repository_url: https://github.com/SAP/ui5-server +options: + commits: + filters: + Type: + - FEATURE + - FIX + - PERF + - DEPENDENCY + - BREAKING + commit_groups: + title_maps: + FEATURE: Features + FIX: Bug Fixes + PERF: Performance Improvements + DEPENDENCY: Dependency Updates + BREAKING: Breaking Changes + header: + pattern: "^\\[(\\w*)\\]\\s(?:([^\\:]*)\\:\\s)?(.*)$" + pattern_maps: + - Type + - Scope + - Subject + issues: + prefix: + - "#" + notes: + keywords: + - BREAKING CHANGE diff --git a/packages/server/.chglog/release-config.yml b/packages/server/.chglog/release-config.yml new file mode 100755 index 00000000000..de2bf2eff6c --- /dev/null +++ b/packages/server/.chglog/release-config.yml @@ -0,0 +1,33 @@ +style: github +template: RELEASE.tpl.md +info: + repository_url: https://github.com/SAP/ui5-server +options: + tag_filter_pattern: '^v[^0123]' # For release notes ignore versions below v4 to that we always compare the _last v4+_ tag with the current release + commits: + filters: + Type: + - FEATURE + - FIX + - PERF + - DEPENDENCY + - BREAKING + commit_groups: + title_maps: + FEATURE: Features + FIX: Bug Fixes + PERF: Performance Improvements + DEPENDENCY: Dependency Updates + BREAKING: Breaking Changes + header: + pattern: "^\\[(\\w*)\\]\\s(?:([^\\:]*)\\:\\s)?(.*)$" + pattern_maps: + - Type + - Scope + - Subject + issues: + prefix: + - "#" + notes: + keywords: + - BREAKING CHANGE diff --git a/packages/server/.editorconfig b/packages/server/.editorconfig new file mode 100644 index 00000000000..b432804f7fc --- /dev/null +++ b/packages/server/.editorconfig @@ -0,0 +1,20 @@ +# see http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = tab + +[*.{css,html,js,cjs,mjs,jsx,ts,tsx,less,txt,json,yml,md}] +trim_trailing_whitespace = true +end_of_line = lf +indent_size = 4 +insert_final_newline = true + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/packages/server/.gitattributes b/packages/server/.gitattributes new file mode 100644 index 00000000000..6313b56c578 --- /dev/null +++ b/packages/server/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/packages/server/.github/ISSUE_TEMPLATE.md b/packages/server/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..6ddeb2221a8 --- /dev/null +++ b/packages/server/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,3 @@ +## 🚨 Issues Have Been Transferred to UI5 CLI Repository + +Please create new issues in the UI5 CLI repository: https://github.com/UI5/cli/issues/new/choose diff --git a/packages/server/.github/ISSUE_TEMPLATE/config.yml b/packages/server/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..34df09809c9 --- /dev/null +++ b/packages/server/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Report UI5 CLI Issues or Request a Feature + url: https://github.com/UI5/cli/issues/new/choose + about: Please create new issues in the UI5 CLI repository diff --git a/packages/server/.github/PULL_REQUEST_TEMPLATE.md b/packages/server/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..2f043b5c5cc --- /dev/null +++ b/packages/server/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +**Thank you for your contribution!** 🙌 + +To get it merged faster, kindly review the checklist below: + +## Pull Request Checklist +- [ ] Reviewed the [Contributing Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#-contributing-code) + + Especially the [How to Contribute](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#how-to-contribute) section +- [ ] [No merge commits](https://github.com/UI5/cli/blob/main/docs/Guidelines.md#no-merge-commits) +- [ ] [Correct commit message style](https://github.com/UI5/cli/blob/main/docs/Guidelines.md#commit-message-style) diff --git a/packages/server/.github/dependabot.yml b/packages/server/.github/dependabot.yml new file mode 100644 index 00000000000..ebf6ca750f8 --- /dev/null +++ b/packages/server/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" +- package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + day: sunday + time: "10:00" + timezone: Etc/UCT + reviewers: + - "SAP/ui5-foundation" + versioning-strategy: increase + commit-message: + prefix: "[DEPENDENCY] " + prefix-development: "[INTERNAL] " diff --git a/packages/server/.github/in-solidarity.yml b/packages/server/.github/in-solidarity.yml new file mode 100644 index 00000000000..4ce829a6be3 --- /dev/null +++ b/packages/server/.github/in-solidarity.yml @@ -0,0 +1 @@ +_extends: ietf/terminology diff --git a/packages/server/.github/workflows/dependabot-auto-merge.yml b/packages/server/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 00000000000..43d92c94fd6 --- /dev/null +++ b/packages/server/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,28 @@ +name: Dependabot auto-merge +on: + pull_request: + branches: + - v4 + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' && github.event.pull_request.auto_merge == null }} + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Approve and auto-merge PRs for minor/patch updates of github-actions + if: | + steps.metadata.outputs.package-ecosystem == 'github_actions' && + contains(fromJSON('["version-update:semver-minor", "version-update:semver-patch"]'), steps.metadata.outputs.update-type) + run: gh pr review --approve "$PR_URL" && gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/packages/server/.github/workflows/github-ci.yml b/packages/server/.github/workflows/github-ci.yml new file mode 100644 index 00000000000..ace9ddfa6aa --- /dev/null +++ b/packages/server/.github/workflows/github-ci.yml @@ -0,0 +1,34 @@ +name: GitHub CI + +on: + push: + branches: + - v4 + pull_request: + branches: + - v4 + +# No permissions are required for this workflow +permissions: {} + +jobs: + test: + name: General checks, tests and coverage reporting + runs-on: ubuntu-24.04 + steps: + + - uses: actions/checkout@v4 + + - name: Use Node.js LTS 20.11.0 + uses: actions/setup-node@v4.4.0 + with: + node-version: 20.11.0 + + - name: Install dependencies + run: npm ci + + - name: Perform checks and tests + run: npm test + + - name: Send report to Coveralls + uses: coverallsapp/github-action@v2.3.6 diff --git a/packages/server/.github/workflows/reuse-compliance.yml b/packages/server/.github/workflows/reuse-compliance.yml new file mode 100644 index 00000000000..3dbdbb22ff9 --- /dev/null +++ b/packages/server/.github/workflows/reuse-compliance.yml @@ -0,0 +1,21 @@ +name: REUSE + +on: + push: + branches: + - v4 + pull_request: + branches: + - v4 + +# No permissions are required for this workflow +permissions: {} + +jobs: + compliance-check: + name: Compliance Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Execute REUSE Compliance Check + uses: fsfe/reuse-action@v5 diff --git a/packages/server/.gitignore b/packages/server/.gitignore new file mode 100644 index 00000000000..8ba81815932 --- /dev/null +++ b/packages/server/.gitignore @@ -0,0 +1,63 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# CI (Azure Pipelines) xUnit test results +test-results.xml + +# IDEs +.vscode/ +*.~vsdx +.idea/ + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# Misc +yarn.lock +.DS_Store + +# Don't include private SSH key for deployment via Travis CI +deploy_key + +# Custom directories +!test/fixtures/**/node_modules +test/tmp/ +jsdocs/ diff --git a/packages/server/.npmrc b/packages/server/.npmrc new file mode 100644 index 00000000000..93ec4f76ba6 --- /dev/null +++ b/packages/server/.npmrc @@ -0,0 +1,3 @@ +# Enforce public npm registry +registry=https://registry.npmjs.org/ +lockfile-version=3 diff --git a/packages/server/CHANGELOG.md b/packages/server/CHANGELOG.md new file mode 100644 index 00000000000..6b6a7bbf813 --- /dev/null +++ b/packages/server/CHANGELOG.md @@ -0,0 +1,465 @@ +# Changelog +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +A list of unreleased changes can be found [here](https://github.com/SAP/ui5-server/compare/v4.0.7...HEAD). + + +## [v4.0.7] - 2025-09-11 +### Bug Fixes +- Rename project to UI5 CLI [`ece7424`](https://github.com/SAP/ui5-server/commit/ece7424419f39eab655027043871048e0476d272) + + + +## [v4.0.6] - 2025-06-06 +### Bug Fixes +- Log error in case UI5 Server uses HTTP/2 with Node 24 ([#722](https://github.com/SAP/ui5-server/issues/722)) [`71dd7af`](https://github.com/SAP/ui5-server/commit/71dd7afc946dd4d56005a9f430148670cae5e76c) + + + +## [v4.0.5] - 2024-09-11 +### Dependency Updates +- Bump path-to-regexp and router [`f713647`](https://github.com/SAP/ui5-server/commit/f713647258c89df7355c78a6c3b86817167027ed) + + + +## [v4.0.4] - 2024-08-27 +### Bug Fixes +- Ensure SSL credentials are only readable by owner [`7220dbb`](https://github.com/SAP/ui5-server/commit/7220dbb2237dbf3104dcb88c15c1ca86b61ba49d) + + + +## [v4.0.3] - 2024-08-09 +### Bug Fixes +- **serveResources:** Do not process manifest.json in test-resources [`964e784`](https://github.com/SAP/ui5-server/commit/964e784f41479300ba45eb4a4818ddd0449d41e7) + + + +## [v4.0.2] - 2024-08-01 +### Dependency Updates +- Bump devcert-sanscache from 0.4.8 to 5.0.1 [`4a06c57`](https://github.com/SAP/ui5-server/commit/4a06c579c510d567ea214d565a490327c0b481d0) + + + +## [v4.0.1] - 2024-07-31 +### Bug Fixes +- **serveResources:** Improve cache invalidation ([#688](https://github.com/SAP/ui5-server/issues/688)) [`777afa5`](https://github.com/SAP/ui5-server/commit/777afa52e459f988e0799ae63b8a9b024db0c398) + + + +## [v4.0.0] - 2024-07-23 +### Breaking Changes +- Drop node v21 support [`2af0d4f`](https://github.com/SAP/ui5-server/commit/2af0d4fd94c720899bc5978d27ca176c7b2feecb) +- Replace console.log with process.stderr ([#643](https://github.com/SAP/ui5-server/issues/643)) [`d42c79f`](https://github.com/SAP/ui5-server/commit/d42c79f0a6e6fa5e685f736dd0ae79b353767051) +- Require Node.js 20.11.x/>=21.2.0 and npm >=10 [`39cc1a2`](https://github.com/SAP/ui5-server/commit/39cc1a2dea8e081888ae1f5f2c47a74bb7efe693) + +### Features +- **manifest.json:** Auto-fill supportedLocales [`a39c8de`](https://github.com/SAP/ui5-server/commit/a39c8debaabe259800ed8f861071b00817c10350) + +### BREAKING CHANGE + +Messages will now be written to stderr instead of stdout. + +JIRA: CPOUI5FOUNDATION-802 +Related to: https://github.com/SAP/ui5-tooling/issues/701 +Sibling of: https://github.com/SAP/ui5-tooling/pull/930, +https://github.com/SAP/ui5-cli/pull/686 + +Support for older Node.js and npm releases has been dropped. +Only Node.js 20.11.x and >=21.2.0 as well as npm v10 or higher are supported. + + +## [v3.2.1] - 2024-05-15 +### Bug Fixes +- **middleware/versionInfo:** Only process dependencies of type 'library' [`4c99455`](https://github.com/SAP/ui5-server/commit/4c99455cf21539c2d1403cca81c603ead7124351) + + + +## [v3.2.0] - 2024-04-24 +### Features +- **serveResources:** Dynamically generate missing library manifest.json [`d31f2c5`](https://github.com/SAP/ui5-server/commit/d31f2c57aa0b0c72fb033c41587ff257312838d1) + + + +## [v3.1.5] - 2023-12-12 +### Bug Fixes +- Unsafe jQuery plugin ([#618](https://github.com/SAP/ui5-server/issues/618)) [`ff1dc29`](https://github.com/SAP/ui5-server/commit/ff1dc297a9221f534173ec44e38e5a6eec6b9ba5) + + + +## [v3.1.4] - 2023-11-20 + + +## [v3.1.3] - 2023-06-06 +### Bug Fixes +- **middleware/testRunner:** Update resources from OpenUI5 [`f0c7291`](https://github.com/SAP/ui5-server/commit/f0c7291d2dc1d753e04184fdf2127c278810f0c4) + + + +## [v3.1.2] - 2023-04-12 +### Bug Fixes +- Fix JSDoc names of typedefs in MiddlewareUtil. [`dbd6fe1`](https://github.com/SAP/ui5-server/commit/dbd6fe19c229471ba2b8621b97b8f5a9bca56a78) + + + +## [v3.1.1] - 2023-03-16 +### Bug Fixes +- Allow serving of propertyfiles of non component project types [`1bc6ec7`](https://github.com/SAP/ui5-server/commit/1bc6ec72a15ae7df558c4938b2670d0e78af710b) + + + +## [v3.1.0] - 2023-03-01 +### Features +- **CSP:** Increase defaultPolicy2 to sap-target-level-3 ([#580](https://github.com/SAP/ui5-server/issues/580)) [`5a981a1`](https://github.com/SAP/ui5-server/commit/5a981a1d7f2d2aaffbcd2f68e02206a8bdb0494b) + + + +## [v3.0.1] - 2023-02-16 +### Bug Fixes +- **MiddlewareUtil:** Provide framework configuration getters to custom tasks ([#579](https://github.com/SAP/ui5-server/issues/579)) [`58bf4f5`](https://github.com/SAP/ui5-server/commit/58bf4f5953aed3a1fc6f4f4353e37fe6c3b7094f) + + + +## [v3.0.0] - 2023-02-09 +### Breaking Changes +- Transform to ES Modules ([#501](https://github.com/SAP/ui5-server/issues/501)) [`05e3013`](https://github.com/SAP/ui5-server/commit/05e3013605e28e9ab5a785aa57616473d40e5710) +- Remove "/proxy" endpoint ([#550](https://github.com/SAP/ui5-server/issues/550)) [`4bdf839`](https://github.com/SAP/ui5-server/commit/4bdf839e96f67ddbc4cb2a18216921d54df4006e) +- Require Project Graph ([#479](https://github.com/SAP/ui5-server/issues/479)) [`d62f85a`](https://github.com/SAP/ui5-server/commit/d62f85a193115a587dbf58225e8130318a475023) +- Require Node.js >= 16.18.0 / npm >= 8 [`63d216a`](https://github.com/SAP/ui5-server/commit/63d216a3ba34e8e50acc6621d43a78c3a0804d67) + +### BREAKING CHANGE +This package has been transformed to ES Modules. Therefore it no longer provides a CommonJS export. +If your project uses CommonJS, it needs to be converted to ESM or use a dynamic import. + +For more information see also: + +- https://sap.github.io/ui5-tooling/updates/migrate-v3/ +- https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +This removes the "/proxy" endpoint and the corresponding +"connectUi5Proxy" middleware from the standard ui5-server. +Internally, this middleware made use of the connect-openui5 proxy +implementation (https://github.com/SAP/connect-openui5#proxy). + +More sophisticated proxy solutions for ui5-server are already available +in the form of custom middleware extensions from the UI5-community. + +The UI5 Team might provide a dedicated custom middleware extension, +with similar functionality, in the future. + +- Server now requires a Project Graph instance instead. +- Standard middleware now rely on Project instances being available on Resources (see https://github.com/SAP/ui5-fs/pull/381) +- MiddlewareRepository#addMiddleware has been removed. Custom middleware need to be added to the project graph instead + +Support for older Node.js and npm releases has been dropped. +Only Node.js v16.18.0 and npm v8 or higher are supported. + +### Features +- **MiddlewareUtil:** Add getProject/getDependencies/resourceFactory API to interface ([#547](https://github.com/SAP/ui5-server/issues/547)) [`ab28f78`](https://github.com/SAP/ui5-server/commit/ab28f789ba929ef1319b6e562267e9717cc9937b) + + + +## [v2.4.1] - 2022-11-30 +### Dependency Updates +- Bump connect-openui5 from 0.10.2 to 0.10.3 [`20b6fc8`](https://github.com/SAP/ui5-server/commit/20b6fc8fbad69265bae3e8f7efd320f6297ac4c4) + + + +## [v2.4.0] - 2021-10-19 +### Features +- Enhance versionInfo middleware to serve sap-ui-version.json ([#420](https://github.com/SAP/ui5-server/issues/420)) [`c6f83f5`](https://github.com/SAP/ui5-server/commit/c6f83f5472eb2fe6a8d4eca10ecdc5f4b522bc3c) + + + +## [v2.3.1] - 2021-07-23 + + +## [v2.3.0] - 2021-07-01 +### Features +- **server:** Expose configuration options for SAP CSP policies [`55d6a96`](https://github.com/SAP/ui5-server/commit/55d6a96cc1a3c762af8173d9fb9588fe742a302d) + + + +## [v2.2.10] - 2021-06-01 + + +## [v2.2.9] - 2021-03-11 +### Dependency Updates +- Bump connect-openui5 from 0.10.1 to 0.10.2 [`849957d`](https://github.com/SAP/ui5-server/commit/849957d39a45c22c5c94f787d66eeccc58eb4d2d) + + + +## [v2.2.8] - 2021-02-09 + + +## [v2.2.7] - 2020-11-06 + + +## [v2.2.6] - 2020-10-22 +### Bug Fixes +- Improve parallel theme request handling [`88bc0d6`](https://github.com/SAP/ui5-server/commit/88bc0d6d4e5ca8bb191029335451713579360e1c) +- **nonReadRequests middleware:** Use response API [`2d2325f`](https://github.com/SAP/ui5-server/commit/2d2325f638820d25738ddbd56afe0d104e37f2e0) + + + +## [v2.2.5] - 2020-10-06 +### Bug Fixes +- Discovery middleware shouldn't fail when lib names overlap ([#362](https://github.com/SAP/ui5-server/issues/362)) [`f5067ce`](https://github.com/SAP/ui5-server/commit/f5067ce38b89442948ce096e5d89651e8970bdb9) + + + +## [v2.2.4] - 2020-09-10 + + +## [v2.2.3] - 2020-09-02 +### Bug Fixes +- **middleware/testRunner:** Update resources from OpenUI5 [`55b1fe7`](https://github.com/SAP/ui5-server/commit/55b1fe742be29b176e55985a0315dcead684d257) + + + +## [v2.2.2] - 2020-08-11 + + +## [v2.2.1] - 2020-07-14 +### Bug Fixes +- **MiddlewareManager:** Provide MiddlewareUtil to custom middleware using specVersion 2.1 [`3e249fa`](https://github.com/SAP/ui5-server/commit/3e249fa4333fb6afa0e512201959bfcbcee196d0) +- **Node.js API:** TypeScript type definition support ([#334](https://github.com/SAP/ui5-server/issues/334)) [`b66f9cc`](https://github.com/SAP/ui5-server/commit/b66f9cc10b35b2997f5e9b3840ef92dd504c8a33) + + + +## [v2.2.0] - 2020-07-01 +### Bug Fixes +- **MiddlewareManager:** Update SAP Target CSP Policies [`269c22c`](https://github.com/SAP/ui5-server/commit/269c22c80a6682a3d680c47e768f69a20ecabcd0) + +### Features +- **CSP:** Add ignorePaths option ([#331](https://github.com/SAP/ui5-server/issues/331)) [`27a962e`](https://github.com/SAP/ui5-server/commit/27a962eb80fd7de95c6076f7d307e0dd06dac057) + + + +## [v2.1.0] - 2020-06-15 +### Features +- **csp:** enable tracking and serving of csp reports ([#323](https://github.com/SAP/ui5-server/issues/323)) [`e0a0c5e`](https://github.com/SAP/ui5-server/commit/e0a0c5e022c2c0041c6cf52631ef834cafe1f873) + + + +## [v2.0.3] - 2020-05-14 + + +## [v2.0.2] - 2020-04-30 +### Bug Fixes +- **CSP Middleware:** Use res.getHeader/setHeader methods ([#312](https://github.com/SAP/ui5-server/issues/312)) [`c53525c`](https://github.com/SAP/ui5-server/commit/c53525ca4bb5825d241d0f137ce3912d681e6548) + + + +## [v2.0.1] - 2020-04-15 +### Dependency Updates +- Bump devcert-sanscache from 0.4.6 to 0.4.8 ([#306](https://github.com/SAP/ui5-server/issues/306)) [`2a9d517`](https://github.com/SAP/ui5-server/commit/2a9d51776e967362d959eef45ce9533a9a27650c) + + + +## [v2.0.0] - 2020-03-31 +### Breaking Changes +- Require Node.js >= 10 [`a8c7a13`](https://github.com/SAP/ui5-server/commit/a8c7a13f68426012e5ff9cfddb365bb32c46f9dc) +- **serveResources middleware:** Expect *.properties files in UTF-8 by default [`af7f9ad`](https://github.com/SAP/ui5-server/commit/af7f9ad52aa834f63c163b99eb4fbc8d1bb05079) + +### Bug Fixes +- Handle encoding in request paths correctly [`256b3f0`](https://github.com/SAP/ui5-server/commit/256b3f037880aad077b0158e3551e10ce8a3dbc7) + +### Features +- Add MiddlewareUtil providing convenience functions to all middleware [`b8ab775`](https://github.com/SAP/ui5-server/commit/b8ab775039635a25109797b92fe34358057ea5e8) +- Add test runner middleware [`ea77e20`](https://github.com/SAP/ui5-server/commit/ea77e201e20545fca7494fc581aa42adbcb2c1d7) + +### BREAKING CHANGE + +If the project a "*.properties" resource originates from cannot be +determined, or if the project does not define a +propertiesFileSourceEncoding configuration or uses a legacy specVersion +(<2.0), the serveResources middleware assumes that the resource is UTF-8 +encoded instead of ISO-8859-1. + +Support for older Node.js releases has been dropped. +Only Node.js v10 or higher is supported. + + + +## [v1.6.0] - 2020-02-24 +### Bug Fixes +- **versionInfo:** Fix pattern to glob for .library files [`3621f78`](https://github.com/SAP/ui5-server/commit/3621f7868dec891f8746ca4b66cf43c4d5d9782b) + +### Features +- **serveThemes:** Support experimental CSS variables and skeleton build ([#278](https://github.com/SAP/ui5-server/issues/278)) [`47d4b55`](https://github.com/SAP/ui5-server/commit/47d4b55986fd84ef85f4b42e9c91f16017183c16) + + + +## [v1.5.4] - 2020-02-10 +### Bug Fixes +- Ensure proper handling of multi-byte characters in streams ([#280](https://github.com/SAP/ui5-server/issues/280)) [`fe652e4`](https://github.com/SAP/ui5-server/commit/fe652e410bd0eab506fc42036ad2cfa374fa5a6c) +- **serveIndex:** Add missing dependency to "graceful-fs" [`e09c472`](https://github.com/SAP/ui5-server/commit/e09c472eb20ed3d0b914c8b2e1d5f22bb8476dca) + + + +## [v1.5.3] - 2020-01-24 +### Bug Fixes +- **sslUtils:** Fix Invalid Common Name error [`db06db7`](https://github.com/SAP/ui5-server/commit/db06db7a371ea7254c408f01cf231de9367c8b0d) + + + +## [v1.5.2] - 2019-12-16 +### Bug Fixes +- Resolve ERR_CERT_REVOKED error for newly generated SSL certs [`f2e1522`](https://github.com/SAP/ui5-server/commit/f2e15229569e68990f63cd38849eb937d2ad9cb8) + + + +## [v1.5.1] - 2019-11-19 +### Dependency Updates +- Bump connect-openui5 from 0.8.0 to 0.9.0 [`0c6d502`](https://github.com/SAP/ui5-server/commit/0c6d50263c4828f5070404ac9dfa337667b24371) + + + +## [v1.5.0] - 2019-11-07 +### Features +- **serveIndex:** use serve-index for serving the application index [`d6ea507`](https://github.com/SAP/ui5-server/commit/d6ea507bdd649653a865f01d4e076caa4313639f) + + + +## [v1.4.0] - 2019-10-24 +### Features +- **Custom Middleware Extensibility:** Allow multiple definitions of the same custom middleware ([#246](https://github.com/SAP/ui5-server/issues/246)) [`55a24ef`](https://github.com/SAP/ui5-server/commit/55a24ef134b01b43683bc21fb24b46d4e472232d) + + + +## [v1.3.0] - 2019-07-31 +### Features +- Properties File Escaping ([#214](https://github.com/SAP/ui5-server/issues/214)) [`dd4844d`](https://github.com/SAP/ui5-server/commit/dd4844d53b787dc14bc5eecae2bc5674425200b7) + + + +## [v1.2.0] - 2019-07-10 +### Features +- **Server:** Add handling for custom middleware ([#200](https://github.com/SAP/ui5-server/issues/200)) [`037b3bc`](https://github.com/SAP/ui5-server/commit/037b3bc001b86061c807e78584e69c53e89d8b96) + + + +## [v1.1.3] - 2019-06-24 +### Bug Fixes +- **serveResources:** Correctly encode non UTF-8 resources [`1ee6723`](https://github.com/SAP/ui5-server/commit/1ee6723b5e5dac653c76a5078ee4afd6af96f8ac) + + + +## [v1.1.2] - 2019-06-03 +### Bug Fixes +- **Middleware:** Allow usage without express server [`4d971b4`](https://github.com/SAP/ui5-server/commit/4d971b4babade56fef154dc4a7a524d6ffa8ad1b) + + + +## [v1.1.1] - 2019-05-13 +### Bug Fixes +- Makes CSP middleware work in an environment without express server ([#184](https://github.com/SAP/ui5-server/issues/184)) [`c3089ad`](https://github.com/SAP/ui5-server/commit/c3089adeee030f4ace899c01944006583146e32e) + + + +## [v1.1.0] - 2019-04-25 +### Dependency Updates +- Bump [@ui5](https://github.com/ui5)/fs from 1.0.1 to 1.0.2 ([#166](https://github.com/SAP/ui5-server/issues/166)) [`5ff4765`](https://github.com/SAP/ui5-server/commit/5ff476504254baf304c2cb9db83746438a10be92) +- Bump [@ui5](https://github.com/ui5)/logger from 1.0.0 to 1.0.1 ([#165](https://github.com/SAP/ui5-server/issues/165)) [`21be52a`](https://github.com/SAP/ui5-server/commit/21be52a109abd5096daefc54ce038a95bd437f6f) +- Bump [@ui5](https://github.com/ui5)/builder from 1.0.0 to 1.0.1 ([#126](https://github.com/SAP/ui5-server/issues/126)) [`e22c118`](https://github.com/SAP/ui5-server/commit/e22c1185e2e5fc718b50704b6a64a121413b3f93) +- Bump [@ui5](https://github.com/ui5)/fs from 1.0.0 to 1.0.1 [`255766a`](https://github.com/SAP/ui5-server/commit/255766a62981af2e5ef584015d2951d39189ef3a) + +### Features +- Add Server Option to Send SAP's Target CSPs by default ([#179](https://github.com/SAP/ui5-server/issues/179)) [`4f05967`](https://github.com/SAP/ui5-server/commit/4f059670306c97ab5f34d82bb335f5ee21d73c72) + + + +## [v1.0.0] - 2019-01-10 +### Dependency Updates +- Bump [@ui5](https://github.com/ui5)/project from 0.2.5 to 1.0.0 ([#109](https://github.com/SAP/ui5-server/issues/109)) [`84d31a5`](https://github.com/SAP/ui5-server/commit/84d31a5340f77fc6ec54e9c5829c8ad656b2adb1) +- Bump [@ui5](https://github.com/ui5)/builder from 0.2.9 to 1.0.0 ([#108](https://github.com/SAP/ui5-server/issues/108)) [`8e39375`](https://github.com/SAP/ui5-server/commit/8e393754f71efe14e9cd5daf345012f2b1d7926d) +- Bump [@ui5](https://github.com/ui5)/fs from 0.2.0 to 1.0.0 ([#107](https://github.com/SAP/ui5-server/issues/107)) [`93e39af`](https://github.com/SAP/ui5-server/commit/93e39afc3e728ff2e829865d7de3c635a43241f0) +- Bump [@ui5](https://github.com/ui5)/logger from 0.2.2 to 1.0.0 ([#106](https://github.com/SAP/ui5-server/issues/106)) [`3687ad6`](https://github.com/SAP/ui5-server/commit/3687ad6b224cf9c37359de30917bc711fe7b239a) + + + +## [v0.2.2] - 2018-10-29 + + +## [v0.2.1] - 2018-07-17 + + +## [v0.2.0] - 2018-07-12 + + +## [v0.1.2] - 2018-07-10 +### Bug Fixes +- **CSP Middleware:** Export middleware, add report-uri [`2091c0c`](https://github.com/SAP/ui5-server/commit/2091c0cc093f9997c582e301ad52bbe74d4651d6) + + + +## [v0.1.1] - 2018-07-10 +### Features +- simplistic serveIndex with additional properties [`fa04ee2`](https://github.com/SAP/ui5-server/commit/fa04ee227cf5d4af4a8ba5d4d3fa594cee417da0) +- Add CSP middleware ([#3](https://github.com/SAP/ui5-server/issues/3)) [`f9ec3ee`](https://github.com/SAP/ui5-server/commit/f9ec3eeb43708462c2d683a80beb1816beeddc92) + + + +## [v0.1.0] - 2018-06-26 + + +## v0.0.1 - 2018-06-06 +[v4.0.7]: https://github.com/SAP/ui5-server/compare/v4.0.6...v4.0.7 +[v4.0.6]: https://github.com/SAP/ui5-server/compare/v4.0.5...v4.0.6 +[v4.0.5]: https://github.com/SAP/ui5-server/compare/v4.0.4...v4.0.5 +[v4.0.4]: https://github.com/SAP/ui5-server/compare/v4.0.3...v4.0.4 +[v4.0.3]: https://github.com/SAP/ui5-server/compare/v4.0.2...v4.0.3 +[v4.0.2]: https://github.com/SAP/ui5-server/compare/v4.0.1...v4.0.2 +[v4.0.1]: https://github.com/SAP/ui5-server/compare/v4.0.0...v4.0.1 +[v4.0.0]: https://github.com/SAP/ui5-server/compare/v3.1.5...v4.0.0 +[v3.2.1]: https://github.com/SAP/ui5-server/compare/v3.2.0...v3.2.1 +[v3.2.0]: https://github.com/SAP/ui5-server/compare/v3.1.5...v3.2.0 +[v3.1.5]: https://github.com/SAP/ui5-server/compare/v3.1.4...v3.1.5 +[v3.1.4]: https://github.com/SAP/ui5-server/compare/v3.1.3...v3.1.4 +[v3.1.3]: https://github.com/SAP/ui5-server/compare/v3.1.2...v3.1.3 +[v3.1.2]: https://github.com/SAP/ui5-server/compare/v3.1.1...v3.1.2 +[v3.1.1]: https://github.com/SAP/ui5-server/compare/v3.1.0...v3.1.1 +[v3.1.0]: https://github.com/SAP/ui5-server/compare/v3.0.1...v3.1.0 +[v3.0.1]: https://github.com/SAP/ui5-server/compare/v3.0.0...v3.0.1 +[v3.0.0]: https://github.com/SAP/ui5-server/compare/v2.4.0...v3.0.0 +[v2.4.1]: https://github.com/SAP/ui5-server/compare/v2.4.0...v2.4.1 +[v2.4.0]: https://github.com/SAP/ui5-server/compare/v2.3.1...v2.4.0 +[v2.3.1]: https://github.com/SAP/ui5-server/compare/v2.3.0...v2.3.1 +[v2.3.0]: https://github.com/SAP/ui5-server/compare/v2.2.10...v2.3.0 +[v2.2.10]: https://github.com/SAP/ui5-server/compare/v2.2.9...v2.2.10 +[v2.2.9]: https://github.com/SAP/ui5-server/compare/v2.2.8...v2.2.9 +[v2.2.8]: https://github.com/SAP/ui5-server/compare/v2.2.7...v2.2.8 +[v2.2.7]: https://github.com/SAP/ui5-server/compare/v2.2.6...v2.2.7 +[v2.2.6]: https://github.com/SAP/ui5-server/compare/v2.2.5...v2.2.6 +[v2.2.5]: https://github.com/SAP/ui5-server/compare/v2.2.4...v2.2.5 +[v2.2.4]: https://github.com/SAP/ui5-server/compare/v2.2.3...v2.2.4 +[v2.2.3]: https://github.com/SAP/ui5-server/compare/v2.2.2...v2.2.3 +[v2.2.2]: https://github.com/SAP/ui5-server/compare/v2.2.1...v2.2.2 +[v2.2.1]: https://github.com/SAP/ui5-server/compare/v2.2.0...v2.2.1 +[v2.2.0]: https://github.com/SAP/ui5-server/compare/v2.1.0...v2.2.0 +[v2.1.0]: https://github.com/SAP/ui5-server/compare/v2.0.3...v2.1.0 +[v2.0.3]: https://github.com/SAP/ui5-server/compare/v2.0.2...v2.0.3 +[v2.0.2]: https://github.com/SAP/ui5-server/compare/v2.0.1...v2.0.2 +[v2.0.1]: https://github.com/SAP/ui5-server/compare/v2.0.0...v2.0.1 +[v2.0.0]: https://github.com/SAP/ui5-server/compare/v1.6.0...v2.0.0 +[v1.6.0]: https://github.com/SAP/ui5-server/compare/v1.5.4...v1.6.0 +[v1.5.4]: https://github.com/SAP/ui5-server/compare/v1.5.3...v1.5.4 +[v1.5.3]: https://github.com/SAP/ui5-server/compare/v1.5.2...v1.5.3 +[v1.5.2]: https://github.com/SAP/ui5-server/compare/v1.5.1...v1.5.2 +[v1.5.1]: https://github.com/SAP/ui5-server/compare/v1.5.0...v1.5.1 +[v1.5.0]: https://github.com/SAP/ui5-server/compare/v1.4.0...v1.5.0 +[v1.4.0]: https://github.com/SAP/ui5-server/compare/v1.3.0...v1.4.0 +[v1.3.0]: https://github.com/SAP/ui5-server/compare/v1.2.0...v1.3.0 +[v1.2.0]: https://github.com/SAP/ui5-server/compare/v1.1.3...v1.2.0 +[v1.1.3]: https://github.com/SAP/ui5-server/compare/v1.1.2...v1.1.3 +[v1.1.2]: https://github.com/SAP/ui5-server/compare/v1.1.1...v1.1.2 +[v1.1.1]: https://github.com/SAP/ui5-server/compare/v1.1.0...v1.1.1 +[v1.1.0]: https://github.com/SAP/ui5-server/compare/v1.0.0...v1.1.0 +[v1.0.0]: https://github.com/SAP/ui5-server/compare/v0.2.2...v1.0.0 +[v0.2.2]: https://github.com/SAP/ui5-server/compare/v0.2.1...v0.2.2 +[v0.2.1]: https://github.com/SAP/ui5-server/compare/v0.2.0...v0.2.1 +[v0.2.0]: https://github.com/SAP/ui5-server/compare/v0.1.2...v0.2.0 +[v0.1.2]: https://github.com/SAP/ui5-server/compare/v0.1.1...v0.1.2 +[v0.1.1]: https://github.com/SAP/ui5-server/compare/v0.1.0...v0.1.1 +[v0.1.0]: https://github.com/SAP/ui5-server/compare/v0.0.1...v0.1.0 diff --git a/packages/server/CONTRIBUTING.md b/packages/server/CONTRIBUTING.md new file mode 100644 index 00000000000..2ddb6276ed5 --- /dev/null +++ b/packages/server/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to the UI5 CLI + +See CONTRIBUTING.md in the [UI5/cli](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md) repository. diff --git a/packages/server/LICENSE.txt b/packages/server/LICENSE.txt new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/packages/server/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/server/LICENSES/Apache-2.0.txt b/packages/server/LICENSES/Apache-2.0.txt new file mode 100644 index 00000000000..4ed90b95224 --- /dev/null +++ b/packages/server/LICENSES/Apache-2.0.txt @@ -0,0 +1,208 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, +AND DISTRIBUTION + + 1. Definitions. + + + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + + + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + + + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + + + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + + + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + + + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. diff --git a/packages/server/LICENSES/MIT.txt b/packages/server/LICENSES/MIT.txt new file mode 100644 index 00000000000..204b93da48d --- /dev/null +++ b/packages/server/LICENSES/MIT.txt @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +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 (including the next +paragraph) 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. diff --git a/packages/server/README.md b/packages/server/README.md new file mode 100644 index 00000000000..02ae7aaab9f --- /dev/null +++ b/packages/server/README.md @@ -0,0 +1,26 @@ +![UI5 icon](https://raw.githubusercontent.com/UI5/cli/main/docs/images/UI5_logo_wide.png) + +# ui5-server +> Modules for running a UI5 development server +> Part of the [UI5 CLI](https://github.com/UI5/cli) + +[![REUSE status](https://api.reuse.software/badge/github.com/SAP/ui5-server)](https://api.reuse.software/info/github.com/SAP/ui5-server) +[![Build Status](https://dev.azure.com/sap/opensource/_apis/build/status/SAP.ui5-server?branchName=v4)](https://dev.azure.com/sap/opensource/_build/latest?definitionId=34&branchName=v4) +[![npm Package Version](https://badge.fury.io/js/%40ui5%2Fserver.svg)](https://www.npmjs.com/package/@ui5/server) +[![Coverage Status](https://coveralls.io/repos/github/SAP/ui5-server/badge.svg)](https://coveralls.io/github/SAP/ui5-server) + +## Documentation +UI5 Server documentation can be found here: [ui5.github.io/cli](https://ui5.github.io/cli/v4/pages/Server/) + +The UI5 Server API Reference can be found here: [`@ui5/server`](https://ui5.github.io/cli/v4/api/module-@ui5_server.html) + +## Contributing +Please check our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md). + +## Support +Please follow our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#report-an-issue) on how to report an issue. + +Please report issues in the main [UI5 CLI](https://github.com/UI5/cli) repository. + +## Release History +See [CHANGELOG.md](CHANGELOG.md). diff --git a/packages/server/REUSE.toml b/packages/server/REUSE.toml new file mode 100644 index 00000000000..9305700ea99 --- /dev/null +++ b/packages/server/REUSE.toml @@ -0,0 +1,23 @@ +version = 1 +SPDX-PackageName = "ui5-server" +SPDX-PackageSupplier = "SAP OpenUI5 " +SPDX-PackageDownloadLocation = "https://github.com/SAP/ui5-server" +SPDX-PackageComment = "The code in this project may include calls to APIs (“API Callsâ€) of\n SAP or third-party products or services developed outside of this project\n (“External Productsâ€).\n “APIs†means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project’s code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls." + +[[annotations]] +path = "**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2025 SAP SE or an SAP affiliate company and UI5 CLI contributors" +SPDX-License-Identifier = "Apache-2.0" + +[[annotations]] +path = "lib/middleware/serveIndex/**" +precedence = "aggregate" +SPDX-FileCopyrightText = ["2010 Sencha Inc.", "2011 LearnBoost", "2011 TJ Holowaychuk", "2014-2015 Douglas Christopher Wilson"] +SPDX-License-Identifier = "MIT" + +[[annotations]] +path = "lib/middleware/testRunner/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2025 SAP SE or an SAP affiliate company and OpenUI5 contributors" +SPDX-License-Identifier = "Apache-2.0" diff --git a/packages/server/azure-pipelines.yml b/packages/server/azure-pipelines.yml new file mode 100644 index 00000000000..acbe7250177 --- /dev/null +++ b/packages/server/azure-pipelines.yml @@ -0,0 +1,72 @@ +# Node.js +# Build a general Node.js project with npm. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- v4 + +variables: + CI: true + +strategy: + matrix: + linux_node_lts_20_min_version: + imageName: 'ubuntu-24.04' + node_version: 20.11.0 + linux_node_22_min_version: + imageName: 'ubuntu-24.04' + node_version: 22.1.0 + linux_node_lts_20: + imageName: 'ubuntu-24.04' + node_version: 20.x + mac_node_lts_20: + imageName: 'macos-13' + node_version: 20.x + windows_node_lts_20: + imageName: 'windows-2022' + node_version: 20.x + linux_node_22: + imageName: 'ubuntu-24.04' + node_version: 22.x + mac_node_22: + imageName: 'macos-13' + node_version: 22.x + windows_node_22: + imageName: 'windows-2022' + node_version: 22.x + +pool: + vmImage: $(imageName) + +steps: +- task: NodeTool@0 + inputs: + versionSpec: $(node_version) + displayName: Install Node.js + +- script: npm ci + displayName: Install Dependencies + +- script: npm ls --prod + displayName: Check for missing / extraneous Dependencies + +- script: npm run test-azure + displayName: Run Tests + +- task: PublishTestResults@2 + displayName: Publish Test Results + condition: succeededOrFailed() + inputs: + testResultsFormat: 'JUnit' + testResultsFiles: '$(System.DefaultWorkingDirectory)/test-results.xml' + +- task: PublishCodeCoverageResults@2 + displayName: Publish Test Coverage Results + condition: succeededOrFailed() + inputs: + summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage/cobertura-coverage.xml' + +- script: npm run coverage + displayName: Run Test Natively in Case of Failures + condition: failed() diff --git a/packages/server/eslint.common.config.js b/packages/server/eslint.common.config.js new file mode 100644 index 00000000000..07876d526f0 --- /dev/null +++ b/packages/server/eslint.common.config.js @@ -0,0 +1,99 @@ +import jsdoc from "eslint-plugin-jsdoc"; +import ava from "eslint-plugin-ava"; +import globals from "globals"; +import js from "@eslint/js"; +import google from "eslint-config-google"; + +export default [{ + ignores: [ // Common ignore patterns across all tooling repos + "**/coverage/", + "test/tmp/", + "test/expected/", + "test/fixtures/", + "**/docs/", + "**/jsdocs/", + ], +}, js.configs.recommended, google, ava.configs["flat/recommended"], { + name: "Common ESLint config used for all tooling repos", + + plugins: { + jsdoc, + }, + + languageOptions: { + globals: { + ...globals.node, + }, + + ecmaVersion: 2023, + sourceType: "module", + }, + + settings: { + jsdoc: { + mode: "jsdoc", + + tagNamePreference: { + return: "returns", + augments: "extends", + }, + }, + }, + + rules: { + "indent": ["error", "tab"], + "linebreak-style": ["error", "unix"], + + "quotes": ["error", "double", { + allowTemplateLiterals: true, + }], + + "semi": ["error", "always"], + "no-negated-condition": "off", + "require-jsdoc": "off", + "no-mixed-requires": "off", + + "max-len": ["error", { + code: 120, + ignoreUrls: true, + ignoreRegExpLiterals: true, + }], + + "no-implicit-coercion": [2, { + allow: ["!!"], + }], + + "comma-dangle": "off", + "no-tabs": "off", + "no-console": 2, // Disallow console.log() + "no-eval": 2, + // The following rule must be disabled as of ESLint 9. + // It's removed and causes issues when present + // https://eslint.org/docs/latest/rules/valid-jsdoc + "valid-jsdoc": 0, + "jsdoc/check-examples": 0, + "jsdoc/check-param-names": 2, + "jsdoc/check-tag-names": 2, + "jsdoc/check-types": 2, + "jsdoc/no-undefined-types": 0, + "jsdoc/require-description": 0, + "jsdoc/require-description-complete-sentence": 0, + "jsdoc/require-example": 0, + "jsdoc/require-hyphen-before-param-description": 0, + "jsdoc/require-param": 2, + "jsdoc/require-param-description": 0, + "jsdoc/require-param-name": 2, + "jsdoc/require-param-type": 2, + "jsdoc/require-returns": 0, + "jsdoc/require-returns-description": 0, + "jsdoc/require-returns-type": 2, + + "jsdoc/tag-lines": [2, "any", { + startLines: 1, + }], + + "jsdoc/valid-types": 0, + "ava/assertion-arguments": 0, + }, +} +]; diff --git a/packages/server/eslint.config.js b/packages/server/eslint.config.js new file mode 100644 index 00000000000..a277a372817 --- /dev/null +++ b/packages/server/eslint.config.js @@ -0,0 +1,12 @@ +import eslintCommonConfig from "./eslint.common.config.js"; + +export default [ + ...eslintCommonConfig, // Load common ESLint config + { + // Add project-specific ESLint config rules here + // in order to override common config + ignores: [ + "lib/middleware/testRunner/", + ] + } +]; diff --git a/packages/server/jsdoc-plugin.cjs b/packages/server/jsdoc-plugin.cjs new file mode 100644 index 00000000000..cd7ef446d0f --- /dev/null +++ b/packages/server/jsdoc-plugin.cjs @@ -0,0 +1,9 @@ +/* + * This plugin fixes unexpected JSDoc behavior that prevents us from using types that start with an at-sign (@). + * JSDoc doesn't see "{@" as a valid type expression, probably as there's also {@link ...}. + */ +exports.handlers = { + jsdocCommentFound: function(e) { + e.comment = e.comment.replace(/{@ui5\//g, "{ @ui5/"); + } +}; diff --git a/packages/server/jsdoc.json b/packages/server/jsdoc.json new file mode 100644 index 00000000000..2f1e5eee3d7 --- /dev/null +++ b/packages/server/jsdoc.json @@ -0,0 +1,61 @@ +{ + "tags": { + "allowUnknownTags": false + }, + "source": { + "include": ["README.md"], + "includePattern": ".+\\.js$", + "excludePattern": "(node_modules(\\\\|/))" + }, + "plugins": [ + "./jsdoc-plugin.cjs" + ], + "opts": { + "encoding": "utf8", + "destination": "jsdocs/", + "recurse": true, + "verbose": true, + "access": "public" + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": false, + "default": { + "useLongnameInNav": true + } + }, + "openGraph": { + "title": "UI5 CLI - API Reference", + "type": "website", + "image": "https://ui5.github.io/cli/v4/images/UI5_logo_wide.png", + "site_name": "UI5 CLI - API Reference", + "url": "https://ui5.github.io/cli/" + }, + "docdash": { + "sectionOrder": [ + "Modules", + "Namespaces", + "Classes", + "Externals", + "Events", + "Mixins", + "Tutorials", + "Interfaces" + ], + "meta": { + "title": "UI5 CLI - API Reference - UI5 Server", + "description": "UI5 CLI - API Reference - UI5 Server", + "keyword": "openui5 sapui5 ui5 build development tool api reference" + }, + "search": true, + "wrap": true, + "menu": { + "GitHub": { + "href": "https://github.com/SAP/ui5-server", + "target": "_blank", + "class": "menu-item", + "id": "github_link" + } + } + } +} diff --git a/packages/server/lib/middleware/MiddlewareManager.js b/packages/server/lib/middleware/MiddlewareManager.js new file mode 100644 index 00000000000..36894892e4d --- /dev/null +++ b/packages/server/lib/middleware/MiddlewareManager.js @@ -0,0 +1,314 @@ +import middlewareRepository from "./middlewareRepository.js"; +import MiddlewareUtil from "./MiddlewareUtil.js"; +import {getLogger} from "@ui5/logger"; +const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); + +/** + * @private + * @typedef {object} MiddlewareResources + * @property {@ui5/fs/AbstractReader} all Reader or Collection to read resources of the + * root project and its dependencies + * @property {@ui5/fs/AbstractReader} rootProject Reader or Collection to read resources of + * the project the server is started in + * @memberof @ui5/server/internal/MiddlewareManager + */ + +/** + * The MiddlewareManager + * + * @private + * @class + * @alias @ui5/server/internal/MiddlewareManager + */ +class MiddlewareManager { + constructor({graph, rootProject, resources, options = { + sendSAPTargetCSP: false, + serveCSPReports: false + }}) { + if (!graph || !rootProject || !resources || !resources.all || + !resources.rootProject || !resources.dependencies) { + throw new Error("[MiddlewareManager]: One or more mandatory parameters not provided"); + } + this.graph = graph; + this.rootProject = rootProject; + this.resources = resources; + this.options = options; + + this.middleware = Object.create(null); + this.middlewareExecutionOrder = []; + this.middlewareUtil = new MiddlewareUtil({graph, project: rootProject}); + } + + /** + * Applies the middleware to + * + * @private + * @param {object} app The express application object + * @returns {Promise>} Promise resolving to an Array with a length of the number + * of added middlewares. The entries of the Array have a value of undefined. + */ + async applyMiddleware(app) { + await this.addStandardMiddleware(); + await this.addCustomMiddleware(); + + return this.middlewareExecutionOrder.map((name) => { + const m = this.middleware[name]; + app.use(m.mountPath, m.middleware); + }); + } + + /** + * Adds the given middleware configuration + * + * @private + * @param {string} middlewareName The name of the middleware + * @param {object} [options] The Options of the middleware + * @param {object} [options.customMiddleware] The custom middleware + * @param {Function} [options.wrapperCallback] Callback called when middleware is called + * @param {string} [options.mountPath="/"] The path hosting the middleware + * @param {string} [options.beforeMiddleware] The name of the middleware called before the added middleware + * @param {string} [options.afterMiddleware] The name of the middleware called after the added middleware + */ + async addMiddleware(middlewareName, { + customMiddleware, wrapperCallback, mountPath = "/", + beforeMiddleware, afterMiddleware + } = {}) { + if (this.middleware[middlewareName]) { + throw new Error(`A middleware with the name ${middlewareName} has already been added`); + } + + let middlewareCallback; + if (customMiddleware) { + middlewareCallback = customMiddleware; + } else { + const middlewareInfo = await middlewareRepository.getMiddleware(middlewareName); + if (wrapperCallback) { + middlewareCallback = wrapperCallback(middlewareInfo); + } else { + middlewareCallback = middlewareInfo.middleware; + } + } + + if (this.middlewareExecutionOrder.includes(middlewareName)) { + throw new Error(`Middleware ${middlewareName} already added to execution order. This should not happen.`); + } + + if (beforeMiddleware || afterMiddleware) { + const refMiddlewareName = beforeMiddleware || afterMiddleware; + let refMiddlewareIdx = this.middlewareExecutionOrder.indexOf(refMiddlewareName); + + if (refMiddlewareName === "connectUi5Proxy") { + throw new Error( + `Standard middleware "connectUi5Proxy", referenced by middleware "${middlewareName}" ` + + `in project ${this.middlewareUtil.getProject()}, ` + + `has been removed in this version of UI5 CLI and can't be referenced anymore. ` + + `Please see the migration guide at https://ui5.github.io/cli/updates/migrate-v3/`); + } + if (refMiddlewareIdx === -1) { + throw new Error(`Could not find middleware ${refMiddlewareName}, referenced by custom ` + + `middleware ${middlewareName}`); + } + if (afterMiddleware) { + // Insert after index of referenced middleware + refMiddlewareIdx++; + } + this.middlewareExecutionOrder.splice(refMiddlewareIdx, 0, middlewareName); + } else { + this.middlewareExecutionOrder.push(middlewareName); + } + + this.middleware[middlewareName] = { + middleware: await Promise.resolve(middlewareCallback({ + resources: this.resources, + middlewareUtil: this.middlewareUtil + })), + mountPath + }; + } + + /** + * Adds all registered standard middlewares + * + * @private + * @returns {Promise} Resolving to undefined once all standard middlewares are added + */ + async addStandardMiddleware() { + await this.addMiddleware("csp", { + wrapperCallback: ({middleware: cspModule}) => { + const oCspConfig = { + allowDynamicPolicySelection: true, + allowDynamicPolicyDefinition: true, + definedPolicies: { + "sap-target-level-1": + "default-src 'self'; " + + "script-src 'self' 'unsafe-eval'; " + + "style-src 'self' 'unsafe-inline'; " + + "font-src 'self' data:; " + + "img-src 'self' https: http: data: blob:; " + + "media-src 'self' https: http: data: blob:; " + + "object-src blob:; " + + "frame-src 'self' https: gap: data: blob: mailto: tel:; " + + "worker-src 'self' blob:; " + + "child-src 'self' blob:; " + + "connect-src 'self' https: wss:; " + + "base-uri 'self';", + "sap-target-level-2": + "default-src 'self'; " + + "script-src 'self'; " + + "style-src 'self' 'unsafe-inline'; " + + "font-src 'self' data:; " + + "img-src 'self' https: http: data: blob:; " + + "media-src 'self' https: http: data: blob:; " + + "object-src blob:; " + + "frame-src 'self' https: gap: data: blob: mailto: tel:; " + + "worker-src 'self' blob:; " + + "child-src 'self' blob:; " + + "connect-src 'self' https: wss:; " + + "base-uri 'self';", + "sap-target-level-3": + "default-src 'self'; " + + "script-src 'self'; " + + "style-src 'self'; " + + "font-src 'self'; " + + "img-src 'self' https:; " + + "media-src 'self' https:; " + + "object-src 'self'; " + + "frame-src 'self' https: gap: mailto: tel:; " + + "worker-src 'self'; " + + "child-src 'self'; " + + "connect-src 'self' https: wss:; " + + "base-uri 'self';" + } + }; + if (this.options.sendSAPTargetCSP) { + const defaultSAPTargetConfig = { + defaultPolicy: "sap-target-level-1", + defaultPolicyIsReportOnly: true, + defaultPolicy2: "sap-target-level-3", + defaultPolicy2IsReportOnly: true, + ignorePaths: ["test-resources/sap/ui/qunit/testrunner.html"] + }; + Object.assign(oCspConfig, defaultSAPTargetConfig); + + if (typeof this.options.sendSAPTargetCSP === "object") { + for (const [name, value] of Object.entries(this.options.sendSAPTargetCSP)) { + if (!hasOwn(defaultSAPTargetConfig, name)) { + throw new TypeError( + `Unknown SAP Target CSP configuration option '${name}'. Allowed options are ` + + `${Object.keys(defaultSAPTargetConfig)}`); + } + oCspConfig[name] = value; + } + } + } + if (this.options.serveCSPReports) { + Object.assign(oCspConfig, { + serveCSPReports: true, + }); + } + return () => { + return cspModule("sap-ui-xx-csp-policy", oCspConfig); + }; + } + }); + await this.addMiddleware("compression"); + await this.addMiddleware("cors"); + await this.addMiddleware("discovery", { + mountPath: "/discovery" + }); + await this.addMiddleware("serveResources"); + await this.addMiddleware("testRunner"); + await this.addMiddleware("serveThemes"); + await this.addMiddleware("versionInfo", { + mountPath: "/resources/sap-ui-version.json" + }); + // Handle anything but read operations *before* the serveIndex middleware + // as it will reject them with a 405 (Method not allowed) instead of 404 like our old tooling + await this.addMiddleware("nonReadRequests"); + await this.addMiddleware("serveIndex", { + wrapperCallback: ({middleware: middleware}) => { + return ({resources, middlewareUtil}) => middleware({ + resources, + middlewareUtil, + simpleIndex: this.options.simpleIndex + }); + } + }); + } + + /** + * Adds all registered custom middlewares + * + * @private + * @returns {Promise} Resolving to undefined once all custom middlewares are added + */ + async addCustomMiddleware() { + const project = this.graph.getRoot(); + const projectCustomMiddleware = project.getCustomMiddleware(); + if (!projectCustomMiddleware.length === 0) { + return; // No custom middleware defined + } + + for (let i = 0; i < projectCustomMiddleware.length; i++) { + const middlewareDef = projectCustomMiddleware[i]; + if (!middlewareDef.name) { + throw new Error(`Missing name for custom middleware definition of project ${project.getName()} ` + + `at index ${i}`); + } + if (middlewareDef.beforeMiddleware && middlewareDef.afterMiddleware) { + throw new Error( + `Custom middleware definition ${middlewareDef.name} of project ${project.getName()} ` + + `defines both "beforeMiddleware" and "afterMiddleware" parameters. Only one must be defined.`); + } + if (!middlewareDef.beforeMiddleware && !middlewareDef.afterMiddleware) { + throw new Error( + `Custom middleware definition ${middlewareDef.name} of project ${project.getName()} ` + + `defines neither a "beforeMiddleware" nor an "afterMiddleware" parameter. One must be defined.`); + } + const customMiddleware = this.graph.getExtension(middlewareDef.name); + if (!customMiddleware) { + throw new Error( + `Could not find custom middleware ${middlewareDef.name}, ` + + `referenced by project ${project.getName()}`); + } + + let middlewareName = middlewareDef.name; + if (this.middleware[middlewareName]) { + // Middleware is already known + // => add a suffix to allow for multiple configurations of the same middleware + let suffixCounter = 0; + while (this.middleware[middlewareName]) { + suffixCounter++; // Start at 1 + middlewareName = `${middlewareDef.name}--${suffixCounter}`; + } + } + + await this.addMiddleware(middlewareName, { + customMiddleware: async ({resources, middlewareUtil}) => { + const params = { + resources, + options: { + configuration: middlewareDef.configuration + } + }; + + const specVersion = customMiddleware.getSpecVersion(); + if (specVersion.gte("3.0")) { + params.options.middlewareName = middlewareName; + params.log = getLogger(`server:custom-middleware:${middlewareDef.name}`); + } + const middlewareUtilInterface = middlewareUtil.getInterface(specVersion); + if (middlewareUtilInterface) { + params.middlewareUtil = middlewareUtilInterface; + } + return (await customMiddleware.getMiddleware())(params); + }, + mountPath: middlewareDef.mountPath, + beforeMiddleware: middlewareDef.beforeMiddleware, + afterMiddleware: middlewareDef.afterMiddleware + }); + } + } +} + +export default MiddlewareManager; diff --git a/packages/server/lib/middleware/MiddlewareUtil.js b/packages/server/lib/middleware/MiddlewareUtil.js new file mode 100644 index 00000000000..8b977c6a520 --- /dev/null +++ b/packages/server/lib/middleware/MiddlewareUtil.js @@ -0,0 +1,273 @@ +import parseurl from "parseurl"; +import mime from "mime-types"; +import { + createReaderCollection, + createReaderCollectionPrioritized, + createResource, + createFilterReader, + createLinkReader, + createFlatReader +} from "@ui5/fs/resourceFactory"; + +/** + * Convenience functions for UI5 Server middleware. + * An instance of this class is passed to every standard UI5 Server middleware. + * Custom middleware that define a specification version >= 2.0 will also receive an instance + * of this class as part of the parameters of their create-middleware function. + * + * The set of functions that can be accessed by a custom middleware depends on the specification + * version defined for the extension. + * + * @public + * @class + * @alias @ui5/server/middleware/MiddlewareUtil + * @hideconstructor + */ +class MiddlewareUtil { + /** + * + * @param {object} parameters + * @param {@ui5/project/graph/ProjectGraph} parameters.graph Relevant ProjectGraph + * @param {@ui5/project/specifications/Project} parameters.project Project that is being served + * @public + */ + constructor({graph, project}) { + if (!graph) { + throw new Error(`Missing parameter "graph"`); + } + if (!project) { + throw new Error(`Missing parameter "project"`); + } + this._graph = graph; + this._project = project; + } + + /** + * Returns the [pathname]{@link https://developer.mozilla.org/en-US/docs/Web/API/URL/pathname} + * of a given request. Any escape sequences will be decoded. + *

+ * This method is only available to custom middleware extensions defining + * Specification Version 2.0 and above. + * + * @param {object} req Request object + * @returns {string} [Pathname]{@link https://developer.mozilla.org/en-US/docs/Web/API/URL/pathname} + * of the given request + * @public + */ + getPathname(req) { + let {pathname} = parseurl(req); + pathname = decodeURIComponent(pathname); + return pathname; + } + + /** + * MIME Info + * + * @example + * const mimeInfo = { + * "type": "text/html", + * "charset": "utf-8", + * "contentType": "text/html; charset=utf-8" + * }; + * + * @public + * @typedef {object} MimeInfo + * @property {string} type Detected content-type for the given resource path + * @property {string} charset Default charset for the detected content-type + * @property {string} contentType Calculated content-type header value + * @memberof @ui5/server/middleware/MiddlewareUtil + */ + /** + * Returns MIME information derived from a given resource path. + *

+ * This method is only available to custom middleware extensions defining + * Specification Version 2.0 and above. + * + * @param {object} resourcePath + * @returns {@ui5/server/middleware/MiddlewareUtil.MimeInfo} + * @public + */ + getMimeInfo(resourcePath) { + const type = mime.lookup(resourcePath) || "application/octet-stream"; + const charset = mime.charset(type); + return { + type, + charset, + contentType: type + (charset ? "; charset=" + charset : "") + }; + } + /** + * Specification Version-dependent [Project]{@link @ui5/project/specifications/Project} interface. + * For details on individual functions, see [Project]{@link @ui5/project/specifications/Project} + * + * @public + * @typedef {object} @ui5/server/middleware/MiddlewareUtil~ProjectInterface + * @property {Function} getType Get the project type + * @property {Function} getName Get the project name + * @property {Function} getVersion Get the project version + * @property {Function} getNamespace Get the project namespace + * @property {Function} getRootReader Get the project rootReader + * @property {Function} getReader Get the project reader, defaulting to "runtime" style instead of "buildtime" + * @property {Function} getRootPath Get the local File System path of the project's root directory + * @property {Function} getSourcePath Get the local File System path of the project's source directory + * @property {Function} getCustomConfiguration Get the project Custom Configuration + * @property {Function} isFrameworkProject Check whether the project is a UI5-Framework project + * @property {Function} getFrameworkName Get the project's framework name configuration + * @property {Function} getFrameworkVersion Get the project's framework version configuration + * @property {Function} getFrameworkDependencies Get the project's framework dependencies configuration + */ + + /** + * Retrieve a single project from the dependency graph + * + *

+ * This method is only available to custom server middleware extensions defining + * Specification Version 3.0 and above. + * + * @param {string|@ui5/fs/Resource} [projectNameOrResource] + * Name of the project to retrieve or a Resource instance to retrieve the associated project for. + * Defaults to the name of the current root project + * @returns {@ui5/server/middleware/MiddlewareUtil~ProjectInterface|undefined} + * Specification Version-dependent interface to the Project instance or undefined + * if the project name is unknown or the provided resource is not associated with any project. + * @public + */ + getProject(projectNameOrResource) { + if (projectNameOrResource) { + if (typeof projectNameOrResource === "string" || projectNameOrResource instanceof String) { + // A project name has been provided + return this._graph.getProject(projectNameOrResource); + } else { + // A Resource instance has been provided + return projectNameOrResource.getProject(); + } + } + // No parameter has been provided, default to the root project + return this._project; + } + + /** + * Retrieve a list of direct dependencies of a given project from the dependency graph. + * Note that this list does not include transitive dependencies. + * + *

+ * This method is only available to custom server middleware extensions defining + * Specification Version 3.0 and above. + * + * @param {string} [projectName] Name of the project to retrieve. + * Defaults to the name of the current root project + * @returns {string[]} Names of all direct dependencies + * @throws {Error} If the requested project is unknown to the graph + * @public + */ + getDependencies(projectName) { + return this._graph.getDependencies(projectName || this._project.getName()); + } + + /** + * Specification Version-dependent set of [@ui5/fs/resourceFactory]{@link @ui5/fs/resourceFactory} + * functions provided to middleware. + * For details on individual functions, see [@ui5/fs/resourceFactory]{@link @ui5/fs/resourceFactory} + * + * @public + * @typedef {object} @ui5/server/middleware/MiddlewareUtil~resourceFactory + * @property {Function} createResource Creates a [Resource]{@link @ui5/fs/Resource}. + * Accepts the same parameters as the [Resource]{@link @ui5/fs/Resource} constructor. + * @property {Function} createReaderCollection Creates a reader collection: + * [ReaderCollection]{@link @ui5/fs/ReaderCollection} + * @property {Function} createReaderCollectionPrioritized Creates a prioritized reader collection: + * [ReaderCollectionPrioritized]{@link @ui5/fs/ReaderCollectionPrioritized} + * @property {Function} createFilterReader + * Create a [Filter-Reader]{@link @ui5/fs/readers/Filter} with the given reader. + * @property {Function} createLinkReader + * Create a [Link-Reader]{@link @ui5/fs/readers/Filter} with the given reader. + * @property {Function} createFlatReader Create a [Link-Reader]{@link @ui5/fs/readers/Link} + * where all requests are prefixed with /resources/. + */ + + /** + * Provides limited access to [@ui5/fs/resourceFactory]{@link @ui5/fs/resourceFactory} functions + * + *

+ * This attribute is only available to custom server middleware extensions defining + * Specification Version 3.0 and above. + * + * @type {@ui5/server/middleware/MiddlewareUtil~resourceFactory} + * @public + */ + resourceFactory = { + createResource, + createReaderCollection, + createReaderCollectionPrioritized, + createFilterReader, + createLinkReader, + createFlatReader, + }; + + /** + * Get an interface to an instance of this class that only provides those functions + * that are supported by the given custom middleware extension specification version. + * + * @param {@ui5/project/specifications/SpecificationVersion} specVersion + * SpecVersionComparator instance of the custom server middleware + * @returns {object} An object with bound instance methods supported by the given specification version + */ + getInterface(specVersion) { + if (specVersion.lt("2.0")) { + // Custom middleware defining specVersion <2.0 does not have access to any MiddlewareUtil API + return undefined; + } + + const baseInterface = {}; + bindFunctions(this, baseInterface, [ + "getPathname", "getMimeInfo" + ]); + + if (specVersion.gte("3.0")) { + // getProject function, returning an interfaced project instance + baseInterface.getProject = (projectName) => { + const project = this.getProject(projectName); + const baseProjectInterface = {}; + bindFunctions(project, baseProjectInterface, [ + "getType", "getName", "getVersion", "getNamespace", + "getRootReader", "getRootPath", "getSourcePath", + "getCustomConfiguration", "isFrameworkProject", "getFrameworkName", + "getFrameworkVersion", "getFrameworkDependencies" + ]); + // Project#getReader defaults to style "buildtime". However ui5-server uses + // style "runtime". The main difference is that for some project types (like applications) + // the /resources/ path prefix is omitted for "runtime". Also, no builder resource- + // exclude configuration is applied. + // Therefore default to style "runtime" here so that custom middleware will commonly work with + // the same paths as ui5-server and no unexpected builder-excludes. + baseProjectInterface.getReader = function(options = {style: "runtime"}) { + return project.getReader(options); + }; + return baseProjectInterface; + }; + // getDependencies function, returning an array of project names + baseInterface.getDependencies = (projectName) => { + return this.getDependencies(projectName); + }; + + baseInterface.resourceFactory = Object.create(null); + [ + // Once new functions get added, extract this array into a variable + // and enhance based on spec version once new functions get added + "createResource", "createReaderCollection", "createReaderCollectionPrioritized", + "createFilterReader", "createLinkReader", "createFlatReader", + ].forEach((factoryFunction) => { + baseInterface.resourceFactory[factoryFunction] = this.resourceFactory[factoryFunction]; + }); + } + return baseInterface; + } +} + +function bindFunctions(sourceObject, targetObject, funcNames) { + funcNames.forEach((funcName) => { + targetObject[funcName] = sourceObject[funcName].bind(sourceObject); + }); +} + +export default MiddlewareUtil; diff --git a/packages/server/lib/middleware/csp.js b/packages/server/lib/middleware/csp.js new file mode 100644 index 00000000000..0f5e6d81c5a --- /dev/null +++ b/packages/server/lib/middleware/csp.js @@ -0,0 +1,200 @@ +import parseurl from "parseurl"; +import Router from "router"; +import querystring from "node:querystring"; +import {getLogger} from "@ui5/logger"; +import bodyParser from "body-parser"; + +const log = getLogger("server:middleware:csp"); + +const HEADER_CONTENT_SECURITY_POLICY = "Content-Security-Policy"; +const HEADER_CONTENT_SECURITY_POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"; +const rPolicy = /^([-_a-zA-Z0-9]+)(:report-only|:ro)?$/i; + +function addHeader(res, header, value) { + const current = res.getHeader(header); + if ( current == null ) { + res.setHeader(header, value); + } else if ( Array.isArray(current) ) { + res.setHeader(header, [...current, value]); + } else { + res.setHeader(header, [current, value]); + } +} + +/** + * Evaluates if the uriPath is either part of the pathName or in the request header referer + * + * @param {string} uriPath path in the URI, e.g. "test-resources/sap/ui/qunit/testrunner.html" + * @param {http.IncomingMessage} req request + * @param {string} pathName path name of the request + * @returns {boolean} whether or not path fragment is in pathName or in referer header + */ +function containsPath(uriPath, req, pathName) { + return pathName.includes(uriPath) || + (req.headers["referer"] && req.headers["referer"].includes(uriPath)); +} + + +/** + * @typedef {object} CspConfig + * @property {boolean} allowDynamicPolicySelection + * @property {boolean} allowDynamicPolicyDefinition + * @property {string} defaultPolicy + * @property {boolean} defaultPolicyIsReportOnly + * @property {string} defaultPolicy2 + * @property {boolean} defaultPolicy2IsReportOnly + * @property {object} definedPolicies + * @property {boolean} serveCSPReports whether to serve the csp resources + * @property {string[]} ignorePaths URI paths which are ignored by the CSP reports, + * e.g. ["test-resources/sap/ui/qunit/testrunner.html"] + */ + +/** + * @module @ui5/server/middleware/csp + * Middleware which enables CSP (content security policy) support + * @see https://www.w3.org/TR/CSP/ + * @param {string} sCspUrlParameterName + * @param {CspConfig} oConfig + * @returns {Function} Returns a server middleware closure. + */ +function createMiddleware(sCspUrlParameterName, oConfig) { + const { + allowDynamicPolicySelection = false, + allowDynamicPolicyDefinition = false, + defaultPolicy = "default", + defaultPolicyIsReportOnly = false, + defaultPolicy2 = null, + defaultPolicy2IsReportOnly = false, + definedPolicies = {}, + serveCSPReports = false, + ignorePaths = [] + } = oConfig; + + + /** + * List of CSP Report entries + */ + const cspReportEntries = []; + const router = new Router(); + // .csplog + // body parser is required to parse csp-report in body (json) + if (serveCSPReports) { + router.post("/.ui5/csp/report.csplog", bodyParser.json({type: "application/csp-report"})); + } + router.post("/.ui5/csp/report.csplog", function(req, res, next) { + if (req.headers["content-type"] === "application/csp-report") { + if (!serveCSPReports) { + res.end(); + return; + } + // Write the violation into an array + // They can be retrieved via a request to '/.ui5/csp/csp-reports.json' + if (typeof req.body !== "object") { + const error = new Error(`No body content available: ${req.url}`); + log.error(error); + next(error); + return; + } + const cspReportObject = req.body["csp-report"]; + if (cspReportObject) { + // extract the csp-report and add it to the cspReportEntries list + cspReportEntries.push(cspReportObject); + } + res.end(); + } else { + next(); + } + }); + + // csp-reports.json + if (serveCSPReports) { + router.get("/.ui5/csp/csp-reports.json", (req, res, next) => { + // serve csp reports + const body = JSON.stringify({ + "csp-reports": cspReportEntries + }, null, "\t"); + res.writeHead(200, { + "Content-Type": "application/json" + }); + res.end(body); + }); + } + + // html get requests + // add csp headers + router.use((req, res, next) => { + const oParsedURL = parseurl(req); + + // add CSP headers only to get requests for *.html pages + if (req.method !== "GET" || !oParsedURL.pathname.endsWith(".html")) { + next(); + return; + } + + const containsIgnorePath = (ignoredPath) => { + return containsPath(ignoredPath, req, oParsedURL.pathname); + }; + + if (ignorePaths.some(containsIgnorePath)) { + next(); + return; + } + + // If default policies are defined, they will even be send without a present URL parameter. + let policy = defaultPolicy && definedPolicies[defaultPolicy]; + let reportOnly = defaultPolicyIsReportOnly; + const policy2 = defaultPolicy2 && definedPolicies[defaultPolicy2]; + const reportOnly2 = defaultPolicy2IsReportOnly; + + const oQuery = querystring.parse(oParsedURL.query); + const sCspUrlParameterValue = oQuery[sCspUrlParameterName]; + if (sCspUrlParameterValue) { + const mPolicyMatch = rPolicy.exec(sCspUrlParameterValue); + + if (mPolicyMatch) { + if (allowDynamicPolicySelection) { + policy = definedPolicies[mPolicyMatch[1]]; + reportOnly = mPolicyMatch[2] !== undefined; + } // else: ignore parameter + } else if (allowDynamicPolicyDefinition) { + // Custom CSP policy directives get passed as part of the CSP URL-Parameter value + if ( sCspUrlParameterValue.endsWith(":report-only") ) { + policy = sCspUrlParameterValue.slice(0, - ":report-only".length); + reportOnly = true; + } else if ( sCspUrlParameterValue.endsWith(":ro") ) { + policy = sCspUrlParameterValue.slice(0, - ":ro".length); + reportOnly = true; + } else { + policy = sCspUrlParameterValue; + reportOnly = false; + } + } // else: parameter ignored + } + + // collect header values based on configuration + if (policy) { + if (reportOnly) { + // Add report-uri. This is mandatory for the report-only mode. + addHeader(res, HEADER_CONTENT_SECURITY_POLICY_REPORT_ONLY, + policy + " report-uri /.ui5/csp/report.csplog;"); + } else { + addHeader(res, HEADER_CONTENT_SECURITY_POLICY, policy); + } + } + if (policy2) { + if (reportOnly2) { + // Add report-uri. This is mandatory for the report-only mode. + addHeader(res, HEADER_CONTENT_SECURITY_POLICY_REPORT_ONLY, + policy2 + " report-uri /.ui5/csp/report.csplog;"); + } else { + addHeader(res, HEADER_CONTENT_SECURITY_POLICY, policy2); + } + } + + next(); + }); + + return router; +} + +export default createMiddleware; diff --git a/packages/server/lib/middleware/discovery.js b/packages/server/lib/middleware/discovery.js new file mode 100644 index 00000000000..732700d7976 --- /dev/null +++ b/packages/server/lib/middleware/discovery.js @@ -0,0 +1,115 @@ +const librariesPattern = /([A-Z0-9._%+-/]+)\/[A-Z0-9._]*\.library$/i; +const testPagesPattern = /(([A-Z0-9._%+-]+\/)+([A-Z_0-9-\\.]+)\.(html|htm))$/i; +const urlPattern = /\/(app_pages|all_libs|all_tests)(?:[?#].*)?$/; + +/** + * Creates and returns the middleware to discover project files. + * + * List project files with URL (needed exclusively by the OpenUI5 testsuite): + *
    + *
  • /discovery/app_pages: get application pages
  • + *
  • /discovery/all_libs: list all libraries
  • + *
  • /discovery/all_tests: list all tests
  • + *
+ * + * @module @ui5/server/middleware/discovery + * @param {object} parameters Parameters + * @param {@ui5/server/internal/MiddlewareManager.middlewareResources} parameters.resources Parameters + * @returns {Function} Returns a server middleware closure. + */ +function createMiddleware({resources}) { + return function discoveryMiddleware(req, res, next) { + const parts = urlPattern.exec(req.url); + const type = parts && parts[1]; + if (!type) { + next(); + return; + } + + const response = []; + + function sendResponse() { + const responseData = {}; + response.sort((a, b) => { + if (type === "app_pages" || type === "all_libs") { + return a.entry.localeCompare(b.entry); + } else { + return a.lib.localeCompare(b.lib); + } + }); + responseData[type] = response; + res.writeHead(200, { + "Content-Type": "application/json" + }); + res.end(JSON.stringify(responseData)); + } + + if (type === "app_pages") { + resources.rootProject.byGlob("/**/*.{html,htm}").then(function(resources) { + resources.forEach(function(resource) { + const relPath = resource.getPath().substr(1); // cut off leading "/" + response.push({ + entry: relPath + }); + }); + sendResponse(); + }); + } else if (type === "all_libs") { + resources.all.byGlob([ + "/resources/**/*.library" + ]).then(function(resources) { + resources.forEach(function(resource) { + const relPath = resource.getPath().substr(11); // cut off leading "/resources/" + const match = librariesPattern.exec(relPath); + if (match) { + response.push({ + entry: match[1] + }); + } + }); + sendResponse(); + }); + } else if (type === "all_tests") { + Promise.all([ + resources.all.byGlob("/resources/**/*.library"), + resources.all.byGlob("/test-resources/**/*.{html,htm}") + ]).then(function(results) { + const libraryResources = results[0]; + const testPageResources = results[1]; + const libs = Object.create(null); + + libraryResources.forEach(function(resource) { + const relPath = resource.getPath().substr(11); // cut off leading "/resources/" + const match = librariesPattern.exec(relPath); + if (match) { + const lib = match[1]; + libs[lib + "/"] = lib.replace(/\//g, "."); + } + }); + + const libPrefixes = Object.keys(libs).sort().reverse(); + testPageResources.forEach(function(resource) { + const relPath = resource.getPath().substr(16); // cut off leading "/test-resources/" + if (testPagesPattern.test(relPath)) { + libPrefixes.some(function(lib) { + if (relPath.startsWith(lib)) { + response.push({ + lib: libs[lib], + name: relPath.substr(lib.length), + url: "../" + relPath + }); + return true; // abort loop + } + }); + } + }); + + sendResponse(); + }); + } else { + next(new Error(`Unknown discovery type "${type}"`)); + } + }; +} + +export default createMiddleware; diff --git a/packages/server/lib/middleware/helper/generateLibraryManifest.js b/packages/server/lib/middleware/helper/generateLibraryManifest.js new file mode 100644 index 00000000000..dfafe25d340 --- /dev/null +++ b/packages/server/lib/middleware/helper/generateLibraryManifest.js @@ -0,0 +1,20 @@ +import createManifestProcessor from "@ui5/builder/processors/manifestCreator"; + +export default async function generateLibraryManifest(middlewareUtil, dotLibResource) { + const project = dotLibResource.getProject(); + const libResources = await project.getReader().byGlob( + `/resources/**/*.{js,json,library,less,css,theming,theme,properties}`); + + const res = await createManifestProcessor({ + libraryResource: dotLibResource, + namespace: project.getNamespace(), + resources: libResources, + getProjectVersion: (projectName) => { + return middlewareUtil.getProject(projectName)?.getVersion(); + } + }); + if (res) { + res.setProject(project); + return res; + } +} diff --git a/packages/server/lib/middleware/middlewareRepository.js b/packages/server/lib/middleware/middlewareRepository.js new file mode 100644 index 00000000000..a9e0d25a1b1 --- /dev/null +++ b/packages/server/lib/middleware/middlewareRepository.js @@ -0,0 +1,54 @@ +const middlewareInfos = { + compression: {path: "compression"}, + cors: {path: "cors"}, + csp: {path: "./csp.js"}, + serveResources: {path: "./serveResources.js"}, + serveIndex: {path: "./serveIndex.js"}, + discovery: {path: "./discovery.js"}, + versionInfo: {path: "./versionInfo.js"}, + serveThemes: {path: "./serveThemes.js"}, + testRunner: {path: "./testRunner.js"}, + nonReadRequests: {path: "./nonReadRequests.js"} +}; + +// see @ui5/server/internal/middlewareRepository#getMiddleware +async function getMiddleware(middlewareName) { + const middlewareInfo = middlewareInfos[middlewareName]; + + if (!middlewareInfo) { + throw new Error(`middlewareRepository: Unknown Middleware ${middlewareName}`); + } + try { + const {default: middleware} = await import(middlewareInfo.path); + return { + middleware + }; + } catch (err) { + throw new Error( + `middlewareRepository: Failed to require middleware module for ${middlewareName}:\n${err.stack}`); + } +} +/** + * @private + * @typedef {object} module:@ui5/server/internal/middlewareRepository~Middleware + * @property {object} middleware The middleware + */ + +/** + * @private + * @module @ui5/server/internal/middlewareRepository + * @borrows getMiddleware as getMiddleware + */ +export default { + + /** + * Determines the desired middleware + * + * @private + * @static + * @function + * @param {string} middlewareName The name of the middleware + * @returns {module:@ui5/server/internal/middlewareRepository~Middleware} The middleware + */ + getMiddleware: getMiddleware +}; diff --git a/packages/server/lib/middleware/nonReadRequests.js b/packages/server/lib/middleware/nonReadRequests.js new file mode 100644 index 00000000000..bf2bb4c5bb9 --- /dev/null +++ b/packages/server/lib/middleware/nonReadRequests.js @@ -0,0 +1,23 @@ +/** + * Creates and returns the middleware to handle non read requests. + * + * Handles non read requests (POST, PUT, DELETE...) and returns an error 404, + * because those operations aren't supported by the server. + * + * @module @ui5/server/middleware/nonReadRequests + * @returns {Function} Returns a server middleware closure. + */ +function createMiddleware() { + return function nonReadRequests(req, res, next) { + // Handle anything but read operations *before* the serveIndex middleware + // as it will reject them with a 405 (Method not allowed) instead of 404 like our old tooling + if (req.method !== "GET" && req.method !== "HEAD" && req.method !== "OPTIONS") { + res.statusCode = 404; + res.end(`Cannot ${req.method} ${req.url}`); + } else { + next(); + } + }; +} + +export default createMiddleware; diff --git a/packages/server/lib/middleware/serveIndex.js b/packages/server/lib/middleware/serveIndex.js new file mode 100644 index 00000000000..d3742c7aca1 --- /dev/null +++ b/packages/server/lib/middleware/serveIndex.js @@ -0,0 +1,124 @@ +import {getLogger} from "@ui5/logger"; +const log = getLogger("server:middleware:serveIndex"); +import mime from "mime-types"; +import serveIndex from "./serveIndex/serveIndex.cjs"; + +const KB = 1024; +const MB = KB * KB; +const GB = KB * KB * KB; + +/** + * Returns the mime type of the given resource + * + * @param {module:@ui5/fs/Resource} resource the resource + * @returns {string} mime type + */ +function getMimeType(resource) { + return mime.lookup(resource.getPath()) || "application/octet-stream"; +} + +/** + * Converts the given bytes into a proper human readable size + * + * @param {number} bytes bytes + * @returns {string} human readable size + */ +function formatSize(bytes) { + let result; + if (bytes < KB) { + result = bytes + " Bytes"; + } else if (bytes < MB) { + result = Number.parseFloat(bytes / KB).toFixed(2) + " KB"; + } else if (bytes < GB) { + result = Number.parseFloat(bytes / MB).toFixed(2) + " MB"; + } else { + result = Number.parseFloat(bytes / GB).toFixed(2) + " GB"; + } + return result; +} + +/** + * Creates a resource info object which is used to create the HTML + * content for the resource listing + * + * @param {@ui5/fs/Resource} resource the resource to convert + * @returns {object} resource info object + */ +function createResourceInfo(resource) { + const stat = resource.getStatInfo(); + const isDir = stat.isDirectory(); + return { + path: resource.getPath() + (isDir ? "/" : ""), + name: resource.getName() + (isDir ? "/" : ""), + isDir: isDir, + mimetype: isDir ? "" : getMimeType(resource), + lastModified: new Date(stat.mtime).toLocaleString(), + size: formatSize(stat.size), + sizeInBytes: stat.size, + project: resource.getProject()?.getName() || "", + projectPath: resource.getProject()?.getRootPath() || "" + }; +} + +/** + * Creates a resource info array from the given resource array + * + * @param {@ui5/fs/Resource[]} resources an array of resources + * @returns {object[]} sorted array of resource infos + */ +function createResourceInfos(resources) { + return resources.map((item, i) => { + return createResourceInfo(item); + }).sort((a, b) => { + if (a.isDir && !b.isDir) { + return -1; + } else if (!a.isDir && b.isDir) { + return 1; + } else { + // numbers will be sorted by name as string like the FS does! + return a.name.localeCompare(b.name); + } + }); +} + +/** + * Creates and returns the middleware to serve a resource index. + * + * @module @ui5/server/middleware/serveIndex + * @param {object} parameters Parameters + * @param {object} parameters.resources Contains the resource reader or collection to access project related files + * @param {@ui5/fs/AbstractReader} parameters.resources.all Resource collection which contains the workspace + * and the project dependencies + * @param {boolean} [parameters.simpleIndex=false] Use a simplified view for the server directory listing + * @param {boolean} [parameters.showHidden=true] Show hidden files in the server directory listing + * @returns {Function} Returns a server middleware closure. + */ + +function createMiddleware({resources, middlewareUtil, simpleIndex = false, showHidden = true}) { + return function(req, res, next) { + const pathname = middlewareUtil.getPathname(req); + log.verbose("\n Listing index of " + pathname); + const glob = pathname + (pathname.endsWith("/") ? "*" : "/*"); + resources.all.byGlob(glob, {nodir: false}).then((resources) => { + if (!resources || resources.length === 0) { // Not found + next(); + return; + } + + const resourceInfos = createResourceInfos(resources); + serveIndex({ + req, + res, + next, + simpleIndex, + showHidden, + resourceInfos, + pathname + }); + }).catch((err) => { + next(err); + }); + }; +} + +export default createMiddleware; diff --git a/packages/server/lib/middleware/serveIndex/directory.html b/packages/server/lib/middleware/serveIndex/directory.html new file mode 100644 index 00000000000..55a2d88d800 --- /dev/null +++ b/packages/server/lib/middleware/serveIndex/directory.html @@ -0,0 +1,82 @@ + + + + + + Index of {directory} + + + + + +
+

~{linked-path}

+ {files} +
+ + diff --git a/packages/server/lib/middleware/serveIndex/icons/application_xp.png b/packages/server/lib/middleware/serveIndex/icons/application_xp.png new file mode 100644 index 00000000000..d22860a3166 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/application_xp.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/application_xp_terminal.png b/packages/server/lib/middleware/serveIndex/icons/application_xp_terminal.png new file mode 100644 index 00000000000..c28dd63812d Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/application_xp_terminal.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/box.png b/packages/server/lib/middleware/serveIndex/icons/box.png new file mode 100644 index 00000000000..8443c23eb94 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/box.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/cd.png b/packages/server/lib/middleware/serveIndex/icons/cd.png new file mode 100644 index 00000000000..ef4322357cb Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/cd.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/controller.png b/packages/server/lib/middleware/serveIndex/icons/controller.png new file mode 100644 index 00000000000..5cf76ed029a Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/controller.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/drive.png b/packages/server/lib/middleware/serveIndex/icons/drive.png new file mode 100644 index 00000000000..37b7c9b27d3 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/drive.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/film.png b/packages/server/lib/middleware/serveIndex/icons/film.png new file mode 100644 index 00000000000..b0ce7bb198a Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/film.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/folder.png b/packages/server/lib/middleware/serveIndex/icons/folder.png new file mode 100644 index 00000000000..698f3d30368 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/folder.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/font.png b/packages/server/lib/middleware/serveIndex/icons/font.png new file mode 100644 index 00000000000..b7960db9dae Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/font.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/image.png b/packages/server/lib/middleware/serveIndex/icons/image.png new file mode 100644 index 00000000000..fc3c393caa3 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/image.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/map.png b/packages/server/lib/middleware/serveIndex/icons/map.png new file mode 100644 index 00000000000..f90ef25ec7f Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/map.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page.png b/packages/server/lib/middleware/serveIndex/icons/page.png new file mode 100644 index 00000000000..03ddd799fa0 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_add.png b/packages/server/lib/middleware/serveIndex/icons/page_add.png new file mode 100644 index 00000000000..d5bfa0719bc Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_add.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_attach.png b/packages/server/lib/middleware/serveIndex/icons/page_attach.png new file mode 100644 index 00000000000..89ee2da0753 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_attach.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_code.png b/packages/server/lib/middleware/serveIndex/icons/page_code.png new file mode 100644 index 00000000000..f7ea90419d9 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_code.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_copy.png b/packages/server/lib/middleware/serveIndex/icons/page_copy.png new file mode 100644 index 00000000000..195dc6d6c36 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_copy.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_delete.png b/packages/server/lib/middleware/serveIndex/icons/page_delete.png new file mode 100644 index 00000000000..3141467c678 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_delete.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_edit.png b/packages/server/lib/middleware/serveIndex/icons/page_edit.png new file mode 100644 index 00000000000..046811ed7a6 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_edit.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_error.png b/packages/server/lib/middleware/serveIndex/icons/page_error.png new file mode 100644 index 00000000000..f07f449a44f Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_error.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_excel.png b/packages/server/lib/middleware/serveIndex/icons/page_excel.png new file mode 100644 index 00000000000..eb6158eb5ca Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_excel.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_find.png b/packages/server/lib/middleware/serveIndex/icons/page_find.png new file mode 100644 index 00000000000..2f193889f7e Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_find.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_gear.png b/packages/server/lib/middleware/serveIndex/icons/page_gear.png new file mode 100644 index 00000000000..8e83281c5f8 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_gear.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_go.png b/packages/server/lib/middleware/serveIndex/icons/page_go.png new file mode 100644 index 00000000000..80fe1ed0cc7 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_go.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_green.png b/packages/server/lib/middleware/serveIndex/icons/page_green.png new file mode 100644 index 00000000000..de8e003f9fb Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_green.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_key.png b/packages/server/lib/middleware/serveIndex/icons/page_key.png new file mode 100644 index 00000000000..d6626cb09eb Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_key.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_lightning.png b/packages/server/lib/middleware/serveIndex/icons/page_lightning.png new file mode 100644 index 00000000000..7e568703d64 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_lightning.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_link.png b/packages/server/lib/middleware/serveIndex/icons/page_link.png new file mode 100644 index 00000000000..312eab0914a Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_link.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_paintbrush.png b/packages/server/lib/middleware/serveIndex/icons/page_paintbrush.png new file mode 100644 index 00000000000..246a2f0b426 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_paintbrush.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_paste.png b/packages/server/lib/middleware/serveIndex/icons/page_paste.png new file mode 100644 index 00000000000..968f073fddd Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_paste.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_red.png b/packages/server/lib/middleware/serveIndex/icons/page_red.png new file mode 100644 index 00000000000..0b18247da58 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_red.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_refresh.png b/packages/server/lib/middleware/serveIndex/icons/page_refresh.png new file mode 100644 index 00000000000..cf347c7d468 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_refresh.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_save.png b/packages/server/lib/middleware/serveIndex/icons/page_save.png new file mode 100644 index 00000000000..caea546af54 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_save.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white.png b/packages/server/lib/middleware/serveIndex/icons/page_white.png new file mode 100644 index 00000000000..8b8b1ca0000 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_acrobat.png b/packages/server/lib/middleware/serveIndex/icons/page_white_acrobat.png new file mode 100644 index 00000000000..8f8095e46fa Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_acrobat.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_actionscript.png b/packages/server/lib/middleware/serveIndex/icons/page_white_actionscript.png new file mode 100644 index 00000000000..159b2407519 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_actionscript.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_add.png b/packages/server/lib/middleware/serveIndex/icons/page_white_add.png new file mode 100644 index 00000000000..aa23dde3746 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_add.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_c.png b/packages/server/lib/middleware/serveIndex/icons/page_white_c.png new file mode 100644 index 00000000000..34a05cccf06 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_c.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_camera.png b/packages/server/lib/middleware/serveIndex/icons/page_white_camera.png new file mode 100644 index 00000000000..f501a593a4e Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_camera.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_cd.png b/packages/server/lib/middleware/serveIndex/icons/page_white_cd.png new file mode 100644 index 00000000000..848bdaf3f15 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_cd.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_code.png b/packages/server/lib/middleware/serveIndex/icons/page_white_code.png new file mode 100644 index 00000000000..0c76bd12977 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_code.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_code_red.png b/packages/server/lib/middleware/serveIndex/icons/page_white_code_red.png new file mode 100644 index 00000000000..87a69145075 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_code_red.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_coldfusion.png b/packages/server/lib/middleware/serveIndex/icons/page_white_coldfusion.png new file mode 100644 index 00000000000..c66011fb0fb Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_coldfusion.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_compressed.png b/packages/server/lib/middleware/serveIndex/icons/page_white_compressed.png new file mode 100644 index 00000000000..2b6b1007f33 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_compressed.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_copy.png b/packages/server/lib/middleware/serveIndex/icons/page_white_copy.png new file mode 100644 index 00000000000..a9f31a278e1 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_copy.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_cplusplus.png b/packages/server/lib/middleware/serveIndex/icons/page_white_cplusplus.png new file mode 100644 index 00000000000..a87cf847cb7 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_cplusplus.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_csharp.png b/packages/server/lib/middleware/serveIndex/icons/page_white_csharp.png new file mode 100644 index 00000000000..ffb8fc932f3 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_csharp.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_cup.png b/packages/server/lib/middleware/serveIndex/icons/page_white_cup.png new file mode 100644 index 00000000000..0a7d6f4a6f6 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_cup.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_database.png b/packages/server/lib/middleware/serveIndex/icons/page_white_database.png new file mode 100644 index 00000000000..bddba1f98ca Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_database.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_delete.png b/packages/server/lib/middleware/serveIndex/icons/page_white_delete.png new file mode 100644 index 00000000000..af1ecaf2981 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_delete.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_dvd.png b/packages/server/lib/middleware/serveIndex/icons/page_white_dvd.png new file mode 100644 index 00000000000..4cc537af0b3 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_dvd.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_edit.png b/packages/server/lib/middleware/serveIndex/icons/page_white_edit.png new file mode 100644 index 00000000000..b93e77600de Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_edit.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_error.png b/packages/server/lib/middleware/serveIndex/icons/page_white_error.png new file mode 100644 index 00000000000..9fc5a0a103d Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_error.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_excel.png b/packages/server/lib/middleware/serveIndex/icons/page_white_excel.png new file mode 100644 index 00000000000..b977d7e52e2 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_excel.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_find.png b/packages/server/lib/middleware/serveIndex/icons/page_white_find.png new file mode 100644 index 00000000000..58184363707 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_find.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_flash.png b/packages/server/lib/middleware/serveIndex/icons/page_white_flash.png new file mode 100644 index 00000000000..5769120b1b6 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_flash.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_freehand.png b/packages/server/lib/middleware/serveIndex/icons/page_white_freehand.png new file mode 100644 index 00000000000..8d719df5205 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_freehand.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_gear.png b/packages/server/lib/middleware/serveIndex/icons/page_white_gear.png new file mode 100644 index 00000000000..106f5aa3611 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_gear.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_get.png b/packages/server/lib/middleware/serveIndex/icons/page_white_get.png new file mode 100644 index 00000000000..e4a1ecba1b6 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_get.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_go.png b/packages/server/lib/middleware/serveIndex/icons/page_white_go.png new file mode 100644 index 00000000000..7e62a924bc5 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_go.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_h.png b/packages/server/lib/middleware/serveIndex/icons/page_white_h.png new file mode 100644 index 00000000000..e902abb0767 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_h.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_horizontal.png b/packages/server/lib/middleware/serveIndex/icons/page_white_horizontal.png new file mode 100644 index 00000000000..1d2d0a49870 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_horizontal.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_key.png b/packages/server/lib/middleware/serveIndex/icons/page_white_key.png new file mode 100644 index 00000000000..d6164845228 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_key.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_lightning.png b/packages/server/lib/middleware/serveIndex/icons/page_white_lightning.png new file mode 100644 index 00000000000..7215d1e8b06 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_lightning.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_link.png b/packages/server/lib/middleware/serveIndex/icons/page_white_link.png new file mode 100644 index 00000000000..bf7bd1c9bfd Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_link.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_magnify.png b/packages/server/lib/middleware/serveIndex/icons/page_white_magnify.png new file mode 100644 index 00000000000..f6b74cc40f8 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_magnify.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_medal.png b/packages/server/lib/middleware/serveIndex/icons/page_white_medal.png new file mode 100644 index 00000000000..d3fffb6d989 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_medal.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_office.png b/packages/server/lib/middleware/serveIndex/icons/page_white_office.png new file mode 100644 index 00000000000..a65bcb3e1e9 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_office.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_paint.png b/packages/server/lib/middleware/serveIndex/icons/page_white_paint.png new file mode 100644 index 00000000000..23a37b891c2 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_paint.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_paintbrush.png b/packages/server/lib/middleware/serveIndex/icons/page_white_paintbrush.png new file mode 100644 index 00000000000..f907e44b333 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_paintbrush.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_paste.png b/packages/server/lib/middleware/serveIndex/icons/page_white_paste.png new file mode 100644 index 00000000000..5b2cbb3fd02 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_paste.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_php.png b/packages/server/lib/middleware/serveIndex/icons/page_white_php.png new file mode 100644 index 00000000000..7868a25945c Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_php.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_picture.png b/packages/server/lib/middleware/serveIndex/icons/page_white_picture.png new file mode 100644 index 00000000000..134b6693687 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_picture.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_powerpoint.png b/packages/server/lib/middleware/serveIndex/icons/page_white_powerpoint.png new file mode 100644 index 00000000000..c4eff0387d5 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_powerpoint.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_put.png b/packages/server/lib/middleware/serveIndex/icons/page_white_put.png new file mode 100644 index 00000000000..884ffd6f0aa Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_put.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_ruby.png b/packages/server/lib/middleware/serveIndex/icons/page_white_ruby.png new file mode 100644 index 00000000000..f59b7c4365f Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_ruby.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_stack.png b/packages/server/lib/middleware/serveIndex/icons/page_white_stack.png new file mode 100644 index 00000000000..44084add79b Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_stack.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_star.png b/packages/server/lib/middleware/serveIndex/icons/page_white_star.png new file mode 100644 index 00000000000..3a1441c9a12 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_star.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_swoosh.png b/packages/server/lib/middleware/serveIndex/icons/page_white_swoosh.png new file mode 100644 index 00000000000..e7708292ada Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_swoosh.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_text.png b/packages/server/lib/middleware/serveIndex/icons/page_white_text.png new file mode 100644 index 00000000000..813f712f726 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_text.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_text_width.png b/packages/server/lib/middleware/serveIndex/icons/page_white_text_width.png new file mode 100644 index 00000000000..d9cf13256f4 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_text_width.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_tux.png b/packages/server/lib/middleware/serveIndex/icons/page_white_tux.png new file mode 100644 index 00000000000..52699bfee0c Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_tux.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_vector.png b/packages/server/lib/middleware/serveIndex/icons/page_white_vector.png new file mode 100644 index 00000000000..4a05955b337 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_vector.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_visualstudio.png b/packages/server/lib/middleware/serveIndex/icons/page_white_visualstudio.png new file mode 100644 index 00000000000..a0a433dfbb6 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_visualstudio.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_width.png b/packages/server/lib/middleware/serveIndex/icons/page_white_width.png new file mode 100644 index 00000000000..1eb880947dd Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_width.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_word.png b/packages/server/lib/middleware/serveIndex/icons/page_white_word.png new file mode 100644 index 00000000000..ae8ecbf4767 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_word.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_world.png b/packages/server/lib/middleware/serveIndex/icons/page_white_world.png new file mode 100644 index 00000000000..6ed2490ed14 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_world.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_wrench.png b/packages/server/lib/middleware/serveIndex/icons/page_white_wrench.png new file mode 100644 index 00000000000..fecadd08afe Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_wrench.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_white_zip.png b/packages/server/lib/middleware/serveIndex/icons/page_white_zip.png new file mode 100644 index 00000000000..fd4bbccdf16 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_white_zip.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_word.png b/packages/server/lib/middleware/serveIndex/icons/page_word.png new file mode 100644 index 00000000000..834cdfaf48a Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_word.png differ diff --git a/packages/server/lib/middleware/serveIndex/icons/page_world.png b/packages/server/lib/middleware/serveIndex/icons/page_world.png new file mode 100644 index 00000000000..b8895ddecf5 Binary files /dev/null and b/packages/server/lib/middleware/serveIndex/icons/page_world.png differ diff --git a/packages/server/lib/middleware/serveIndex/serveIndex.cjs b/packages/server/lib/middleware/serveIndex/serveIndex.cjs new file mode 100644 index 00000000000..ffc1f0e0745 --- /dev/null +++ b/packages/server/lib/middleware/serveIndex/serveIndex.cjs @@ -0,0 +1,419 @@ +/* + * serve-index + * + * Version: 1.9.1 + * + * Author: Douglas Christopher Wilson + * Web: https://github.com/expressjs/serve-index/tree/20e83c893b701c3a117a0f6836be0f5a818bb925 + * + * Licensed under + * MIT License, see "/LICENSES/MIT.txt" + * + * Copyright (c) 2010 Sencha Inc. + * Copyright (c) 2011 LearnBoost + * Copyright (c) 2011 TJ Holowaychuk + * Copyright (c) 2014-2015 Douglas Christopher Wilson + * + */ + +const escapeHtml = require("escape-html"); +const mime = require("mime-types"); +const path = require("path"); +const fs = require("graceful-fs"); +const normalize = path.normalize; +const sep = path.sep; +const join = path.join; +const extname = path.extname; +const cache = {}; + +/** + * Map html `files`, returning an html unordered list. + * + * @private + */ + +function createHtmlFileList(files, view) { + let html = "
    " + + (view === "details" ? ( + "
  • " + + "Name" + + "Size" + + "Modified" + + "Project" + + "
  • ") : ""); + + html += files.map(function(file) { + const classes = []; + const isDir = file.isDir; + const path = file.path; + + classes.push("icon"); + + if (isDir) { + classes.push("icon-directory"); + } else { + const ext = extname(file.name); + const icon = iconLookup(file.name); + + classes.push("icon"); + classes.push("icon-" + ext.substring(1)); + + if (classes.indexOf(icon.className) === -1) { + classes.push(icon.className); + } + } + + const date = file.lastModified; + const size = file.size; + + return "
  • " + + "" + escapeHtml(file.name) + "" + + "" + escapeHtml(size) + "" + + "" + escapeHtml(date) + "" + + "" + escapeHtml(file.project) + "" + + "
  • "; + }).join("\n"); + + html += "
"; + + 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); + const body = str + .replace(/{style}/g, locals.style.concat(iconStyle(locals.fileList))) + .replace(/{files}/g, createHtmlFileList(locals.fileList, locals.viewName)) + .replace(/{directory}/g, escapeHtml(locals.directory)) + .replace(/{linked-path}/g, htmlPath(locals.directory)); + + callback(null, body); + }); + }; +} + +/** + * Map html `dir`, returning a linked path. + */ + +function htmlPath(dir) { + const parts = dir.split("/"); + const crumb = new Array(parts.length); + + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + + if (part) { + parts[i] = encodeURIComponent(part); + crumb[i] = "" + escapeHtml(part) + ""; + } + } + + return crumb.join(" / "); +} + +/** + * Get the icon data for the file name. + */ + +function iconLookup(filename) { + const ext = extname(filename); + + // try by extension + if (icons[ext]) { + return { + className: "icon-" + ext.substring(1), + fileName: icons[ext] + }; + } + + const 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("/", "-"), + fileName: icons[mimetype] + }; + } + + const suffix = mimetype.split("+")[1]; + + if (suffix && icons["+" + suffix]) { + return { + className: "icon-" + suffix, + fileName: icons["+" + suffix] + }; + } + + const 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. + */ + +function iconStyle(files) { + let iconName; + let i; + const list = []; + const rules = {}; + let selector; + const selectors = {}; + let style = ""; + + for (i = 0; i < files.length; i++) { + const file = files[i]; + + const isDir = file.isDir; + const icon = isDir ? + {className: "icon-directory", fileName: icons.folder} : + iconLookup(file.name); + iconName = icon.fileName; + + selector = "#files ." + icon.className + " .name"; + + if (!rules[iconName]) { + rules[iconName] = "background-image: url(data:image/png;base64," + load(iconName) + ");"; + selectors[iconName] = []; + list.push(iconName); + } + + if (selectors[iconName].indexOf(selector) === -1) { + selectors[iconName].push(selector); + } + } + + for (i = 0; i < list.length; i++) { + iconName = list[i]; + style += selectors[iconName].join(",\n") + " {\n " + rules[iconName] + "\n}\n"; + } + + return style; +} + +/** + * Load and cache the given `icon`. + * + * @param {string} icon + * @returns {string} + */ + +function load(icon) { + if (cache[icon]) return cache[icon]; + return cache[icon] = fs.readFileSync(path.join(__dirname, "icons", icon), "base64"); +} + +/** + * Normalizes the path separator from system separator + * to URL separator, aka `/`. + * + * @param {string} path + * @returns {string} + */ + +function normalizeSlashes(path) { + return path.split(sep).join("/"); +} + +/** + * Filter "hidden" `files`, aka files + * beginning with a `.`. + * + * @param resourceInfos + * @returns {Array} + */ + +function removeHidden(resourceInfos) { + return resourceInfos.filter(function(info) { + return !(/(^|\/)\.[^/.]/g).test(info.path); + }); +} + +/** + * Icon map. + */ + +const icons = { + // base icons + "default": "page_white.png", + "folder": "folder.png", + + // generic mime type icons + "font": "font.png", + "image": "image.png", + "text": "page_white_text.png", + "video": "film.png", + + // generic mime suffix icons + "+json": "page_white_code.png", + "+xml": "page_white_code.png", + "+zip": "box.png", + + // specific mime type icons + "application/javascript": "page_white_code_red.png", + "application/json": "page_white_code.png", + "application/msword": "page_white_word.png", + "application/pdf": "page_white_acrobat.png", + "application/postscript": "page_white_vector.png", + "application/rtf": "page_white_word.png", + "application/vnd.ms-excel": "page_white_excel.png", + "application/vnd.ms-powerpoint": "page_white_powerpoint.png", + "application/vnd.oasis.opendocument.presentation": "page_white_powerpoint.png", + "application/vnd.oasis.opendocument.spreadsheet": "page_white_excel.png", + "application/vnd.oasis.opendocument.text": "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", + + // other, extension-specific icons + ".accdb": "page_white_database.png", + ".apk": "box.png", + ".app": "application_xp.png", + ".as": "page_white_actionscript.png", + ".asp": "page_white_code.png", + ".aspx": "page_white_code.png", + ".bat": "application_xp_terminal.png", + ".bz2": "box.png", + ".c": "page_white_c.png", + ".cab": "box.png", + ".cfm": "page_white_coldfusion.png", + ".clj": "page_white_code.png", + ".cc": "page_white_cplusplus.png", + ".cgi": "application_xp_terminal.png", + ".cpp": "page_white_cplusplus.png", + ".cs": "page_white_csharp.png", + ".db": "page_white_database.png", + ".dbf": "page_white_database.png", + ".deb": "box.png", + ".dll": "page_white_gear.png", + ".dmg": "drive.png", + ".docx": "page_white_word.png", + ".erb": "page_white_ruby.png", + ".exe": "application_xp.png", + ".fnt": "font.png", + ".gam": "controller.png", + ".gz": "box.png", + ".h": "page_white_h.png", + ".ini": "page_white_gear.png", + ".iso": "cd.png", + ".jar": "box.png", + ".java": "page_white_cup.png", + ".jsp": "page_white_cup.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" +}; + +function handleRequest({req, res, next, showHidden, simpleIndex, resourceInfos, pathname}) { + const hidden = showHidden; // display hidden files + const view = simpleIndex ? "tiles" : "details"; + + if (!hidden) { + resourceInfos = removeHidden(resourceInfos); + } + + const stylesheet = join(__dirname, "style.css"); + const template = join(__dirname, "directory.html"); + + const render = createHtmlRender(template); + + // read stylesheet + fs.readFile(stylesheet, "utf8", function(err, style) { + if (err) { + return next(err); + } + + // create locals for rendering + const locals = { + directory: pathname, + fileList: resourceInfos, + path: path, + style: style, + viewName: view + }; + + // render html + render(locals, function(err, body) { + if (err) return next(err); + + res.writeHead(200, { + "Content-Type": "text/html; charset=utf-8", + "Content-Length": Buffer.byteLength(body, "utf8"), + "X-Content-Type-Options": "nosniff" + }); + res.end(body, "utf8"); + }); + }); +} + +module.exports = handleRequest; diff --git a/packages/server/lib/middleware/serveIndex/style.css b/packages/server/lib/middleware/serveIndex/style.css new file mode 100644 index 00000000000..df00b01e1bc --- /dev/null +++ b/packages/server/lib/middleware/serveIndex/style.css @@ -0,0 +1,262 @@ +* { + margin: 0; + padding: 0; + outline: 0; +} + +body { + padding: 80px 100px; + font: 13px "Helvetica Neue", "Lucida Grande", "Arial"; + background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9)); + background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9); + background-repeat: no-repeat; + color: #555; + -webkit-font-smoothing: antialiased; +} +h1, h2, h3 { + font-size: 22px; + color: #343434; +} +h1 em, h2 em { + padding: 0 5px; + font-weight: normal; +} +h1 { + font-size: 60px; +} +h2 { + margin-top: 10px; +} +h3 { + margin: 5px 0 10px 0; + padding-bottom: 5px; + border-bottom: 1px solid #eee; + font-size: 18px; +} +ul li { + list-style: none; +} +ul li:hover { + cursor: pointer; + color: #2e2e2e; +} +ul li .path { + padding-left: 5px; + font-weight: bold; +} +ul li .line { + padding-right: 5px; + font-style: italic; +} +ul li:first-child .path { + padding-left: 0; +} +p { + line-height: 1.5; +} +a { + color: #555; + text-decoration: none; +} +a:hover { + color: #303030; +} +#stacktrace { + margin-top: 15px; +} +.directory h1 { + margin-bottom: 15px; + font-size: 18px; +} +ul#files { + width: 100%; + height: 100%; + overflow: hidden; +} +ul#files li { + float: left; + width: 30%; + line-height: 25px; + margin: 1px; +} +ul#files li a { + display: block; + height: 25px; + border: 1px solid transparent; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + overflow: hidden; + white-space: nowrap; +} +ul#files li a:focus, +ul#files li a:hover { + background: rgba(255,255,255,0.65); + border: 1px solid #ececec; +} +ul#files li a.highlight { + -webkit-transition: background .4s ease-in-out; + background: #ffff4f; + border-color: #E9DC51; +} +#search { + display: block; + position: fixed; + top: 20px; + right: 20px; + width: 90px; + -webkit-transition: width ease 0.2s, opacity ease 0.4s; + -moz-transition: width ease 0.2s, opacity ease 0.4s; + -webkit-border-radius: 32px; + -moz-border-radius: 32px; + -webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -webkit-font-smoothing: antialiased; + text-align: left; + font: 13px "Helvetica Neue", Arial, sans-serif; + padding: 4px 10px; + border: none; + background: transparent; + margin-bottom: 0; + outline: none; + opacity: 0.7; + color: #888; +} +#search:focus { + width: 120px; + opacity: 1.0; +} + +/*views*/ +#files span { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + text-indent: 10px; +} +#files .name { + background-repeat: no-repeat; +} +#files .icon .name { + text-indent: 28px; +} + +/*tiles*/ +.view-tiles .name { + width: 100%; + background-position: 8px 5px; +} +.view-tiles .size, +.view-tiles .date { + display: none; +} + +/*details*/ +ul#files.view-details li { + float: none; + display: block; + width: 90%; +} +ul#files.view-details li.header { + height: 25px; + background: #000; + color: #fff; + font-weight: bold; +} +.view-details .header { + border-radius: 5px; +} +.view-details .name { + width: 30%; + background-position: 8px 5px; +} +.view-details .size { + width: 10%; +} +.view-details .date { + width: 30%; +} +.view-details .project { + width: 30%; +} +.view-details .size { + text-align: center; +} +.view-details .date, +.view-details .project { + text-align: right; +} + +/*mobile*/ +@media (max-width: 768px) { + body { + font-size: 13px; + line-height: 16px; + padding: 0; + } + #search { + position: static; + width: 100%; + font-size: 2em; + line-height: 1.8em; + text-indent: 10px; + border: 0; + border-radius: 0; + padding: 10px 0; + margin: 0; + } + #search:focus { + width: 100%; + border: 0; + opacity: 1; + } + .directory h1 { + font-size: 2em; + line-height: 1.5em; + color: #fff; + background: #000; + padding: 15px 10px; + margin: 0; + } + ul#files { + border-top: 1px solid #cacaca; + } + ul#files li { + float: none; + width: auto !important; + display: block; + border-bottom: 1px solid #cacaca; + font-size: 2em; + line-height: 1.2em; + text-indent: 0; + margin: 0; + } + ul#files li:nth-child(odd) { + background: #e0e0e0; + } + ul#files li a { + height: auto; + border: 0; + border-radius: 0; + padding: 15px 10px; + } + ul#files li a:focus, + ul#files li a:hover { + border: 0; + } + #files .header, + #files .size, + #files .date { + display: none !important; + } + #files .name { + float: none; + display: inline-block; + width: 100%; + text-indent: 0; + background-position: 0 50%; + } + #files .icon .name { + text-indent: 41px; + } +} diff --git a/packages/server/lib/middleware/serveResources.js b/packages/server/lib/middleware/serveResources.js new file mode 100644 index 00000000000..3e6c1d0ac63 --- /dev/null +++ b/packages/server/lib/middleware/serveResources.js @@ -0,0 +1,147 @@ +import {getLogger} from "@ui5/logger"; +const log = getLogger("server:middleware:serveResources"); +import replaceStream from "replacestream"; +import etag from "etag"; +import fresh from "fresh"; +import fsInterface from "@ui5/fs/fsInterface"; + +const rProperties = /\.properties$/i; +const rReplaceVersion = /\.(library|js|json)$/i; +const rManifest = /\/manifest\.json$/i; +const rResourcesPrefix = /^\/resources\//i; +const rTestResourcesPrefix = /^\/test-resources\//i; + +function isFresh(req, res) { + return fresh(req.headers, { + "etag": res.getHeader("ETag") + }); +} + +/** + * Creates and returns the middleware to serve application resources. + * + * @module @ui5/server/middleware/serveResources + * @param {object} parameters Parameters + * @param {@ui5/server/internal/MiddlewareManager.middlewareResources} parameters.resources Parameters + * @param {object} parameters.middlewareUtil [MiddlewareUtil]{@link @ui5/server/middleware/MiddlewareUtil} instance + * @returns {Function} Returns a server middleware closure. + */ +function createMiddleware({resources, middlewareUtil}) { + return async function serveResources(req, res, next) { + try { + const pathname = middlewareUtil.getPathname(req); + let resource = await resources.all.byPath(pathname); + if (!resource) { // Not found + if (!rManifest.test(pathname) || !rResourcesPrefix.test(pathname)) { + next(); + return; + } + log.verbose(`Could not find manifest.json for ${pathname}. ` + + `Checking for .library file to generate manifest.json from.`); + const {default: generateLibraryManifest} = await import("./helper/generateLibraryManifest.js"); + // Attempt to find a .library file, which is required for generating a manifest.json + const dotLibraryPath = pathname.replace(rManifest, "/.library"); + const dotLibraryResource = await resources.all.byPath(dotLibraryPath); + if (dotLibraryResource && dotLibraryResource.getProject()?.getType() === "library") { + resource = await generateLibraryManifest(middlewareUtil, dotLibraryResource); + } + if (!resource) { + // Not a library project, missing .library file or other reason for failed manifest.json generation + next(); + return; + } + } else if ( + rManifest.test(pathname) && !rTestResourcesPrefix.test(pathname) && + resource.getProject()?.getNamespace() + ) { + // Special handling for manifest.json file by adding additional content to the served manifest.json + // NOTE: This should only be done for manifest.json files that exist in the sources, + // not in test-resources. + // Files created by generateLibraryManifest (see above) should not be handled in here. + // Only manifest.json files in library / application projects should be handled. + // resource.getProject.getNamespace() returns null for all other kind of projects. + const {default: manifestEnhancer} = await import("@ui5/builder/processors/manifestEnhancer"); + await manifestEnhancer({ + resources: [resource], + // Ensure that only files within the manifest's project are accessible + // Using the "runtime" style to match the style used by the UI5 server + fs: fsInterface(resource.getProject().getReader({style: "runtime"})) + }); + } + + const resourcePath = resource.getPath(); + if (rProperties.test(resourcePath)) { + // Special handling for *.properties files escape non ascii characters. + const {default: nonAsciiEscaper} = await import("@ui5/builder/processors/nonAsciiEscaper"); + const project = resource.getProject(); + let propertiesFileSourceEncoding = project?.getPropertiesFileSourceEncoding?.(); + + if (!propertiesFileSourceEncoding) { + if (project && project.getSpecVersion().lte("1.1")) { + // default encoding to "ISO-8859-1" for old specVersions + propertiesFileSourceEncoding = "ISO-8859-1"; + } else { + // default encoding to "UTF-8" for all projects starting with specVersion 2.0 + propertiesFileSourceEncoding = "UTF-8"; + } + } + const encoding = nonAsciiEscaper.getEncodingFromAlias(propertiesFileSourceEncoding); + await nonAsciiEscaper({ + resources: [resource], options: { + encoding + } + }); + } + + const {contentType, charset} = middlewareUtil.getMimeInfo(resourcePath); + if (!res.getHeader("Content-Type")) { + res.setHeader("Content-Type", contentType); + } + + // Enable ETag caching + const statInfo = resource.getStatInfo(); + if (statInfo?.size !== undefined && !resource.isModified()) { + let etagHeader = etag(statInfo); + if (resource.getProject()) { + // Add project version to ETag to invalidate cache when project version changes. + // This is necessary to invalidate files with ${version} placeholders. + etagHeader = etagHeader.slice(0, -1) + `-${resource.getProject().getVersion()}"`; + } + res.setHeader("ETag", etagHeader); + } else { + // Fallback to buffer if stats are not available or insufficient or resource is modified. + // Modified resources must use the buffer for cache invalidation so that UI5 CLI changes + // invalidate the cache even when the original resource is not modified. + res.setHeader("ETag", etag(await resource.getBuffer())); + } + + if (isFresh(req, res)) { + // client has a fresh copy of the resource + res.statusCode = 304; + res.end(); + return; + } + + let stream = resource.getStream(); + + // Only execute version replacement for UTF-8 encoded resources because replaceStream will always output + // UTF-8 anyways. + // Also, only process .library, *.js and *.json files. Just like it's done in Application- + // and LibraryBuilder + if ((!charset || charset === "UTF-8") && rReplaceVersion.test(resourcePath)) { + if (resource.getProject()) { + stream.setEncoding("utf8"); + stream = stream.pipe(replaceStream("${version}", resource.getProject().getVersion())); + } else { + log.verbose(`Project missing from resource ${pathname}"`); + } + } + + stream.pipe(res); + } catch (err) { + next(err); + } + }; +} + +export default createMiddleware; diff --git a/packages/server/lib/middleware/serveThemes.js b/packages/server/lib/middleware/serveThemes.js new file mode 100644 index 00000000000..57771224805 --- /dev/null +++ b/packages/server/lib/middleware/serveThemes.js @@ -0,0 +1,123 @@ +import {ThemeBuilder} from "@ui5/builder/processors/themeBuilder"; +import fsInterface from "@ui5/fs/fsInterface"; +import {basename, dirname} from "node:path/posix"; +import etag from "etag"; +import fresh from "fresh"; +import parseurl from "parseurl"; + +function isFresh(req, res) { + return fresh(req.headers, { + "etag": res.getHeader("ETag") + }); +} + +// List of experimental css variables resources that should activate the "cssVariables" build +const cssVariablesThemeResources = [ + "css_variables.source.less", + "css_variables.css", + "library_skeleton.css", + "library_skeleton-RTL.css" +]; + +// List of resources that should be handled by the middleware +const themeResources = [ + "library.css", + "library-RTL.css", + "library-parameters.json", + ...cssVariablesThemeResources +]; + +/** + * Creates and returns the middleware to build themes. + * + * The theme is built in realtime. If a less file was modified, the theme build is triggered to rebuild the theme. + * + * @module @ui5/server/middleware/serveThemes + * @param {object} parameters Parameters + * @param {@ui5/server/internal/MiddlewareManager.middlewareResources} parameters.resources Parameters + * @param {object} parameters.middlewareUtil Specification version dependent interface to a + * [MiddlewareUtil]{@link @ui5/server/middleware/MiddlewareUtil} instance + * @returns {Function} Returns a server middleware closure. + */ +function createMiddleware({resources, middlewareUtil}) { + const builder = new ThemeBuilder({ + fs: fsInterface(resources.all) + }); + const buildOptions = Object.create(null); + + const currentRequests = Object.create(null); + + async function buildTheme(pathname) { + const filename = basename(pathname); + + if (cssVariablesThemeResources.includes(filename) && !buildOptions.cssVariables) { + // Activate CSS Variables build the first time a relevant resource is requested + buildOptions.cssVariables = true; + // Clear the cache to ensure that the build is executed again with "cssVariables: true" + builder.clearCache(); + } + + const sourceLessPath = dirname(pathname) + "/library.source.less"; + const sourceLessResource = await resources.all.byPath(sourceLessPath); + if (!sourceLessResource) { // Not found + return; + } + + const createdResources = await builder.build([sourceLessResource], buildOptions); + + // Pick requested file resource + const resource = createdResources.find((res) => basename(res.getPath()) === filename); + if (!resource) { + throw new Error(`Theme Build did not return requested file "${pathname}"`); + } + + return resource; + } + + async function sendResponse(req, res, resource) { + const resourcePath = resource.getPath(); + const {contentType} = middlewareUtil.getMimeInfo(resourcePath); + res.setHeader("Content-Type", contentType); + + const content = await resource.getBuffer(); + + res.setHeader("ETag", etag(content)); + + if (isFresh(req, res)) { + // client has a fresh copy of the resource + res.statusCode = 304; + res.end(); + return; + } + + res.end(content); + } + + return async function theme(req, res, next) { + try { + const pathname = parseurl(req).pathname; + const filename = basename(pathname); + if (!themeResources.includes(filename)) { + next(); + return; + } + + if (!currentRequests[pathname]) { + currentRequests[pathname] = buildTheme(pathname); + } + + const resource = await currentRequests[pathname]; + if (!resource) { + next(); + } else { + await sendResponse(req, res, resource); + } + + delete currentRequests[pathname]; + } catch (err) { + next(err); + } + }; +} + +export default createMiddleware; diff --git a/packages/server/lib/middleware/testRunner.js b/packages/server/lib/middleware/testRunner.js new file mode 100644 index 00000000000..71bb4f4b79b --- /dev/null +++ b/packages/server/lib/middleware/testRunner.js @@ -0,0 +1,62 @@ +import {promisify} from "node:util"; +import fs from "graceful-fs"; +const readFile = promisify(fs.readFile); +import {fileURLToPath} from "node:url"; +import mime from "mime-types"; +import parseurl from "parseurl"; +import {getLogger} from "@ui5/logger"; +const log = getLogger("server:middleware:testRunner"); + +const testRunnerResourceRegEx = /\/test-resources\/sap\/ui\/qunit\/(testrunner\.(html|css)|TestRunner.js)$/; +const resourceCache = Object.create(null); + +function serveResource(res, resourcePath, resourceContent) { + const type = mime.lookup(resourcePath) || "application/octet-stream"; + const charset = mime.charset(type); + const contentType = type + (charset ? "; charset=" + charset : ""); + + // resources served by this middleware do not change often + res.setHeader("Cache-Control", "public, max-age=1800"); + + res.setHeader("Content-Type", contentType); + res.end(resourceContent); +} + +/** + * Creates and returns the middleware to serve a resource index. + * + * @module @ui5/server/middleware/testRunner + * @param {object} parameters Parameters + * @param {object} parameters.resources Contains the resource reader or collection to access project related files + * @returns {Function} Returns a server middleware closure. + */ +function createMiddleware({resources}) { + return async function(req, res, next) { + try { + const pathname = parseurl(req).pathname; + const parts = testRunnerResourceRegEx.exec(pathname); + const resourceName = parts && parts[1]; + + if (resourceName) { // either "testrunner.html", "testrunner.css" or "TestRunner.js" (case sensitive!) + log.verbose(`Serving ${pathname}`); + let pResource; + if (!resourceCache[pathname]) { + const filePath = fileURLToPath(new URL(`./testRunner/${resourceName}`, import.meta.url)); + pResource = readFile(filePath, {encoding: "utf8"}); + resourceCache[pathname] = pResource; + } else { + pResource = resourceCache[pathname]; + } + + const resourceContent = await pResource; + serveResource(res, pathname, resourceContent); + } else { + next(); + } + } catch (err) { + next(err); + } + }; +} + +export default createMiddleware; diff --git a/packages/server/lib/middleware/testRunner/TestRunner.js b/packages/server/lib/middleware/testRunner/TestRunner.js new file mode 100644 index 00000000000..d7efe7ed8f9 --- /dev/null +++ b/packages/server/lib/middleware/testRunner/TestRunner.js @@ -0,0 +1,674 @@ +(function(window) { + "use strict"; + + /*global CollectGarbage, Handlebars */ + /* eslint radix: 0 */ + + /* + * Simulate the JSUnit Testsuite to collect the available + * test pages per Suite + */ + window.jsUnitTestSuite = function() {}; + window.jsUnitTestSuite.prototype.getTestPages = function() { + return this.aPages; + }; + window.jsUnitTestSuite.prototype.addTestPage = function(sTestPage) { + this.aPages = this.aPages || []; + // in case of running in the root context the testsuites right now + // generate an invalid URL because they assume that test-resources is + // the context path - this section makes sure to remove the duplicate + // test-resources segments in the path + if (sTestPage.indexOf("/test-resources/test-resources") === 0 || sTestPage.indexOf("/test-resources/resources") === 0) { + sTestPage = sTestPage.substr("/test-resources".length); + } + this.aPages.push(sTestPage); + }; + + function XHRQueue(iMaxParallelRequests, iWaitTime) { + this.iLimit = iMaxParallelRequests === undefined ? Infinity : iMaxParallelRequests; + this.iWaitTime = iWaitTime === undefined ? 0 : iWaitTime; + this.aPendingTasks = []; + this.oRunningTasks = new Set(); + this.iLastTaskExecution = -Infinity; + } + + XHRQueue.prototype.ajax = function(sUrl, options) { + var oTask = { + url: sUrl, + options: options + }; + oTask.promise = new Promise(function(resolve, reject) { + oTask.resolve = resolve; + oTask.reject = reject; + }); + this.aPendingTasks.push(oTask); + this._processNext(); + return oTask.promise; + }; + + XHRQueue.prototype._processNext = function() { + var iNow = Date.now(); + var iDelay = iNow - this.iLastTaskExecution; + if ( iDelay < this.iWaitTime ) { + setTimeout(function() { + this._processNext(); + }.bind(this), iDelay); + return; + } + if ( this.aPendingTasks.length > 0 && this.oRunningTasks.size < this.iLimit ) { + var oTask = this.aPendingTasks.shift(); + this.oRunningTasks.add(oTask); + this.iLastTaskExecution = iNow; + Promise.resolve(jQuery.ajax(oTask.url, oTask.options)) + .then(oTask.resolve, oTask.reject) + .finally(function() { + this.oRunningTasks.delete(oTask); + this._processNext(); + }.bind(this)); + } + }; + + var oXHRQueue = new XHRQueue(50, 2); + + /* + * Template for test results + */ + var sResultsTemplate = "{{#tests}}" + + "
" + + "

{{header}}

" + + "
    " + + "{{#results}}" + + "
  1. " + + "

    {{result.TestName}} ({{result.Failed}} ,{{result.Passed}} ,{{result.All}})

    " + + " Rerun" + + "
      " + + "{{#result.testmessages}}" + + "
    1. {{message}}" + + "
      {{expected}}" + + "
      {{actual}}" + + "
      {{diff}}" + + "
      {{source}}" + + "
    2. " + + "{{/result.testmessages}}" + + "
    " + + "
  2. " + + "{{/results}}" + + "
" + + "
" + + "{{/tests}}"; + + /** + * Utility class to find test pages and check them for being + * a testsuite or a QUnit testpage - also it returns the coverage + * results. + */ + window.sap = window.sap || {}; + window.sap.ui = window.sap.ui || {}; + window.sap.ui.qunit = window.sap.ui.qunit || {}; + window.sap.ui.qunit.TestRunner = { + + checkTestPage: function(sTestPage, bSequential) { + var oPromise = + this._checkTestPage(sTestPage, bSequential) + .then(function(aTestPages) { + return aTestPages; + }); + oPromise.done = oPromise.then; // compat for Deferred + oPromise.fail = oPromise.catch; // compat for Deferred + return oPromise; + }, + + _checkTestPage: function(sTestPage, bSequential) { + + var oPromise = new Promise(function(resolve, reject) { + + if (typeof sTestPage !== "string") { + window.console.log("QUnit: invalid test page specified"); + reject("QUnit: invalid test page specified"); + } + + /* + if (window.console && typeof window.console.log === "function") { + window.console.log("QUnit: checking test page: " + sTestPage); + }*/ + + // check for an existing test page and check for test suite or page + oXHRQueue.ajax(sTestPage).then(function(sData) { + if (/(?:window\.suite\s*=|function\s*suite\s*\(\s*\)\s*{)/.test(sData) + || (/data-sap-ui-testsuite/.test(sData) && !/sap\/ui\/test\/starter\/runTest/.test(sData)) + || /sap\/ui\/test\/starter\/createSuite/.test(sData) ) { + var $frame = jQuery("