Skip to content

Commit

Permalink
Enable multi page app (#88)
Browse files Browse the repository at this point in the history
* Try opening index.html only if we have to do it
* Prevent server from crash if no index.html
* Send a 404 status if file does not exist
* Allow to access indexes in sub directories if path is `/path/to/a` it will serve `/path/to/index.html`
* use originalUrl instead of baseUrl in prod middleware
* Add subpath index into prod test environment
* Add tests for subpath serving
* Tests for non existing indexes
* Create findClosestIndexToRoot function
Returns the closest index between given path and the given root.
Returns undefined if no index is found
* Use new index resolution in dev mode
* Use new index resolution in prod mode
* Add tests for new index resolution
remove not found tests
* Add environment without index at root
* Add tests for no root index cases
* Inline `getBasePath` function
---------

Co-authored-by: Szymon Miśtal <mistalzaq@gmail.com>
  • Loading branch information
elturpin and szymmis authored Nov 2, 2023
1 parent 3f1480b commit 0c4b032
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 8 deletions.
30 changes: 24 additions & 6 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,23 @@ function isIgnoredPath(path: string, req: express.Request) {
: Config.ignorePaths(path, req);
}

function findClosestIndexToRoot(
reqPath: string,
root: string
): string | undefined {
const basePath = reqPath.slice(0, reqPath.lastIndexOf("/"));
const dirs = basePath.split("/");

while (dirs.length > 0) {
const pathToTest = path.join(root, ...dirs, "index.html");
if (fs.existsSync(pathToTest)) {
return pathToTest;
}
dirs.pop();
}
return undefined;
}

async function injectViteIndexMiddleware(
app: core.Express,
server: ViteDevServer
Expand All @@ -199,13 +216,12 @@ async function injectViteIndexMiddleware(
app.get("/*", async (req, res, next) => {
if (isIgnoredPath(req.path, req)) return next();

const template = fs.readFileSync(
path.resolve(config.root, "index.html"),
"utf8"
);

if (isStaticFilePath(req.path)) next();
else {
const indexPath = findClosestIndexToRoot(req.path, config.root);
if (indexPath === undefined) return next();

const template = fs.readFileSync(indexPath, "utf8");
const html = await server.transformIndexHtml(req.originalUrl, template);
res.send(getTransformedHTML(html, req));
}
Expand All @@ -218,8 +234,10 @@ async function injectIndexMiddleware(app: core.Express) {
app.use("*", (req, res, next) => {
if (isIgnoredPath(req.baseUrl, req)) return next();

const html = fs.readFileSync(path.resolve(distPath, "index.html"), "utf-8");
const indexPath = findClosestIndexToRoot(req.originalUrl, distPath);
if (indexPath === undefined) return next();

const html = fs.readFileSync(indexPath, "utf8");
res.send(getTransformedHTML(html, req));
});
}
Expand Down
12 changes: 12 additions & 0 deletions tests/env/dist/subpath/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>subpath</h1>
</body>
</html>
12 changes: 12 additions & 0 deletions tests/noIndexEnv/dist/subpath/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>subpath</h1>
</body>
</html>
12 changes: 12 additions & 0 deletions tests/noIndexEnv/subpath/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>subpath</h1>
</body>
</html>
106 changes: 104 additions & 2 deletions tests/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,26 @@ test("Express app", async (done) => {
it("get api routes work");

response = await request(app).get("/");
expect(response.text).toMatch(/<body>/);
expect(response.text).toMatch(/<h1>index<\/h1>/);
response = await request(app).get("/route");
expect(response.text).toMatch(/<body>/);
expect(response.text).toMatch(/<h1>index<\/h1>/);

it("html is served correctly");

response = await request(app).get("/subpath/");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);
response = await request(app).get("/subpath/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("subpath html is served correctly");

response = await request(app).get("/some/path/route");
expect(response.text).toMatch(/<h1>index<\/h1>/);
response = await request(app).get("/subpath/to/some/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("fallback to closest index toward root");

response = await request(app).get("/test.txt");
expect(response.text).toBe("Hello from test.txt");

Expand Down Expand Up @@ -86,9 +100,23 @@ test("Express app with explicit static middleware", async (done) => {

it("html is served correctly");

response = await request(app).get("/subpath/");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);
response = await request(app).get("/subpath/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("subpath html is served correctly");

expect(response.headers.before).toBe("1");
expect(response.headers.after).toBe("1");

response = await request(app).get("/some/path/route");
expect(response.text).toMatch(/<h1>index<\/h1>/);
response = await request(app).get("/subpath/to/some/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("fallback to closest index toward root");

response = await request(app).get("/test.txt");
expect(response.text).toBe("Hello from test.txt");

Expand Down Expand Up @@ -136,6 +164,20 @@ test("Express app with custom http server", async (done) => {

it("html is served correctly");

response = await request(app).get("/subpath/");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);
response = await request(app).get("/subpath/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("subpath html is served correctly");

response = await request(app).get("/some/path/route");
expect(response.text).toMatch(/<h1>index<\/h1>/);
response = await request(app).get("/subpath/to/some/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("fallback to closest index toward root");

response = await request(app).get("/test.txt");
expect(response.text).toBe("Hello from test.txt");

Expand Down Expand Up @@ -209,6 +251,24 @@ test("Express app with transformer function", async (done) => {

it("html is transformed correctly");

response = await request(app).get("/subpath/");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);
response = await request(app).get("/subpath/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("subpath html is served correctly");

expect(response.text).toMatch(/<meta name="test"\/>/);

it("subpath html is transformed correctly");

response = await request(app).get("/some/path/route");
expect(response.text).toMatch(/<h1>index<\/h1>/);
response = await request(app).get("/subpath/to/some/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("fallback to closest index toward root");

response = await request(app).get("/test.txt");
expect(response.text).toBe("Hello from test.txt");

Expand Down Expand Up @@ -236,6 +296,20 @@ test("Express app with ignored paths", async (done) => {

it("html is served correctly");

response = await request(app).get("/subpath/");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);
response = await request(app).get("/subpath/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("subpath html is served correctly");

response = await request(app).get("/some/path/route");
expect(response.text).toMatch(/<h1>index<\/h1>/);
response = await request(app).get("/subpath/to/some/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("fallback to closest index toward root");

response = await request(app).get("/ignored");
expect(response.text).toMatch(/Cannot GET \/ignored/);

Expand All @@ -262,4 +336,32 @@ test("Express app with ignored paths", async (done) => {
});
});

test("Express with no index at root", async (done) => {
const app = express();

process.chdir(path.join(__dirname, "noIndexEnv"));

const server = ViteExpress.listen(app, 3000, async () => {
// default middleware fallback will respond with 404
let response = await request(app).get("/");
expect(response.status).toBe(404);
response = await request(app).get("/route");
expect(response.status).toBe(404);
response = await request(app).get("/some/path/route");
expect(response.status).toBe(404);

it("fallback with next middleware if no index found");

response = await request(app).get("/subpath/route");
expect(response.text).toMatch(/<h1>subpath<\/h1>/);

it("html is served correctly");

server.close(() => {
process.chdir(baseDir);
done();
});
});
});

run();

0 comments on commit 0c4b032

Please sign in to comment.