@hyperspan/framework 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.ts +22 -20
- package/dist/assets.d.ts +15 -3
- package/dist/assets.js +5 -162
- package/dist/server.d.ts +12 -1
- package/dist/server.js +67 -298
- package/package.json +11 -7
- package/src/assets.ts +4 -3
- package/src/clientjs/hyperspan-client.ts +4 -4
- package/src/server.ts +27 -18
- package/dist/index.d.ts +0 -121
- package/dist/index.js +0 -2484
- package/src/index.ts +0 -1
package/src/assets.ts
CHANGED
|
@@ -11,7 +11,7 @@ const PWD = import.meta.dir;
|
|
|
11
11
|
*/
|
|
12
12
|
export const clientJSFiles = new Map<string, { src: string; type?: string }>();
|
|
13
13
|
export async function buildClientJS() {
|
|
14
|
-
const sourceFile = resolve(PWD, '../', './
|
|
14
|
+
const sourceFile = resolve(PWD, '../', './src/clientjs/hyperspan-client.ts');
|
|
15
15
|
const output = await Bun.build({
|
|
16
16
|
entrypoints: [sourceFile],
|
|
17
17
|
outdir: `./public/_hs/js`,
|
|
@@ -20,8 +20,8 @@ export async function buildClientJS() {
|
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
const jsFile = output.outputs[0].path.split('/').reverse()[0];
|
|
23
|
+
|
|
23
24
|
clientJSFiles.set('_hs', { src: '/_hs/js/' + jsFile });
|
|
24
|
-
return jsFile;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -61,7 +61,7 @@ export async function buildClientCSS() {
|
|
|
61
61
|
export function hyperspanStyleTags() {
|
|
62
62
|
const cssFiles = Array.from(clientCSSFiles.entries());
|
|
63
63
|
return html`${cssFiles.map(
|
|
64
|
-
([
|
|
64
|
+
([_, file]) => html`<link rel="stylesheet" href="/_hs/css/${file}" />`
|
|
65
65
|
)}`;
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -71,6 +71,7 @@ export function hyperspanStyleTags() {
|
|
|
71
71
|
*/
|
|
72
72
|
export function hyperspanScriptTags() {
|
|
73
73
|
const jsFiles = Array.from(clientJSFiles.entries());
|
|
74
|
+
|
|
74
75
|
return html`
|
|
75
76
|
<script type="importmap">
|
|
76
77
|
{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {html} from '@hyperspan/html';
|
|
2
|
+
import {Idiomorph} from './idiomorph.esm';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Used for streaming content from the server to the client.
|
|
@@ -20,7 +20,7 @@ function htmlAsyncContentObserver() {
|
|
|
20
20
|
asyncContent.forEach((el: any) => {
|
|
21
21
|
try {
|
|
22
22
|
// Also observe child nodes for nested async content
|
|
23
|
-
asyncContentObserver.observe(el.content, {
|
|
23
|
+
asyncContentObserver.observe(el.content, {childList: true, subtree: true});
|
|
24
24
|
|
|
25
25
|
const slotId = el.id.replace('_content', '');
|
|
26
26
|
const slotEl = document.getElementById(slotId);
|
|
@@ -41,7 +41,7 @@ function htmlAsyncContentObserver() {
|
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
});
|
|
44
|
-
asyncContentObserver.observe(document.body, {
|
|
44
|
+
asyncContentObserver.observe(document.body, {childList: true, subtree: true});
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
htmlAsyncContentObserver();
|
package/src/server.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {readdir} from 'node:fs/promises';
|
|
2
|
-
import {basename, extname, join} from 'node:path';
|
|
3
|
-
import {TmplHtml, html, renderStream, renderAsync, render} from '@hyperspan/html';
|
|
4
|
-
import {isbot} from 'isbot';
|
|
5
|
-
import {buildClientJS, buildClientCSS} from './assets';
|
|
6
|
-
import {Hono} from 'hono';
|
|
7
|
-
import {serveStatic} from 'hono/bun';
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
|
+
import { basename, extname, join } from 'node:path';
|
|
3
|
+
import { TmplHtml, html, renderStream, renderAsync, render } from '@hyperspan/html';
|
|
4
|
+
import { isbot } from 'isbot';
|
|
5
|
+
import { buildClientJS, buildClientCSS } from './assets';
|
|
6
|
+
import { Hono } from 'hono';
|
|
7
|
+
import { serveStatic } from 'hono/bun';
|
|
8
8
|
import * as z from 'zod';
|
|
9
|
-
import type {Context, Handler} from 'hono';
|
|
9
|
+
import type { Context, Handler } from 'hono';
|
|
10
10
|
|
|
11
11
|
export const IS_PROD = process.env.NODE_ENV === 'production';
|
|
12
12
|
const CWD = process.cwd();
|
|
@@ -193,7 +193,7 @@ export async function runFileRoute(RouteModule: any, context: Context): Promise<
|
|
|
193
193
|
if (!routeMethodHandler) {
|
|
194
194
|
return new Response('Method Not Allowed', {
|
|
195
195
|
status: 405,
|
|
196
|
-
headers: {'content-type': 'text/plain'},
|
|
196
|
+
headers: { 'content-type': 'text/plain' },
|
|
197
197
|
});
|
|
198
198
|
}
|
|
199
199
|
|
|
@@ -206,8 +206,15 @@ export async function runFileRoute(RouteModule: any, context: Context): Promise<
|
|
|
206
206
|
return routeContent;
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
let routeKind = typeof routeContent;
|
|
210
|
+
|
|
209
211
|
// Render TmplHtml if returned from route handler
|
|
210
|
-
if (
|
|
212
|
+
if (
|
|
213
|
+
routeKind === 'object' &&
|
|
214
|
+
(routeContent instanceof TmplHtml ||
|
|
215
|
+
routeContent.constructor.name === 'TmplHtml' ||
|
|
216
|
+
routeContent?._kind === 'TmplHtml')
|
|
217
|
+
) {
|
|
211
218
|
if (streamingEnabled) {
|
|
212
219
|
return new StreamResponse(renderStream(routeContent)) as Response;
|
|
213
220
|
} else {
|
|
@@ -216,6 +223,8 @@ export async function runFileRoute(RouteModule: any, context: Context): Promise<
|
|
|
216
223
|
}
|
|
217
224
|
}
|
|
218
225
|
|
|
226
|
+
console.log('Returning unknown type... ', routeContent);
|
|
227
|
+
|
|
219
228
|
return routeContent;
|
|
220
229
|
} catch (e) {
|
|
221
230
|
console.error(e);
|
|
@@ -235,13 +244,13 @@ async function runAPIRoute(routeFn: any, context: Context, middlewareResult?: an
|
|
|
235
244
|
|
|
236
245
|
return context.json(
|
|
237
246
|
{
|
|
238
|
-
meta: {success: false},
|
|
247
|
+
meta: { success: false },
|
|
239
248
|
data: {
|
|
240
249
|
message: e.message,
|
|
241
250
|
stack: IS_PROD ? undefined : e.stack?.split('\n'),
|
|
242
251
|
},
|
|
243
252
|
},
|
|
244
|
-
{status: 500}
|
|
253
|
+
{ status: 500 }
|
|
245
254
|
);
|
|
246
255
|
}
|
|
247
256
|
}
|
|
@@ -267,7 +276,7 @@ async function showErrorReponse(context: Context, err: Error) {
|
|
|
267
276
|
export type THSServerConfig = {
|
|
268
277
|
appDir: string;
|
|
269
278
|
staticFileRoot: string;
|
|
270
|
-
rewrites?: Array<{source: string; destination: string}>;
|
|
279
|
+
rewrites?: Array<{ source: string; destination: string }>;
|
|
271
280
|
// For customizing the routes and adding your own...
|
|
272
281
|
beforeRoutesAdded?: (app: Hono) => void;
|
|
273
282
|
afterRoutesAdded?: (app: Hono) => void;
|
|
@@ -287,7 +296,7 @@ export async function buildRoutes(config: THSServerConfig): Promise<THSRouteMap[
|
|
|
287
296
|
// Walk all pages and add them as routes
|
|
288
297
|
const routesDir = join(config.appDir, 'routes');
|
|
289
298
|
console.log(routesDir);
|
|
290
|
-
const files = await readdir(routesDir, {recursive: true});
|
|
299
|
+
const files = await readdir(routesDir, { recursive: true });
|
|
291
300
|
const routes: THSRouteMap[] = [];
|
|
292
301
|
|
|
293
302
|
for (const file of files) {
|
|
@@ -353,7 +362,7 @@ export async function createServer(config: THSServerConfig): Promise<Hono> {
|
|
|
353
362
|
const fullRouteFile = join(CWD, route.file);
|
|
354
363
|
const routePattern = normalizePath(route.route);
|
|
355
364
|
|
|
356
|
-
routeMap.push({route: routePattern, file: route.file});
|
|
365
|
+
routeMap.push({ route: routePattern, file: route.file });
|
|
357
366
|
|
|
358
367
|
// Import route
|
|
359
368
|
const routeModule = await import(fullRouteFile);
|
|
@@ -373,7 +382,7 @@ export async function createServer(config: THSServerConfig): Promise<Hono> {
|
|
|
373
382
|
app.get('/', (context) => {
|
|
374
383
|
return context.text(
|
|
375
384
|
'No routes found. Add routes to app/routes. Example: `app/routes/index.ts`',
|
|
376
|
-
{status: 404}
|
|
385
|
+
{ status: 404 }
|
|
377
386
|
);
|
|
378
387
|
});
|
|
379
388
|
}
|
|
@@ -396,7 +405,7 @@ export async function createServer(config: THSServerConfig): Promise<Hono> {
|
|
|
396
405
|
);
|
|
397
406
|
|
|
398
407
|
app.notFound((context) => {
|
|
399
|
-
return context.text('Not... found?', {status: 404});
|
|
408
|
+
return context.text('Not... found?', { status: 404 });
|
|
400
409
|
});
|
|
401
410
|
|
|
402
411
|
return app;
|
|
@@ -431,7 +440,7 @@ export function createReadableStreamFromAsyncGenerator(output: AsyncGenerator) {
|
|
|
431
440
|
return new ReadableStream({
|
|
432
441
|
async start(controller) {
|
|
433
442
|
while (true) {
|
|
434
|
-
const {done, value} = await output.next();
|
|
443
|
+
const { done, value } = await output.next();
|
|
435
444
|
|
|
436
445
|
if (done) {
|
|
437
446
|
controller.close();
|
package/dist/index.d.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
-
|
|
3
|
-
import { TmplHtml } from '@hyperspan/html';
|
|
4
|
-
import { Context, Handler, Hono } from 'hono';
|
|
5
|
-
|
|
6
|
-
export declare const IS_PROD: boolean;
|
|
7
|
-
/**
|
|
8
|
-
* Route
|
|
9
|
-
* Define a route that can handle a direct HTTP request
|
|
10
|
-
* Route handlers should return a Response or TmplHtml object
|
|
11
|
-
*/
|
|
12
|
-
export declare function createRoute(handler: Handler): HSRoute;
|
|
13
|
-
/**
|
|
14
|
-
* Component
|
|
15
|
-
* Define a component or partial with an optional loading placeholder
|
|
16
|
-
* These can be rendered anywhere inside other templates - even if async.
|
|
17
|
-
*/
|
|
18
|
-
export declare function createComponent(render: () => THSComponentReturn | Promise<THSComponentReturn>): HSComponent;
|
|
19
|
-
/**
|
|
20
|
-
* Form + route handler
|
|
21
|
-
* Automatically handles and parses form data
|
|
22
|
-
*
|
|
23
|
-
* INITIAL IDEA OF HOW THIS WILL WORK:
|
|
24
|
-
* ---
|
|
25
|
-
* 1. Renders component as initial form markup for GET request
|
|
26
|
-
* 2. Bind form onSubmit function to custom client JS handling
|
|
27
|
-
* 3. Submits form with JavaScript fetch()
|
|
28
|
-
* 4. Replaces form content with content from server
|
|
29
|
-
* 5. All validation and save logic is on the server
|
|
30
|
-
* 6. Handles any Exception thrown on server as error displayed in client
|
|
31
|
-
*/
|
|
32
|
-
export declare function createForm(renderForm: (data?: any) => THSResponseTypes, schema?: z.ZodSchema | null): HSFormRoute;
|
|
33
|
-
/**
|
|
34
|
-
* Types
|
|
35
|
-
*/
|
|
36
|
-
export type THSComponentReturn = TmplHtml | string | number | null;
|
|
37
|
-
export type THSResponseTypes = TmplHtml | Response | string | null;
|
|
38
|
-
export declare const HS_DEFAULT_LOADING: () => TmplHtml;
|
|
39
|
-
/**
|
|
40
|
-
* Route handler helper
|
|
41
|
-
*/
|
|
42
|
-
export declare class HSComponent {
|
|
43
|
-
_kind: string;
|
|
44
|
-
_handlers: Record<string, Handler>;
|
|
45
|
-
_loading?: () => TmplHtml;
|
|
46
|
-
render: () => THSComponentReturn | Promise<THSComponentReturn>;
|
|
47
|
-
constructor(render: () => THSComponentReturn | Promise<THSComponentReturn>);
|
|
48
|
-
loading(fn: () => TmplHtml): this;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Route handler helper
|
|
52
|
-
*/
|
|
53
|
-
export declare class HSRoute {
|
|
54
|
-
_kind: string;
|
|
55
|
-
_handlers: Record<string, Handler>;
|
|
56
|
-
_methods: null | string[];
|
|
57
|
-
constructor(handler: Handler);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Form route handler helper
|
|
61
|
-
*/
|
|
62
|
-
export type THSFormRenderer = (data?: any) => THSResponseTypes;
|
|
63
|
-
export declare class HSFormRoute {
|
|
64
|
-
_kind: string;
|
|
65
|
-
_handlers: Record<string, Handler>;
|
|
66
|
-
_form: THSFormRenderer;
|
|
67
|
-
_methods: null | string[];
|
|
68
|
-
_schema: null | z.ZodSchema;
|
|
69
|
-
constructor(renderForm: THSFormRenderer, schema?: z.ZodSchema | null);
|
|
70
|
-
getDefaultData(): unknown;
|
|
71
|
-
/**
|
|
72
|
-
* Get form renderer method
|
|
73
|
-
*/
|
|
74
|
-
renderForm(data?: any): THSResponseTypes;
|
|
75
|
-
get(handler: Handler): this;
|
|
76
|
-
patch(handler: Handler): this;
|
|
77
|
-
post(handler: Handler): this;
|
|
78
|
-
put(handler: Handler): this;
|
|
79
|
-
delete(handler: Handler): this;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Run route from file
|
|
83
|
-
*/
|
|
84
|
-
export declare function runFileRoute(RouteModule: any, context: Context): Promise<Response | false>;
|
|
85
|
-
export type THSServerConfig = {
|
|
86
|
-
appDir: string;
|
|
87
|
-
staticFileRoot: string;
|
|
88
|
-
rewrites?: Array<{
|
|
89
|
-
source: string;
|
|
90
|
-
destination: string;
|
|
91
|
-
}>;
|
|
92
|
-
beforeRoutesAdded?: (app: Hono) => void;
|
|
93
|
-
afterRoutesAdded?: (app: Hono) => void;
|
|
94
|
-
};
|
|
95
|
-
export type THSRouteMap = {
|
|
96
|
-
file: string;
|
|
97
|
-
route: string;
|
|
98
|
-
params: string[];
|
|
99
|
-
};
|
|
100
|
-
export declare function buildRoutes(config: THSServerConfig): Promise<THSRouteMap[]>;
|
|
101
|
-
/**
|
|
102
|
-
* Create and start Bun HTTP server
|
|
103
|
-
*/
|
|
104
|
-
export declare function createServer(config: THSServerConfig): Promise<Hono>;
|
|
105
|
-
/**
|
|
106
|
-
* Streaming HTML Response
|
|
107
|
-
*/
|
|
108
|
-
export declare class StreamResponse extends Response {
|
|
109
|
-
constructor(iterator: AsyncIterator<unknown>, options?: {});
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Does what it says on the tin...
|
|
113
|
-
*/
|
|
114
|
-
export declare function createReadableStreamFromAsyncGenerator(output: AsyncGenerator): ReadableStream<any>;
|
|
115
|
-
/**
|
|
116
|
-
* Normalize URL path
|
|
117
|
-
* Removes trailing slash and lowercases path
|
|
118
|
-
*/
|
|
119
|
-
export declare function normalizePath(urlPath: string): string;
|
|
120
|
-
|
|
121
|
-
export {};
|