Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support custom 404 (static html only) #497

Merged
merged 11 commits into from
Feb 19, 2024
1 change: 1 addition & 0 deletions examples/03_minimal/public/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h1>Custom Not Found Page</h1>
10 changes: 9 additions & 1 deletion packages/waku/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import path from 'node:path';
import { existsSync, writeFileSync, unlinkSync } from 'node:fs';
import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
import { pathToFileURL } from 'node:url';
import { parseArgs } from 'node:util';
import { createRequire } from 'node:module';
Expand Down Expand Up @@ -142,6 +142,14 @@ async function runStart(options: { ssr: boolean }) {
env: process.env as any,
}),
);
app.notFound((c) => {
// FIXME better implementation using node stream?
const file = path.join(distDir, publicDir, '404.html');
if (existsSync(file)) {
return c.html(readFileSync(file, 'utf8'), 404);
}
return c.text('404 Not Found', 404);
});
const port = parseInt(process.env.PORT || '8080', 10);
startServer(app, port);
}
Expand Down
12 changes: 11 additions & 1 deletion packages/waku/src/lib/builder/output-netlify.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import path from 'node:path';
import { mkdirSync, writeFileSync } from 'node:fs';
import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'node:fs';

import type { ResolvedConfig } from '../config.js';

Expand All @@ -13,9 +13,19 @@ export const emitNetlifyOutput = async (
mkdirSync(functionsDir, {
recursive: true,
});
const notFoundFile = path.join(
rootDir,
config.distDir,
config.publicDir,
'404.html',
);
const notFoundHtml = existsSync(notFoundFile)
? readFileSync(notFoundFile, 'utf8')
: null;
writeFileSync(
path.join(functionsDir, 'serve.js'),
`
globalThis.__WAKU_NOT_FOUND_HTML__ = ${JSON.stringify(notFoundHtml)};
export { default } from '../../${config.distDir}/${config.serveJs}';
export const config = {
preferStatic: true,
Expand Down
14 changes: 14 additions & 0 deletions packages/waku/src/lib/builder/serve-cloudflare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,23 @@ import { honoMiddleware } from '../middleware/hono-prd.js';
const ssr = !!import.meta.env.WAKU_BUILD_SSR;
const loadEntries = () => import(import.meta.env.WAKU_ENTRIES_FILE!);
let serveWaku: ReturnType<typeof honoMiddleware> | undefined;
let staticContent: any;

const parsedManifest: Record<string, string> = JSON.parse(manifest);

const app = new Hono();
app.use('*', serveStatic({ root: './', manifest }));
app.use('*', (c, next) => serveWaku!(c, next));
app.notFound(async (c) => {
const path = parsedManifest['404.html'];
const content: ArrayBuffer | undefined =
path && (await staticContent?.get(path, { type: 'arrayBuffer' }));
if (content) {
c.header('Content-Type', 'text/html; charset=utf-8');
return c.body(content, 404);
}
return c.text('404 Not Found', 404);
});

export default {
async fetch(
Expand All @@ -22,6 +35,7 @@ export default {
) {
if (!serveWaku) {
serveWaku = honoMiddleware({ loadEntries, ssr, env });
staticContent = env.__STATIC_CONTENT;
}
return app.fetch(request, env, ctx);
},
Expand Down
13 changes: 11 additions & 2 deletions packages/waku/src/lib/builder/serve-deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@ import { serveStatic } from 'https://deno.land/x/hono/middleware.ts';

import { honoMiddleware } from '../middleware/hono-prd.js';

declare const Deno: any;

const ssr = !!import.meta.env.WAKU_BUILD_SSR;
const distDir = import.meta.env.WAKU_CONFIG_DIST_DIR;
const publicDir = import.meta.env.WAKU_CONFIG_PUBLIC_DIR;
const loadEntries = () => import(import.meta.env.WAKU_ENTRIES_FILE!);
// @ts-expect-error no types
const env = Deno.env.toObject();

const app = new Hono();
app.use('*', serveStatic({ root: `${distDir}/${publicDir}` }));
app.use('*', honoMiddleware({ loadEntries, ssr, env }));
app.notFound(async (c: any) => {
const file = `${distDir}/${publicDir}/404.html`;
const info = await Deno.stat(file);
if (info.isFile) {
c.header('Content-Type', 'text/html; charset=utf-8');
return c.body(await Deno.readFile(file), 404);
}
return c.text('404 Not Found', 404);
});

// @ts-expect-error no types
Deno.serve(app.fetch);
7 changes: 7 additions & 0 deletions packages/waku/src/lib/builder/serve-netlify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ const env: Record<string, string> = process.env as any;

const app = new Hono();
app.use('*', honoMiddleware({ loadEntries, ssr, env }));
app.notFound((c) => {
const notFoundHtml = (globalThis as any).__WAKU_NOT_FOUND_HTML__;
if (typeof notFoundHtml === 'string') {
return c.html(notFoundHtml, 404);
}
return c.text('404 Not Found', 404);
});

export default async (req: Request, context: Context) =>
app.fetch(req, { context });
12 changes: 12 additions & 0 deletions packages/waku/src/lib/builder/serve-vercel.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import path from 'node:path';
import { existsSync, readFileSync } from 'node:fs';
import type { IncomingMessage, ServerResponse } from 'node:http';
import { Hono } from 'hono';
import { getRequestListener } from '@hono/node-server';

import { honoMiddleware } from '../middleware/hono-prd.js';

const ssr = !!import.meta.env.WAKU_BUILD_SSR;
const distDir = import.meta.env.WAKU_CONFIG_DIST_DIR!;
const publicDir = import.meta.env.WAKU_CONFIG_PUBLIC_DIR!;
const loadEntries = () => import(import.meta.env.WAKU_ENTRIES_FILE!);
const env: Record<string, string> = process.env as any;

const app = new Hono();
app.use('*', honoMiddleware({ loadEntries, ssr, env }));
app.notFound((c) => {
// FIXME better implementation using node stream?
const file = path.join(distDir, publicDir, '404.html');
if (existsSync(file)) {
return c.html(readFileSync(file, 'utf8'), 404);
}
return c.text('404 Not Found', 404);
});
const requestListener = getRequestListener(app.fetch);

export default function handler(req: IncomingMessage, res: ServerResponse) {
Expand Down
Loading