Repository: ember-cli/ember-cli (js)

Issue:
Serve app at /rooturl when using the history location type
I noticed that ember-cli started defaulting locationType to history ~a year ago. This is great!

Unlike `auto` location, `history` location supports visiting the bare root url (i.e. no trailing slash) directly. For example, if your config contains `rootURL: '/example/``, with history location the app will work correctly when served at http://localhost/example (e.g. via nginx).

The issue I'm running into is that the ember-cli development server doesn't serve the app at this URL:

<img width="716" alt="image" src="https://user-images.githubusercontent.com/1151810/230393165-b665c85d-35bc-4dfc-b16a-02391f92ac48.png">

I think that we should modify the HistoryMiddleware to serve the app on the bare root url when using the history location type. Somewhere around here: https://github.com/ember-cli/ember-cli/blob/master/lib/tasks/server/middleware/history-support/index.js#L89-L91


Pull request description:
Serve app on root url without trailing slash
Closes #10246.

I confirmed that my test fails without the middleware change and passes after the middleware change.

I also tried this out locally in an application that specifies `rootURL: "/example"`. Note that after the application boots up, Ember does a client-side route transition to normalize the URL to include the trailing slash.


https://user-images.githubusercontent.com/1151810/230454814-8f3d406b-af3b-4484-aaa4-7deeed3d3d03.mov

Patch:
diff --git a/lib/tasks/server/middleware/history-support/index.js b/lib/tasks/server/middleware/history-support/index.js
index 7d787a4f5..b5950c7fe 100644
--- a/lib/tasks/server/middleware/history-support/index.js
+++ b/lib/tasks/server/middleware/history-support/index.js
@@ -87,8 +87,14 @@ class HistorySupportAddon {
       return false;
     }
     let rootURL = options.rootURL === '' ? '/' : cleanBaseURL(options.rootURL);
-    let rootURLRegexp = new RegExp(`^${rootURL}`);
-    return rootURLRegexp.test(req.path);
+    if (req.path.startsWith(rootURL)) {
+      return true;
+    }
+    // exactly match the rootURL without a trailing slash
+    if (req.path === rootURL.slice(0, -1)) {
+      return true;
+    }
+    return false;
   }
 }
 


Test patch:
diff --git a/tests/unit/tasks/server/express-server-test.js b/tests/unit/tasks/server/express-server-test.js
index 8c7de2d3b..9b552da69 100644
--- a/tests/unit/tasks/server/express-server-test.js
+++ b/tests/unit/tasks/server/express-server-test.js
@@ -752,6 +752,27 @@ describe('express-server', function () {
         });
       });
 
+      it('serves index.html when file not found (with rootURL) with auto/history location on root url without trailing slash', function (done) {
+        project._config = {
+          rootURL: '/foo',
+          locationType: 'history',
+        };
+
+        startServer('/foo').then(function () {
+          request(subject.app)
+            .get('/foo')
+            .set('accept', 'text/html')
+            .expect(200)
+            .expect('Content-Type', /html/)
+            .end(function (err) {
+              if (err) {
+                return done(err);
+              }
+              done();
+            });
+        });
+      });
+
       it('serves index.html when file not found (with rootURL) with custom history location', function (done) {
         project._config = {
           rootURL: '/',