Skip to content

Commit 522df77

Browse files
committed
feat: implement better errors all over, new errors pages+refined logic, debounce watch
1 parent 09ac5dd commit 522df77

File tree

26 files changed

+811
-303
lines changed

26 files changed

+811
-303
lines changed

.github/workflows/release.yaml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
22
# See: https://github.com/JulianCataldo/gh-actions
33

4+
# For matrix setup:
5+
# https://github.com/withastro/astro/blob/main/.github/workflows/ci.yml
6+
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow
7+
48
name: CI / Release
59

610
on:
@@ -28,13 +32,31 @@ permissions:
2832
jobs:
2933
release:
3034
name: CI / Release
31-
runs-on: ubuntu-latest
35+
3236
permissions:
3337
contents: write # to be able to publish a GitHub release
3438
issues: write # to be able to comment on released issues
3539
pull-requests: write # to be able to comment on released pull requests
3640
id-token: write # to enable use of OIDC for npm provenance
3741

42+
runs-on: ubuntu-latest
43+
# TODO:
44+
# runs-on: ${{ matrix.os }}
45+
# timeout-minutes: 25
46+
# # needs: build
47+
# strategy:
48+
# matrix:
49+
# OS: [ubuntu-latest]
50+
# NODE_VERSION: [18, 20]
51+
# include:
52+
# - os: macos-14
53+
# NODE_VERSION: 18
54+
# - os: windows-latest
55+
# NODE_VERSION: 18
56+
# fail-fast: false
57+
# env:
58+
# NODE_VERSION: ${{ matrix.NODE_VERSION }}
59+
3860
steps:
3961
# MARK: Setup GH Action
4062

integration/__fixtures__/server-express/dist_expected/server/chunk/basic.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defineRoute } from '@gracile/gracile/route';
2-
import * as Route from '@gracile/gracile/_internals/route';
2+
import { RequestMethod } from '@gracile/gracile/_internals/route';
33

44
const basic = defineRoute({
55
handler: {
@@ -8,7 +8,7 @@ const basic = defineRoute({
88
{
99
url,
1010
param1: url.searchParams.get("foo"),
11-
[Route.RequestMethod.GET]: "ok",
11+
[RequestMethod.GET]: "ok",
1212
// TODO: When middleware are implemented, mock this properly
1313
locals: { requestIdLength: locals.requestId.length }
1414
},
@@ -26,7 +26,7 @@ const basic = defineRoute({
2626
{
2727
url,
2828
param1: url.searchParams.get("foo"),
29-
[Route.RequestMethod.POST]: "ok",
29+
[RequestMethod.POST]: "ok",
3030
body: await request.json(),
3131
locals: { requestIdLength: locals.requestId.length }
3232
},
@@ -38,7 +38,7 @@ const basic = defineRoute({
3838
{
3939
url,
4040
param1: url.searchParams.get("foo"),
41-
[Route.RequestMethod.PUT]: "ok",
41+
[RequestMethod.PUT]: "ok",
4242
body: await request.json(),
4343
locals: { requestIdLength: locals.requestId.length }
4444
},
@@ -50,7 +50,7 @@ const basic = defineRoute({
5050
{
5151
url,
5252
param1: url.searchParams.get("foo"),
53-
[Route.RequestMethod.QUERY]: "ok",
53+
[RequestMethod.QUERY]: "ok",
5454
body: await request.json(),
5555
locals: { requestIdLength: locals.requestId.length }
5656
},
@@ -62,7 +62,7 @@ const basic = defineRoute({
6262
{
6363
url,
6464
param1: url.searchParams.get("foo"),
65-
[Route.RequestMethod.DELETE]: "ok",
65+
[RequestMethod.DELETE]: "ok",
6666
locals: { requestIdLength: locals.requestId.length }
6767
},
6868
{ status: 200, statusText: "DONE", headers: { bar: "baz" } }
@@ -73,7 +73,7 @@ const basic = defineRoute({
7373
{
7474
url,
7575
param1: url.searchParams.get("foo"),
76-
[Route.RequestMethod.PATCH]: "ok",
76+
[RequestMethod.PATCH]: "ok",
7777
body: await request.json(),
7878
locals: { requestIdLength: locals.requestId.length }
7979
},

integration/__fixtures__/server-express/dist_expected/server/chunk/route-premises.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,6 @@ options) {
2323
return routeModule;
2424
};
2525
}
26-
// TODO: remove this, use `Response.json instead`? Or keep for old envs?
27-
// export function jsonResponse(data: any, init?: ResponseInit) {
28-
// return new Response(JSON.stringify(data), {
29-
// ...init,
30-
// headers: { 'Content-Type': 'application/json' },
31-
// });
32-
// }
33-
// NOTE: Useful?
34-
// export function notFound(statusText = '404 - Not found') {
35-
// return new Response(null, {
36-
// status: 404,
37-
// statusText,
38-
// });
39-
// }
4026

4127
const routeImports = new Map(
4228
[

integration/__fixtures__/server-express/dist_expected/server/entrypoint.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { URLPattern } from '@gracile/gracile/url-pattern';
22
import { createGracileHandler } from '@gracile/gracile/_internals/server-runtime';
3+
import { createLogger } from '@gracile/gracile/_internals/logger';
34

45
const routes = new Map([
56
[
@@ -204,6 +205,8 @@ const routeAssets = new Map([
204205
]
205206
]);
206207

208+
createLogger();
209+
207210
const handler = createGracileHandler({
208211
root: process.cwd(),
209212
routes,
Lines changed: 6 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,20 @@
1-
<!-- -->
2-
31
<!doctype html>
42
<html lang="en">
53
<head>
6-
<script type="module" src="/@vite/client"></script>
7-
84
<meta charset="UTF-8" />
95
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
10-
11-
<title>Error</title>
12-
</head>
13-
<body>
6+
<title>500: TypeError | Internal Server Error</title>
147
<style>
158
html {
16-
color-scheme: dark;
9+
color-scheme: light dark;
1710
font-size: 16px;
1811
line-height: 1.23rem;
1912
font-family: system-ui;
2013
}
21-
body {
22-
padding: 1rem;
23-
}
24-
25-
pre {
26-
padding: 1rem;
27-
28-
overflow-y: auto;
29-
}
30-
button {
31-
font-size: 2rem;
32-
}
33-
34-
h1 {
35-
color: tomato;
36-
}
3714
</style>
38-
39-
<main>
40-
<h1>😵 An error has occurred!</h1>
41-
<button id="reload">Reload</button>
42-
<!-- -->
43-
<hr />
44-
45-
<pre>
46-
Error: !!! OH NO !!! I AM A FAKE ERROR !!!
47-
at RouteModule.template (__REPLACED_FOR_TESTS__)
48-
at renderRouteTemplate (__REPLACED_FOR_TESTS__)
49-
at async middleware (__REPLACED_FOR_TESTS__)
50-
at async nodeHandler (__REPLACED_FOR_TESTS__)</pre
51-
>
52-
<!-- <pre>$ {e.name}</pre> -->
53-
<!-- <pre>$ {e.message}</pre> -->
54-
<!-- <pre>$ {e.cause}</pre> -->
55-
<hr />
56-
</main>
57-
58-
<script>
59-
reload.addEventListener("click", () => document.location.reload());
60-
</script>
15+
<script type="module" src="/@vite/client"></script>
16+
</head>
17+
<body>
18+
<h1>500: TypeError | Internal Server Error</h1>
6119
</body>
6220
</html>

integration/__fixtures__/static-site/src/routes/throws.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { defineRoute } from '@gracile/gracile/route';
22
import { html } from '@gracile/gracile/server-html';
3+
// import { GracileUserError } from '@gracile/gracile/error';
34

45
import { document } from '../documents/document-minimal.js';
56

67
export default defineRoute({
78
document: (context) => document({ ...context, title: 'Gracile - Oh no' }),
89

910
template: (context) => {
10-
throw new Error('!!! OH NO !!! I AM A FAKE ERROR !!!');
11+
throw new TypeError('!!! OH NO !!! I AM A FAKE ERROR !!!');
1112
html`
1213
<h1>⚠️ Arrrrrhh !!</h1>
1314

integration/manual-tests.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# pnpm tsx --test -C test src/addons/client-router.test.ts
2+
3+
# set -e
4+
25
pnpm tsx --test -C test src/routes-premises.test.ts
36

47
pnpm tsx --test -C test src/addons/metadata.test.ts

integration/src/exports.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { fileURLToPath } from 'node:url';
99

1010
import { constants as serverConstants } from '@gracile/engine/server/constants';
1111
import * as internalLogger from '@gracile/gracile/_internals/logger';
12+
import * as route from '@gracile/gracile/_internals/route';
13+
import * as routeModule from '@gracile/gracile/_internals/route-module';
1214
import * as serverRuntime from '@gracile/gracile/_internals/server-runtime';
1315
import * as document from '@gracile/gracile/document';
1416
import { env as envFromNodeConditions } from '@gracile/gracile/env';
@@ -150,5 +152,8 @@ describe('gracile package should do its exports correctly', () => {
150152

151153
test('internals', () => {
152154
assert.equal(typeof internalLogger.createLogger, 'function');
155+
assert.equal(typeof route.RequestMethod, 'object');
156+
assert.equal(typeof routeModule.RouteModule, 'function');
157+
assert.equal(typeof serverRuntime.createGracileHandler, 'function');
153158
});
154159
});

integration/src/server-express/_common.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,15 @@ async function tests(mode: string, item: string, writeActual: boolean) {
151151
));
152152
// TODO: Test with "accept: json" when implemented
153153

154-
await it(`load an error page when a route throws - ${item}`, async () =>
154+
await it(`load an error page when a route throws - ${item}`, async () => {
155+
const ressource = await fetchResource(ADDRESS, ['throws']);
156+
155157
assert.equal(
156-
(await fetchResource(ADDRESS, ['throws'])).includes(
157-
'Error: !!! OH NO !!! I AM A FAKE ERROR !!!',
158-
),
158+
// <vite-error-overlay> should take over just after (its a client only component) in DEV
159+
ressource.includes('500: Error | Internal Server Error'),
159160
true,
160-
));
161+
);
162+
});
161163

162164
await it(`should redirect`, async () =>
163165
checkResponse(

packages/engine/src/dev/dev.ts

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getLogger } from '@gracile/internal-utils/logger/helpers';
22
import c from 'picocolors';
33
import { type ViteDevServer } from 'vite';
44

5-
import { collectRoutes } from '../routes/collect.js';
5+
import { collectRoutes, WATCHED_FILES_REGEX } from '../routes/collect.js';
66
import type { RoutesManifest } from '../routes/route.js';
77
import {
88
createGracileHandler,
@@ -14,7 +14,7 @@ import { generateRoutesTypings } from './route-typings.js';
1414
export async function createDevHandler({
1515
routes,
1616
vite,
17-
gracileConfig,dd
17+
gracileConfig,
1818
}: {
1919
routes: RoutesManifest;
2020
vite: ViteDevServer;
@@ -27,34 +27,36 @@ export async function createDevHandler({
2727

2828
const root = vite.config.root;
2929

30-
logger.info(c.dim('\nCreating handler…'), { timestamp: true });
30+
logger.info('');
31+
logger.info(c.dim('Creating the request handler…'), { timestamp: true });
3132

32-
await collectRoutes(routes, root, gracileConfig.routes?.exclude);
33+
const collect = async () => {
34+
await collectRoutes(routes, root, gracileConfig.routes?.exclude);
3335

34-
if (gracileConfig.experimental?.generateRoutesTypings)
35-
generateRoutesTypings(root, routes).catch((error) =>
36-
logger.error(String(error)),
37-
);
36+
if (gracileConfig.experimental?.generateRoutesTypings)
37+
await generateRoutesTypings(root, routes).catch((error) =>
38+
logger.error(String(error)),
39+
);
40+
};
3841

42+
await collect();
43+
44+
let wait: ReturnType<typeof setTimeout>;
3945
vite.watcher.on('all', (event, file) => {
40-
// console.log({ event });
4146
if (
42-
file.match(
43-
/\/src\/routes\/(.*)\.(ts|js|css|scss|sass|less|styl|stylus)$/,
44-
) &&
47+
file.match(WATCHED_FILES_REGEX) &&
4548
['add', 'unlink'].includes(event)
46-
)
47-
collectRoutes(routes, root, gracileConfig.routes?.exclude)
48-
.then(() => {
49-
vite.hot.send('vite:invalidate');
50-
51-
if (gracileConfig.experimental?.generateRoutesTypings)
52-
generateRoutesTypings(root, routes).catch((error) =>
53-
logger.error(String(error)),
54-
);
55-
})
56-
.catch((e) => logger.error(String(e)));
49+
//
50+
) {
51+
clearTimeout(wait);
52+
wait = setTimeout(() => {
53+
collect()
54+
.then(() => vite.hot.send('vite:invalidate'))
55+
.catch((error) => logger.error(String(error)));
56+
}, 100);
57+
}
5758
});
59+
5860
//
5961

6062
// NOTE: Wrong place?

0 commit comments

Comments
 (0)