Skip to content

Commit

Permalink
feat: init hono adapter, reference starter
Browse files Browse the repository at this point in the history
  • Loading branch information
JulianCataldo committed Aug 5, 2024
1 parent 79b1951 commit e0c9c2e
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 25 deletions.
54 changes: 51 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ Gracile is powered by **Vite** and **Lit SSR**.

With it, you can achieve:

- File-based routing
- File-based routing with efficient code-splitting
- **S**erver **S**ide **R**endering
- **S**tatic **S**ite **G**eneration
- **Server** integration (`Request`/`Response`) for **Express**
- **Server** integration (`Request`/`Response`) for **Express**, **Hono**
- Full-stack **Custom Elements** (Lit), with hydration
- **Content** websites
- Multi or Single Page **Applications**
- Progressive enhancements
- Progressive enhancements (no JS fallbacks, smart hydration…)
- And more, via **Add-ons** (Markdown, Metadata, SVG…)

All that, with a **few conventions**, a **standard-oriented** approach and a very **contained footprint** 🤏.
Expand All @@ -46,6 +46,54 @@ Web Components, TypeScript, SASS, Lit, and other DX perks are all at your finger
Thanks to the Vite modular architecture, and Node.js versatility, **developer experience** is smoothed up
across the board, while in **development** and when building for **production**.

## Ease of use

Write the same markup, styling and scripting languages for both server and
client side.
The ones that you already know and use everywhere else: **HTML**, **CSS** and
**JavaScript**.

Simplicity doesn't mean obfuscation. You're still in charge without abandoning flexibility to your framework.

## Standards oriented

Built with a platform-minded philosophy. Every time a standard can be leveraged
for a task, it should be.
It also means fewer vendor-specific idioms to churn on and a more portable
codebase overall.
Stop re-implementing the wheel, and embrace **future-proof APIs**, you'll thank
yourself later!

## Developer Experience

The DX bar has been constantly raised, alongside developers' expectations about
their everyday tooling.
The "Vanilla" community is full of gems, in a scattered way.
Gracile provides an integrated, **out-of-the-box** experience while keeping
non-core opinions as _opt-ins_.

## Convention over configuration

Finding the right balance between **convenience** and **freedom** is tricky.
Hopefully, more and more patterns will be established in the full-stack JS
space.

Gracile is inspired by those widespread practices that will make you feel at
home.

## Light and unobtrusive

All in all, the Gracile framework is just Vite, Lit SSR and a very **restricted set of helpers and third parties**.
Check its [dependency tree on npmgraph](https://npmgraph.js.org/?q=@gracile/gracile), you'll see by yourself.
Also, everything is done to **keep your Vite configuration as pristine as possible**. Augmenting an existing project can be done in a pinch, with no interference.

## Performances

**Speed is not the main goal** for Gracile, that's because it is just the sane
default you'll start with.
Avoiding complex template transformations, or surgically shipping client-side JS
are just a few facets of what makes Gracile a "_do more with less_" power tool.

## 👐 Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md).
Expand Down
4 changes: 2 additions & 2 deletions integration/src/exports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ describe('gracile package should do its exports correctly', () => {
assert.equal(typeof plugin.createGracileMiddleware, 'function');

assert.equal(typeof node.printAddressInfos, 'function');
assert.equal(typeof node.authenticateBasic, 'function');
assert.equal(typeof node.notFoundHandler, 'function');
// assert.equal(typeof node.authenticateBasic, 'function');
// assert.equal(typeof node.notFoundHandler, 'function');

assert.equal(typeof serverHtml.html, 'function');

Expand Down
9 changes: 7 additions & 2 deletions packages/create-gracile/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const program = new Command()
)
.option(
'-n, --next',
`${c.yellow(`Choose from the \`next\` versions of template.\n`)}`,
`${c.yellow(`Use the \`next\` version of the selected template.\n`)}`,
)
.option(
'-i, --install-dependencies',
Expand Down Expand Up @@ -209,7 +209,12 @@ const template = templateFound
hint: 'Just the minimum to get started with a dynamic SSRed website',
},
{
value: 'basics-blog-static',
value: 'minimal-server-hono',
label: 'Minimal - Server Hono',
hint: 'Just the minimum to get started with a dynamic SSRed website',
},
{
value: 'basics-static-blog',
label: 'Basics - Blog static',
hint: 'Show features and conventions for a Gracile static website',
},
Expand Down
4 changes: 3 additions & 1 deletion packages/create-gracile/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ export const TEMPLATE_LIST = [
//
'minimal-static',
'minimal-server-express',
'basics-blog-static',
'minimal-server-hono',

'basics-static-blog',
'basics-server',
] as const;
export const TEMPLATE_LIST_ANON = [...(TEMPLATE_LIST as unknown as string[])];
2 changes: 1 addition & 1 deletion packages/engine/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from './build/static.js';
import { createDevHandler } from './dev/dev.js';
import type { RoutesManifest } from './routes/route.js';
import { nodeAdapter } from './server/node.js';
import { nodeAdapter } from './server/adapters/node.js';
import type { GracileConfig } from './user-config.js';
import { buildRoutes } from './vite/plugins/build-routes.js';
import { virtualRoutes } from './vite/plugins/virtual-routes.js';
Expand Down
41 changes: 41 additions & 0 deletions packages/engine/src/server/adapters/hono.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// import { logger } from '@gracile/internal-utils/logger.build';

import { relative } from 'node:path';
import { Readable } from 'node:stream';
import { fileURLToPath } from 'node:url';

import { CLIENT_DIST_DIR } from '../env.js';
import { type GracileAsyncMiddleware } from '../request.js';

export function honoAdapter(middleware: GracileAsyncMiddleware) {
return async function honoMiddleware(context: {
req: { raw: Request };
res: ResponseInit;
var: unknown;
}) {
const result = await middleware(context.req.raw, context.var);

if (result instanceof Readable) {
return new Response(Readable.toWeb(result) as ReadableStream, {
headers: { 'content-type': 'html' },
});

// context.res.headers.set('content-type', 'html');
// return stream(c, async (stream) => {
// stream.onAbort(() => {
// throw new Error('Stream aborted!');
// });
// await stream.pipe(/** @type {ReadableStream} */ Readable.toWeb(result));
// });
}

if (result) return result;
throw new Error('Rendering was impossible in the Hono adapter!');
};
}

export function getClientDistPath(root: string) {
return relative(process.cwd(), fileURLToPath(new URL(CLIENT_DIST_DIR, root)));
}

export { printAddressInfos } from '../utils.js';
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { fileURLToPath } from 'node:url';

import { logger } from '@gracile/internal-utils/logger.build';
import { createServerAdapter } from '@whatwg-node/server';
import type { IncomingMessage, ServerResponse } from 'http';
import { Readable, Writable } from 'stream';

import { type GracileAsyncMiddleware } from './request.js';
import { CLIENT_DIST_DIR } from '../env.js';
import { type GracileAsyncMiddleware } from '../request.js';

// NOTE: Find a more canonical way to ponyfill the Node HTTP request to standard Request
// @ts-expect-error Abusing this feature!
Expand Down Expand Up @@ -56,3 +59,9 @@ export function nodeAdapter(middleware: GracileAsyncMiddleware) {
return null;
};
}

export function getClientDistPath(root: string) {
return fileURLToPath(new URL(CLIENT_DIST_DIR, root));
}

export { printAddressInfos } from '../utils.js';
27 changes: 14 additions & 13 deletions packages/engine/src/server/utils.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// NOTE: Util. to pretty print for user provided server.

import type { IncomingMessage, Server, ServerResponse } from 'node:http';
import { fileURLToPath } from 'node:url';

// import type { AddressInfo } from 'node:net';
import { logger } from '@gracile/internal-utils/logger';
import { DEV } from 'esm-env';
import c from 'picocolors';

import { CLIENT_DIST_DIR, IP_EXPOSED } from './env.js';
import { IP_EXPOSED } from './env.js';

export function printAddressInfos(options: {
server?: Server;
address?: string;
port?: number;
}) {
let address: null | string = null;

Expand All @@ -23,26 +23,30 @@ export function printAddressInfos(options: {
address = `http://${infos.address}:${infos.port}/`;
}
} else if (options.address) {
address = options.address;
address = `http://${options.address}${options.port ? `:${String(options.port)}` : ''}`;
}
if (!address) throw new Error('Incorrect options');

logger.info(c.green(`${DEV ? 'development' : 'production'} server started`), {
timestamp: true,
});

logger.info(
`
${c.dim('┃')} Local ${c.cyan(address.replace(/::1?/, 'localhost'))}` +
if (address?.includes(IP_EXPOSED))
logger.info(
`${
address?.includes(IP_EXPOSED)
? `\n${c.dim('┃')} Network ${c.cyan(address)}`
: ''
}
`,
);
}`,
);
else
logger.info(
`
${c.dim('┃')} Local ${c.cyan(address.replace(/::1?/, 'localhost'))}`,
);
}

// NOTE: UNUSED (keep?)
function sendHtml(res: ServerResponse, payload: unknown) {
res.setHeader('content/type', 'text/html');
res.end(payload);
Expand Down Expand Up @@ -70,6 +74,7 @@ export type LocalMiddlewareContext = {
response: ResponseInit;
};

// NOTE: NOT EXPOSED FOR NOW. Maybe just put in docs.
export type BasicAuthUser = { userName: string | null };

/**
Expand Down Expand Up @@ -104,7 +109,3 @@ export const authenticateBasic = (

return { userName: null };
};

export function getClientDistPath(root: string) {
return fileURLToPath(new URL(CLIENT_DIST_DIR, root));
}
3 changes: 3 additions & 0 deletions packages/gracile/src/hono.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// export * from '@gracile/engine/dev/.old/server';
export * from '@gracile/engine/server/adapters/hono';
export * from '@gracile/engine/server/env';
3 changes: 1 addition & 2 deletions packages/gracile/src/node.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// export * from '@gracile/engine/dev/.old/server';
export * from '@gracile/engine/server/adapters/node';
export * from '@gracile/engine/server/env';
export * from '@gracile/engine/server/node';
export * from '@gracile/engine/server/utils';

0 comments on commit e0c9c2e

Please sign in to comment.