Skip to content

Commit 59290c6

Browse files
committed
docs: finish initial typedoc setup, add contribute notes, jsdocs
1 parent 11342db commit 59290c6

File tree

16 files changed

+412
-37
lines changed

16 files changed

+412
-37
lines changed

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ dist
99
.old*
1010

1111
labs
12+
13+
/index.ts

CONTRIBUTING.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,45 @@ That way, you'll be able to use them with your locally developed Gracile version
5656
nodemon -w 'node_modules/@gracile' -x 'vite dev'
5757
5858
-->
59+
60+
## Testing
61+
62+
The `./local-ci.sh` can be used locally to simulate the GitHub CI pipeline.
63+
No git hooks with Lint-Staged on this repo! It slows down things too much,
64+
so instead you'll have to remember to launch this local pipeline before
65+
the ultimate merge.
66+
Intermediate commits on a PR that fails a CI is OK. CI passing is only required
67+
before merging the `fix|feat` branch to the `next` integration branch.
68+
69+
Gracile is still in a sub `<1.x` version, with many breaking changes all over.
70+
As creating tests for fast moving targets is a waste of time,
71+
coverage is kept empiric; and added to stabilize APIs that are mostly settled and
72+
are **user-facing** (e.g. `defineRoute`).
73+
74+
### Integration
75+
76+
Most of the testing is achieved here for now, because it offers the most bang for the buck
77+
for the end user,
78+
but it is slower than unit tests, and not optimized well enough yet.
79+
The main reason is due to spinning up real Vite dev/build instances that provoke file system/network conflicts (HMR, server…).
80+
Workaround: **tests are sequential**, but they could be re-instated to parallel or semi-parallel runs.
81+
82+
The main technique used for Gracile integration tests is snapshots comparison of server-produced responses.
83+
Correctness of user-facing package exports is also covered.
84+
85+
### Unit
86+
87+
Most critical aspects of the framework are meant to be unit-tested, with
88+
priority to the core, for now.
89+
90+
### End-to-end
91+
92+
Head over to the [starter projects](https://github.com/gracile-web/starter-projects)
93+
where you can find Playwright test suites for each template in build and dev modes.
94+
95+
They simulate route navigation, compare screenshots to be sure that nothing broke,
96+
and more advanced interactions, notably with client/server communication.
97+
98+
## More
99+
100+
- [Conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)

index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Makes Typedoc happy

packages/engine/src/plugin.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,29 @@ import { nodeAdapter } from './server/adapters/node.js';
1414
import type { GracileConfig } from './user-config.js';
1515
import { buildRoutes } from './vite/plugins/build-routes.js';
1616
import { virtualRoutes } from './vite/plugins/virtual-routes.js';
17+
1718
export type { GracileConfig };
1819

1920
let isClientBuilt = false;
21+
22+
/**
23+
* The main Vite plugin for loading the Gracile framework.
24+
*
25+
* @param config - Gracile configuration.
26+
* @returns Vite plugins. `any` is used to prevent Vite typings version mismatches for the plugin API.
27+
* @example
28+
* `/vite.config.js`
29+
* ```js
30+
* import { gracile } from '@gracile/gracile/plugin';
31+
* import { defineConfig } from 'vite';
32+
*
33+
* export default defineConfig({
34+
* plugins: [
35+
* gracile({ output: 'server' }),
36+
* ],
37+
* });
38+
* ```
39+
*/
2040
// Return as `any` to avoid Plugin type mismatches when there are multiple Vite versions installed
2141
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2242
export const gracile = (config?: GracileConfig): any /* Plugin */[] => {

packages/engine/src/server/adapters/hono.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@ export type GracileHonoHandler = (context: {
1313
var: unknown;
1414
}) => Promise<Response>;
1515

16+
/**
17+
* @param handler - Takes a pre-built Gracile handler from `./dist/server/entrypoint.js`.
18+
* @example
19+
* `/src/server.js`
20+
* ```js
21+
* import { Hono } from 'hono';
22+
* import { serve } from '@hono/node-server';
23+
*
24+
* import * as gracile from '@gracile/gracile/hono';
25+
*
26+
* import { handler } from './dist/server/entrypoint.js';
27+
*
28+
* const app = new Hono();
29+
*
30+
* app.use(gracile.honoAdapter(handler));
31+
*
32+
* serve(app);
33+
* ```
34+
*/
1635
export const honoAdapter =
1736
(handler: GracileHandler): GracileHonoHandler =>
1837
async (context) => {
@@ -30,6 +49,21 @@ export const honoAdapter =
3049
throw new Error('Rendering was impossible in the Hono adapter!');
3150
};
3251

52+
/**
53+
*
54+
* @param root - resolve `dist/client` from this file path.
55+
* @example
56+
* `/src/server.js`
57+
* ```js
58+
* import * as gracile from '@gracile/gracile/node';
59+
* import { Hono } from 'hono';
60+
* import { serveStatic } from '@hono/node-server/serve-static';
61+
*
62+
* const app = new Hono();
63+
*
64+
* app.get('*', serveStatic({ root: gracile.getClientDistPath(import.meta.url) }));
65+
* ```
66+
*/
3367
export function getClientDistPath(root: string) {
3468
return relative(
3569
process.cwd(),

packages/engine/src/server/adapters/node.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,34 @@ function standardResponseInitToNodeResponse(
2727
if (responseInit.statusText) res.statusMessage = responseInit.statusText;
2828
}
2929

30-
export function nodeAdapter(handler: GracileHandler) {
30+
export type { GracileHandler };
31+
32+
export type GracileNodeHandler = (
33+
req: IncomingMessage,
34+
res: ServerResponse,
35+
locals?: unknown,
36+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
37+
) => Promise<ServerResponse<IncomingMessage> | null | void>;
38+
39+
/**
40+
* @param handler - Takes a pre-built Gracile handler from `./dist/server/entrypoint.js`.
41+
* @example
42+
* `/src/server.js`
43+
* ```js
44+
* import express from 'express';
45+
*
46+
* import * as gracile from '@gracile/gracile/node';
47+
*
48+
* import { handler } from './dist/server/entrypoint.js';
49+
*
50+
* const app = express();
51+
*
52+
* app.use(gracile.nodeAdapter(handler));
53+
*
54+
* const server = app.listen();
55+
* ```
56+
*/
57+
export function nodeAdapter(handler: GracileHandler): GracileNodeHandler {
3158
return async function nodeHandler(
3259
req: IncomingMessage,
3360
res: ServerResponse,
@@ -79,8 +106,21 @@ export function nodeAdapter(handler: GracileHandler) {
79106
};
80107
}
81108

109+
/**
110+
* @param root - resolve `dist/client` from this file path.
111+
* @example
112+
* ```js
113+
* `/src/server.js`
114+
* import * as gracile from '@gracile/gracile/node';
115+
* import express from 'express';
116+
*
117+
* const app = express();
118+
*
119+
* app.use(express.static(gracile.getClientDistPath(import.meta.url)));
120+
* ```
121+
*/
82122
export function getClientDistPath(root: string) {
83-
return fileURLToPath(new URL(CLIENT_DIST_DIR, root));
123+
return fileURLToPath(new URL(server.CLIENT_DIST_DIR, root));
84124
}
85125

86126
export { printAddressInfos } from '../utils.js';

packages/engine/src/server/request.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,20 @@ import type * as R from '../routes/route.js';
1313

1414
// type NextFunction = (error?: unknown) => void | Promise<void>;
1515

16+
type StandardResponse = { response: Response; body?: never; init?: never };
17+
type ResponseWithNodeReadable = {
18+
response?: never;
19+
body: Readable;
20+
init: ResponseInit;
21+
};
22+
23+
/**
24+
* The underlying handler interface that you can use to build your own adapter.
25+
*/
1626
export type GracileHandler = (
1727
request: Request,
1828
locals?: unknown,
19-
) => Promise<
20-
| { response: Response; body?: never; init?: never }
21-
| { response?: never; body: Readable; init: ResponseInit }
22-
| null
23-
>;
29+
) => Promise<StandardResponse | ResponseWithNodeReadable | null>;
2430

2531
const CONTENT_TYPE_HTML = { 'Content-Type': 'text/html' };
2632

packages/engine/src/server/utils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@ import { server as serverConstants } from './constants.js';
1212
// logger.info('HY');
1313
// });
1414

15+
/**
16+
* Pretty print your server instance address as soon as it is listening.
17+
* Matches the dev. server CLI output style.
18+
*
19+
* @param server - Takes an `node:net` `AddressInfo` like object (address, family, port) or just a provided, pre-constructed string.
20+
* @example
21+
*
22+
* ```js
23+
* import * as gracile from '@gracile/gracile/hono';
24+
* import { serve } from '@hono/node-server';
25+
*
26+
* // ...
27+
*
28+
* serve(
29+
* { fetch: app.fetch, port: 3030, hostname: 'localhost' },
30+
* (address) => gracile.printAddressInfos(address),
31+
* );
32+
* ```
33+
*/
1534
export function printAddressInfos(server: string | AddressInfo | null) {
1635
let address: null | string = null;
1736
if (!server) throw new Error('Incorrect address infos');

packages/engine/src/user-config.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,64 @@
11
import type { Connect } from 'vite';
22

3+
/**
4+
* @example
5+
* `/vite.config.js`
6+
* ```js
7+
* import { gracile } from '@gracile/gracile/plugin';
8+
* import { defineConfig } from 'vite';
9+
*
10+
* export default defineConfig({
11+
* plugins: [
12+
* gracile({
13+
* output: 'server',
14+
*
15+
* dev: {
16+
* locals: (_context) => {
17+
* return {
18+
* requestId: crypto.randomUUID(),
19+
* userEmail: 'admin@admin.home.arpa',
20+
* };
21+
* },
22+
* },
23+
*
24+
* routes: {
25+
* exclude: ['**\/a-defective-route.ts'],
26+
* },
27+
* }),
28+
* ],
29+
* });
30+
* ```
31+
*/
332
export interface GracileConfig {
433
/**
34+
* The target output for the build phase.
35+
*
36+
* See the [documentation](https://gracile.js.org/docs/learn/usage/output-modes/).
37+
*
538
* @defaultValue 'static'
639
*/
740
output?: 'static' | 'server';
841

42+
/**
43+
* Settings for the development mode.
44+
*/
945
dev?: {
46+
/**
47+
* Get incoming request context and apply locals for the Gracile request handler.
48+
* Useful for mocking the production server.
49+
*
50+
* For `server` mode only.
51+
*/
1052
locals?: (context: { nodeRequest: Connect.IncomingMessage }) => unknown;
1153
};
1254

55+
/**
56+
* Settings for routes in `/src/routes`.
57+
*/
1358
routes?: {
59+
/**
60+
* Exclude routes with an array of patterns. Useful for debugging.
61+
*/
1462
exclude?: string[];
1563
};
1664
}

packages/engine/typedoc.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"extends": ["../../typedoc.base.jsonc"],
3+
// If not set, will result in a warning being printed since it's likely a misconfiguration
4+
"entryPoints": [
5+
//
6+
"./src/index.ts"
7+
// "./src/plugin.ts",
8+
// "./src/server-html.ts"
9+
// "./src/route.ts",
10+
// "./src/document.ts",
11+
]
12+
}

0 commit comments

Comments
 (0)