Skip to content

Commit d284270

Browse files
authored
Merge pull request #11 from lilnasy/fix-images
fix: Improve Static Server Reliability
2 parents 6f14ebf + 436f92b commit d284270

File tree

1 file changed

+64
-6
lines changed

1 file changed

+64
-6
lines changed

package/src/server/index.ts

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/// <reference types="astro/client" />
2-
2+
import path, { relative } from 'node:path';
3+
import url from 'node:url';
4+
import { readdir } from 'node:fs/promises';
35
import cluster from 'node:cluster';
46
import os from 'node:os';
57
import type { SSRManifest } from 'astro';
@@ -81,16 +83,25 @@ function handler(
8183

8284
const app = new App(manifest);
8385

84-
return (req: Request, server: Server<undefined>): Promise<Response> => {
86+
// The dist may be copied somewhere after building.
87+
// The build environment's full client path (options.client) can't be relied on in production.
88+
// `resolveClientDir()` finds the full path to the client directory in the current environment.
89+
const clientDir = resolveClientDir(options);
90+
91+
const clientAssetsPromise = getStaticAssets(clientDir);
92+
let clientAssets: Awaited<typeof clientAssetsPromise> | undefined;
93+
94+
return async (req: Request, server: Server<undefined>): Promise<Response> => {
8595
const routeData = app.match(req);
8696
if (!routeData) {
8797
const url = new URL(req.url);
88-
89-
const manifestAssetExists = manifest.assets.has(url.pathname);
98+
const staticAssetExists = (clientAssets ??= await clientAssetsPromise).has(
99+
url.pathname,
100+
);
90101

91102
// If the manifest asset doesn't exist, or the request url ends with a slash
92103
// we should serve the index.html file from the respective directory.
93-
if (!manifestAssetExists || req.url.endsWith('/')) {
104+
if (!staticAssetExists || req.url.endsWith('/')) {
94105
const localPath = new URL(
95106
`./${app.removeBase(url.pathname)}/index.html`,
96107
clientRoot,
@@ -99,7 +110,7 @@ function handler(
99110
}
100111

101112
// Otherwise we attempt to serve the static asset from the client directory.
102-
if (manifestAssetExists) {
113+
if (staticAssetExists) {
103114
const localPath = new URL(app.removeBase(url.pathname), clientRoot);
104115
return serveStaticFile(url.pathname, localPath, clientRoot, options);
105116
}
@@ -112,3 +123,50 @@ function handler(
112123
});
113124
};
114125
}
126+
127+
async function getStaticAssets(clientDir: string): Promise<Set<string>> {
128+
const dirEntries = await readdir(clientDir, { withFileTypes: true, recursive: true });
129+
const publicPath = new Set<string>();
130+
for (const entry of dirEntries) {
131+
if (entry.isFile() == false) continue;
132+
publicPath.add(
133+
prependForwardSlash(path.relative(clientDir, entry.parentPath) + '/' + entry.name),
134+
);
135+
}
136+
return publicPath;
137+
}
138+
139+
/**
140+
* From https://github.com/withastro/adapters/blob/@astrojs/node@9.0.0/packages/node/src/serve-static.ts#L109-L125
141+
*
142+
* Copyright of withastro/adapters contributors, Reproduced under MIT License
143+
*/
144+
// @ts-expect-error client and server fields are always present
145+
function resolveClientDir(options: InternalOptions): string {
146+
const clientURLRaw = new URL(options.client);
147+
const serverURLRaw = new URL(options.server);
148+
const rel = path.relative(
149+
url.fileURLToPath(serverURLRaw),
150+
url.fileURLToPath(clientURLRaw),
151+
);
152+
153+
// Walk up the parent folders until you find the one that is the root of the server entry folder. This is how we find the client folder relatively.
154+
const serverFolder = path.basename(options.server);
155+
let serverEntryFolderURL = path.dirname(import.meta.url);
156+
while (!serverEntryFolderURL.endsWith(serverFolder)) {
157+
serverEntryFolderURL = path.dirname(serverEntryFolderURL);
158+
}
159+
160+
const serverEntryURL = serverEntryFolderURL + '/entry.mjs';
161+
const clientURL = new URL(appendForwardSlash(rel), serverEntryURL);
162+
const client = url.fileURLToPath(clientURL);
163+
return client;
164+
}
165+
166+
function prependForwardSlash(pth: string): string {
167+
return pth.startsWith('/') ? pth : '/' + pth;
168+
}
169+
170+
function appendForwardSlash(pth: string): string {
171+
return pth.endsWith('/') ? pth : pth + '/';
172+
}

0 commit comments

Comments
 (0)