hono-preact 0.1.0 → 0.2.0
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/README.md +2 -1
- package/dist/adapter-cloudflare.d.ts +1 -0
- package/dist/adapter-cloudflare.d.ts.map +1 -0
- package/dist/adapter-cloudflare.js +2 -0
- package/dist/adapter-node.d.ts +1 -0
- package/dist/adapter-node.d.ts.map +1 -0
- package/dist/adapter-node.js +2 -0
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +1 -1
- package/dist/iso/action.d.ts +10 -14
- package/dist/iso/action.js +57 -21
- package/dist/iso/define-app.d.ts +7 -0
- package/dist/iso/define-app.js +3 -0
- package/dist/iso/define-loader.d.ts +19 -0
- package/dist/iso/define-loader.js +4 -0
- package/dist/iso/define-middleware.d.ts +43 -0
- package/dist/iso/define-middleware.js +6 -0
- package/dist/iso/define-page.d.ts +7 -2
- package/dist/iso/define-page.js +1 -1
- package/dist/iso/define-routes.d.ts +24 -1
- package/dist/iso/define-routes.js +34 -0
- package/dist/iso/define-stream-observer.d.ts +20 -0
- package/dist/iso/define-stream-observer.js +3 -0
- package/dist/iso/index.d.ts +10 -5
- package/dist/iso/index.js +5 -3
- package/dist/iso/internal/contexts.d.ts +0 -2
- package/dist/iso/internal/contexts.js +0 -1
- package/dist/iso/internal/loader-fetch.js +37 -7
- package/dist/iso/internal/loader-runner.js +105 -8
- package/dist/iso/internal/middleware-runner.d.ts +22 -0
- package/dist/iso/internal/middleware-runner.js +79 -0
- package/dist/iso/internal/page-middleware-host.d.ts +13 -0
- package/dist/iso/internal/page-middleware-host.js +119 -0
- package/dist/iso/internal/route-boundary.d.ts +1 -0
- package/dist/iso/internal/route-boundary.js +16 -0
- package/dist/iso/internal/stream-observer-runner.d.ts +13 -0
- package/dist/iso/internal/stream-observer-runner.js +48 -0
- package/dist/iso/internal/use-partitioner.d.ts +9 -0
- package/dist/iso/internal/use-partitioner.js +11 -0
- package/dist/iso/internal/use-types.d.ts +7 -0
- package/dist/iso/internal/use-types.js +1 -0
- package/dist/iso/internal.d.ts +5 -4
- package/dist/iso/internal.js +8 -6
- package/dist/iso/outcomes.d.ts +38 -0
- package/dist/iso/outcomes.js +56 -0
- package/dist/iso/page-only.d.ts +5 -0
- package/dist/iso/page-only.js +20 -0
- package/dist/iso/page.d.ts +3 -3
- package/dist/iso/page.js +3 -3
- package/dist/page.d.ts +1 -0
- package/dist/page.d.ts.map +1 -0
- package/dist/page.js +8 -0
- package/dist/server/actions-handler.d.ts +20 -6
- package/dist/server/actions-handler.js +83 -47
- package/dist/server/context.js +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/loaders-handler.d.ts +16 -0
- package/dist/server/loaders-handler.js +94 -17
- package/dist/server/render.d.ts +2 -0
- package/dist/server/render.js +104 -33
- package/dist/server/route-server-modules.d.ts +42 -1
- package/dist/server/route-server-modules.js +184 -0
- package/dist/server/sse.d.ts +24 -1
- package/dist/server/sse.js +56 -4
- package/dist/vite/adapter-cloudflare.d.ts +2 -0
- package/dist/vite/adapter-cloudflare.js +25 -0
- package/dist/vite/adapter-node.d.ts +2 -0
- package/dist/vite/adapter-node.js +49 -0
- package/dist/vite/adapter.d.ts +29 -0
- package/dist/vite/adapter.js +1 -0
- package/dist/vite/client-shim.js +5 -4
- package/dist/vite/guard-strip.js +52 -27
- package/dist/vite/hono-preact.d.ts +6 -6
- package/dist/vite/hono-preact.js +48 -77
- package/dist/vite/index.d.ts +2 -1
- package/dist/vite/index.js +1 -1
- package/dist/vite/node-dev-server.d.ts +4 -0
- package/dist/vite/node-dev-server.js +121 -0
- package/dist/vite/server-entry.d.ts +30 -7
- package/dist/vite/server-entry.js +161 -78
- package/dist/vite/server-exports-contract.d.ts +6 -0
- package/dist/vite/server-exports-contract.js +43 -0
- package/dist/vite/server-loader-validation.js +36 -9
- package/dist/vite/server-loaders-parser.d.ts +17 -1
- package/dist/vite/server-loaders-parser.js +41 -0
- package/dist/vite/server-only.js +20 -2
- package/package.json +32 -4
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* Static context the framework hands an adapter. `command` and `outDir`
|
|
4
|
+
* are intentionally absent: they are not known when honoPreact() builds its
|
|
5
|
+
* plugin array. Adapters that need them read them from their own plugin
|
|
6
|
+
* hooks (config / configResolved).
|
|
7
|
+
*/
|
|
8
|
+
export interface HonoPreactAdapterContext {
|
|
9
|
+
/** Vite project root (process.cwd() when honoPreact() is called). */
|
|
10
|
+
root: string;
|
|
11
|
+
/** Absolute path of the framework-generated core Hono app module. */
|
|
12
|
+
coreAppModuleId: string;
|
|
13
|
+
/** Absolute path where the adapter's wrapEntry() output is written. */
|
|
14
|
+
entryWrapperId: string;
|
|
15
|
+
/** Absolute path of the user's api module, if it exists. Used by adapters
|
|
16
|
+
* that need to reach api-module exports (e.g. the Node adapter's
|
|
17
|
+
* WebSocket `injectWebSocket`). Undefined when the project has no api.ts. */
|
|
18
|
+
apiModuleId?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A deployment target. `vitePlugins()` contributes the terminal build/dev
|
|
22
|
+
* plugins; `wrapEntry()` returns the platform tail that imports the core
|
|
23
|
+
* Hono app module and adapts it to the runtime.
|
|
24
|
+
*/
|
|
25
|
+
export interface HonoPreactAdapter {
|
|
26
|
+
name: string;
|
|
27
|
+
vitePlugins(ctx: HonoPreactAdapterContext): Plugin[];
|
|
28
|
+
wrapEntry(ctx: HonoPreactAdapterContext): string;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/vite/client-shim.js
CHANGED
|
@@ -17,10 +17,11 @@ export function clientShimPlugin(clientEntry) {
|
|
|
17
17
|
return {
|
|
18
18
|
name: 'hono-preact:client-shim',
|
|
19
19
|
enforce: 'pre',
|
|
20
|
-
apply(_, { command
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
20
|
+
apply(_, { command }) {
|
|
21
|
+
// The shim is needed for dev and for the build. The `transform` hook
|
|
22
|
+
// below self-gates to the client entry module, so it never injects
|
|
23
|
+
// into SSR/worker code regardless of build environment.
|
|
24
|
+
return command === 'serve' || command === 'build';
|
|
24
25
|
},
|
|
25
26
|
configResolved(config) {
|
|
26
27
|
resolvedEntry = path.resolve(config.root, clientEntry);
|
package/dist/vite/guard-strip.js
CHANGED
|
@@ -2,10 +2,33 @@ import { parse } from '@babel/parser';
|
|
|
2
2
|
import MagicString from 'magic-string';
|
|
3
3
|
import { BABEL_PARSER_PLUGINS } from './parser-options.js';
|
|
4
4
|
const ISO_PACKAGE_SOURCES = new Set(['../iso/index.js', 'hono-preact']);
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// In the server bundle we strip anything client-only. The replacement
|
|
6
|
+
// `fn` arity matches the documented `(ctx, next) => Promise<void | Outcome>`
|
|
7
|
+
// shape so any user introspecting `mw.fn` sees the right signature; the
|
|
8
|
+
// framework path filters on `runs` before invoking and never executes a
|
|
9
|
+
// wrong-env body.
|
|
10
|
+
const SERVER_BUNDLE_STRIPS = [
|
|
11
|
+
{
|
|
12
|
+
name: 'defineClientMiddleware',
|
|
13
|
+
replacement: `{ __kind: 'middleware', runs: 'client', fn: (_ctx, next) => next() }`,
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
// In the client bundle we strip anything server-only. Stream observers
|
|
17
|
+
// fire on the server-side streaming pipeline (start/chunk/end/error/abort)
|
|
18
|
+
// so they're server-only too.
|
|
19
|
+
const CLIENT_BUNDLE_STRIPS = [
|
|
20
|
+
{
|
|
21
|
+
name: 'defineServerMiddleware',
|
|
22
|
+
replacement: `{ __kind: 'middleware', runs: 'server', fn: (_ctx, next) => next() }`,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'defineStreamObserver',
|
|
26
|
+
replacement: `{ __kind: 'observer' }`,
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
function collectLocalBindings(ast, strips) {
|
|
8
30
|
const bindings = new Map();
|
|
31
|
+
const byName = new Map(strips.map((s) => [s.name, s]));
|
|
9
32
|
for (const node of ast.program.body) {
|
|
10
33
|
if (node.type !== 'ImportDeclaration')
|
|
11
34
|
continue;
|
|
@@ -17,11 +40,9 @@ function collectLocalBindings(ast, targets) {
|
|
|
17
40
|
continue;
|
|
18
41
|
if (spec.imported.type !== 'Identifier')
|
|
19
42
|
continue;
|
|
20
|
-
const
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
bindings.set(spec.local.name, name);
|
|
24
|
-
}
|
|
43
|
+
const strategy = byName.get(spec.imported.name);
|
|
44
|
+
if (strategy) {
|
|
45
|
+
bindings.set(spec.local.name, strategy);
|
|
25
46
|
}
|
|
26
47
|
}
|
|
27
48
|
}
|
|
@@ -38,18 +59,15 @@ function findCallsByLocalName(node, bindings, hits) {
|
|
|
38
59
|
const n = node;
|
|
39
60
|
if (n.type === 'CallExpression' &&
|
|
40
61
|
n.callee?.type === 'Identifier' &&
|
|
41
|
-
n.callee.name
|
|
42
|
-
bindings.
|
|
43
|
-
n.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
argStart: n.arguments[0].start,
|
|
51
|
-
argEnd: n.arguments[0].end,
|
|
52
|
-
});
|
|
62
|
+
n.callee.name) {
|
|
63
|
+
const strategy = bindings.get(n.callee.name);
|
|
64
|
+
if (strategy && n.start !== undefined && n.end !== undefined) {
|
|
65
|
+
hits.push({
|
|
66
|
+
strategy,
|
|
67
|
+
start: n.start,
|
|
68
|
+
end: n.end,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
53
71
|
}
|
|
54
72
|
for (const key of Object.keys(node)) {
|
|
55
73
|
if (key === 'loc' ||
|
|
@@ -66,19 +84,27 @@ export function guardStripPlugin() {
|
|
|
66
84
|
transform(code, id, options) {
|
|
67
85
|
if (!/\.[jt]sx?$/.test(id))
|
|
68
86
|
return;
|
|
87
|
+
// F7: `.server.*` files are intentionally skipped in both bundles.
|
|
88
|
+
// In the client bundle the server-only stub plugin already rewrites
|
|
89
|
+
// imports of these files; in the server bundle the file's own
|
|
90
|
+
// body stays as-authored. The validation plugin restricts a
|
|
91
|
+
// `.server.*` module's named exports to the allowlist, so a user
|
|
92
|
+
// cannot land a `defineClientMiddleware(...)` value as a recognized
|
|
93
|
+
// export and ship it to the server.
|
|
69
94
|
if (/\.server\.[jt]sx?$/.test(id))
|
|
70
95
|
return;
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
96
|
+
const strips = options?.ssr ? SERVER_BUNDLE_STRIPS : CLIENT_BUNDLE_STRIPS;
|
|
97
|
+
// Cheap pre-filter: only parse files that mention at least one of the
|
|
98
|
+
// symbols we strip. Avoids parsing the entire dep graph just to
|
|
99
|
+
// confirm no strips apply.
|
|
100
|
+
if (!strips.some((s) => code.includes(s.name)))
|
|
75
101
|
return;
|
|
76
102
|
const ast = parse(code, {
|
|
77
103
|
sourceType: 'module',
|
|
78
104
|
plugins: BABEL_PARSER_PLUGINS,
|
|
79
105
|
errorRecovery: true,
|
|
80
106
|
});
|
|
81
|
-
const bindings = collectLocalBindings(ast,
|
|
107
|
+
const bindings = collectLocalBindings(ast, strips);
|
|
82
108
|
if (bindings.size === 0)
|
|
83
109
|
return;
|
|
84
110
|
const hits = [];
|
|
@@ -87,9 +113,8 @@ export function guardStripPlugin() {
|
|
|
87
113
|
return;
|
|
88
114
|
const s = new MagicString(code);
|
|
89
115
|
for (const hit of [...hits].reverse()) {
|
|
90
|
-
s.overwrite(hit.
|
|
116
|
+
s.overwrite(hit.start, hit.end, hit.strategy.replacement);
|
|
91
117
|
}
|
|
92
|
-
s.prepend(`import { ${NOOP_LOCAL_NAME} } from '${NOOP_IMPORT_SOURCE}';\n`);
|
|
93
118
|
return { code: s.toString(), map: s.generateMap({ hires: true }) };
|
|
94
119
|
},
|
|
95
120
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Plugin } from 'vite';
|
|
2
|
+
import type { HonoPreactAdapter } from './adapter.js';
|
|
2
3
|
export interface HonoPreactOptions {
|
|
4
|
+
/** Deployment target. Required. See hono-preact/adapter-cloudflare. */
|
|
5
|
+
adapter: HonoPreactAdapter;
|
|
3
6
|
layout?: string;
|
|
4
7
|
routes?: string;
|
|
5
8
|
api?: string;
|
|
9
|
+
appConfig?: string;
|
|
6
10
|
clientEntry?: string;
|
|
7
|
-
entry?: string;
|
|
8
|
-
clientBuild?: BuildEnvironmentOptions;
|
|
9
|
-
serverBuild?: BuildEnvironmentOptions;
|
|
10
|
-
sharedBuild?: BuildEnvironmentOptions;
|
|
11
11
|
}
|
|
12
|
-
export declare function honoPreact(options
|
|
12
|
+
export declare function honoPreact(options: HonoPreactOptions): Plugin[];
|
package/dist/vite/hono-preact.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import build from '@hono/vite-build/cloudflare-workers';
|
|
2
|
-
import devServer, { defaultOptions } from '@hono/vite-dev-server';
|
|
3
|
-
import cloudflareAdapter from '@hono/vite-dev-server/cloudflare';
|
|
4
1
|
import preact from '@preact/preset-vite';
|
|
5
2
|
import { clientShimPlugin } from './client-shim.js';
|
|
6
3
|
import { clientEntryPlugin, VIRTUAL_CLIENT_ENTRY_ID } from './client-entry.js';
|
|
@@ -8,68 +5,56 @@ import { serverLoaderValidationPlugin } from './server-loader-validation.js';
|
|
|
8
5
|
import { moduleKeyPlugin } from './module-key-plugin.js';
|
|
9
6
|
import { serverOnlyPlugin } from './server-only.js';
|
|
10
7
|
import { guardStripPlugin } from './guard-strip.js';
|
|
11
|
-
import {
|
|
12
|
-
export function honoPreact(options
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
import { generatedCoreAppAbsPath, generatedEntryWrapperAbsPath, serverEntryPlugin, } from './server-entry.js';
|
|
9
|
+
export function honoPreact(options) {
|
|
10
|
+
// `?? {}` is deliberate: TypeScript types `options` as required, but a
|
|
11
|
+
// zero-arg `honoPreact()` call still reaches here at runtime. Without the
|
|
12
|
+
// fallback, destructuring `undefined` throws a cryptic TypeError; with it,
|
|
13
|
+
// the friendly `adapter`-required guard below fires instead.
|
|
14
|
+
const { adapter, layout = 'src/Layout.tsx', routes = 'src/routes.ts', api = 'src/api.ts', appConfig = 'src/app-config.ts', clientEntry = VIRTUAL_CLIENT_ENTRY_ID, } = options ?? {};
|
|
15
|
+
if (!adapter) {
|
|
16
|
+
throw new Error('[hono-preact] honoPreact() requires an `adapter` option. ' +
|
|
17
|
+
"Import one, e.g. `import { cloudflareAdapter } from 'hono-preact/adapter-cloudflare'`, " +
|
|
18
|
+
'and pass `honoPreact({ adapter: cloudflareAdapter() })`.');
|
|
19
|
+
}
|
|
20
|
+
const coreAppPath = generatedCoreAppAbsPath();
|
|
21
|
+
const entryWrapperPath = generatedEntryWrapperAbsPath();
|
|
22
|
+
const ctx = {
|
|
23
|
+
root: process.cwd(),
|
|
24
|
+
coreAppModuleId: coreAppPath,
|
|
25
|
+
entryWrapperId: entryWrapperPath,
|
|
26
|
+
};
|
|
27
|
+
// Shared config plus the `client` build environment's input. The worker
|
|
28
|
+
// environment is configured by the adapter's plugins; the `client`
|
|
29
|
+
// environment's entry is framework-owned (every adapter needs the same
|
|
30
|
+
// browser bundle) so it lives here. Without it, the client environment has
|
|
31
|
+
// no input and `vite build` emits no client JavaScript. The
|
|
32
|
+
// `static/client.js` entry name is the URL the SSR layer references and
|
|
33
|
+
// must stay stable.
|
|
16
34
|
const configPlugin = {
|
|
17
35
|
name: 'hono-preact:config',
|
|
18
|
-
config(
|
|
19
|
-
|
|
36
|
+
config() {
|
|
37
|
+
return {
|
|
20
38
|
resolve: {
|
|
21
39
|
dedupe: ['preact', 'preact/compat', 'preact/hooks', 'preact-iso'],
|
|
22
40
|
},
|
|
23
41
|
build: {
|
|
24
42
|
target: 'esnext',
|
|
25
43
|
assetsDir: 'static',
|
|
26
|
-
ssrEmitAssets: true,
|
|
27
|
-
minify: true,
|
|
28
|
-
...sharedBuild,
|
|
29
44
|
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
...restClientBuild,
|
|
41
|
-
rollupOptions: {
|
|
42
|
-
input: userRollup?.input ?? [clientEntry],
|
|
43
|
-
output: {
|
|
44
|
-
entryFileNames: 'static/client.js',
|
|
45
|
-
chunkFileNames: 'static/[name]-[hash].js',
|
|
46
|
-
assetFileNames: 'static/[name]-[hash].[ext]',
|
|
47
|
-
// Array-form output is not supported; use an OutputOptions object to
|
|
48
|
-
// override individual fields (entryFileNames, chunkFileNames, etc.).
|
|
49
|
-
...(userRollup?.output && !Array.isArray(userRollup.output)
|
|
50
|
-
? userRollup.output
|
|
51
|
-
: {}),
|
|
45
|
+
environments: {
|
|
46
|
+
client: {
|
|
47
|
+
build: {
|
|
48
|
+
rollupOptions: {
|
|
49
|
+
input: [clientEntry],
|
|
50
|
+
output: {
|
|
51
|
+
entryFileNames: 'static/client.js',
|
|
52
|
+
chunkFileNames: 'static/[name]-[hash].js',
|
|
53
|
+
assetFileNames: 'static/[name]-[hash].[ext]',
|
|
54
|
+
},
|
|
52
55
|
},
|
|
53
56
|
},
|
|
54
57
|
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
...shared,
|
|
59
|
-
ssr: {
|
|
60
|
-
noExternal: ['preact-render-to-string', 'preact-iso', 'hono-preact'],
|
|
61
|
-
},
|
|
62
|
-
build: {
|
|
63
|
-
...shared.build,
|
|
64
|
-
// Inline sourcemaps so SSR error stacks point at user source
|
|
65
|
-
// (e.g. `pages/issue.server.ts:42`) instead of the rolled-up
|
|
66
|
-
// Worker chunk. We pick `inline` over a separate .map file because
|
|
67
|
-
// SSR is a single bundle and Workers tooling will not look at a
|
|
68
|
-
// sibling .map. Bundle size grows; for typical app server bundles
|
|
69
|
-
// this is a few hundred KB at most and is worth the debuggability.
|
|
70
|
-
// Users can override by passing `serverBuild.sourcemap: false`.
|
|
71
|
-
sourcemap: 'inline',
|
|
72
|
-
...serverBuild,
|
|
73
58
|
},
|
|
74
59
|
};
|
|
75
60
|
},
|
|
@@ -78,34 +63,20 @@ export function honoPreact(options = {}) {
|
|
|
78
63
|
configPlugin,
|
|
79
64
|
clientShimPlugin(clientEntry),
|
|
80
65
|
clientEntryPlugin({ routes }),
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
: []),
|
|
66
|
+
serverEntryPlugin({
|
|
67
|
+
layout,
|
|
68
|
+
routes,
|
|
69
|
+
api,
|
|
70
|
+
appConfig,
|
|
71
|
+
adapter,
|
|
72
|
+
coreAppPath,
|
|
73
|
+
entryWrapperPath,
|
|
74
|
+
}),
|
|
91
75
|
serverLoaderValidationPlugin(),
|
|
92
76
|
moduleKeyPlugin(),
|
|
93
77
|
serverOnlyPlugin(),
|
|
94
78
|
guardStripPlugin(),
|
|
95
|
-
|
|
96
|
-
apply: (_, { command, mode }) => command === 'build' && mode !== 'client',
|
|
97
|
-
}),
|
|
98
|
-
Object.assign(devServer({
|
|
99
|
-
entry: resolvedEntry,
|
|
100
|
-
exclude: [
|
|
101
|
-
...defaultOptions.exclude,
|
|
102
|
-
/\.scss/,
|
|
103
|
-
/\.css/,
|
|
104
|
-
/\?url/,
|
|
105
|
-
/\?inline/,
|
|
106
|
-
],
|
|
107
|
-
adapter: cloudflareAdapter,
|
|
108
|
-
}), { apply: 'serve' }),
|
|
79
|
+
...adapter.vitePlugins(ctx),
|
|
109
80
|
...preact(),
|
|
110
81
|
];
|
|
111
82
|
}
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { honoPreact } from './hono-preact.js';
|
|
|
2
2
|
export { serverLoaderValidationPlugin } from './server-loader-validation.js';
|
|
3
3
|
export { serverOnlyPlugin, VITE_ROOT_ACCESSOR } from './server-only.js';
|
|
4
4
|
export { moduleKeyPlugin } from './module-key-plugin.js';
|
|
5
|
-
export {
|
|
5
|
+
export { GENERATED_CORE_APP_RELATIVE, GENERATED_ENTRY_WRAPPER_RELATIVE, generatedCoreAppAbsPath, generatedEntryWrapperAbsPath, serverEntryPlugin, } from './server-entry.js';
|
|
6
|
+
export type { HonoPreactAdapter, HonoPreactAdapterContext } from './adapter.js';
|
|
6
7
|
export { clientEntryPlugin, VIRTUAL_CLIENT_ENTRY_ID } from './client-entry.js';
|
|
7
8
|
export { guardStripPlugin } from './guard-strip.js';
|
package/dist/vite/index.js
CHANGED
|
@@ -2,6 +2,6 @@ export { honoPreact } from './hono-preact.js';
|
|
|
2
2
|
export { serverLoaderValidationPlugin } from './server-loader-validation.js';
|
|
3
3
|
export { serverOnlyPlugin, VITE_ROOT_ACCESSOR } from './server-only.js';
|
|
4
4
|
export { moduleKeyPlugin } from './module-key-plugin.js';
|
|
5
|
-
export {
|
|
5
|
+
export { GENERATED_CORE_APP_RELATIVE, GENERATED_ENTRY_WRAPPER_RELATIVE, generatedCoreAppAbsPath, generatedEntryWrapperAbsPath, serverEntryPlugin, } from './server-entry.js';
|
|
6
6
|
export { clientEntryPlugin, VIRTUAL_CLIENT_ENTRY_ID } from './client-entry.js';
|
|
7
7
|
export { guardStripPlugin } from './guard-strip.js';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createServerModuleRunner } from 'vite';
|
|
2
|
+
export function nodeBuildPlugin(ctx) {
|
|
3
|
+
return {
|
|
4
|
+
name: 'hono-preact:node-build',
|
|
5
|
+
config() {
|
|
6
|
+
return {
|
|
7
|
+
environments: {
|
|
8
|
+
// The Node target has no Cloudflare-style plugin to set the client
|
|
9
|
+
// outDir, so set it here. wrapEntry()'s serveStatic expects the
|
|
10
|
+
// client bundle at dist/client.
|
|
11
|
+
client: {
|
|
12
|
+
build: { outDir: 'dist/client' },
|
|
13
|
+
},
|
|
14
|
+
ssr: {
|
|
15
|
+
build: {
|
|
16
|
+
outDir: 'dist/server',
|
|
17
|
+
ssr: true,
|
|
18
|
+
rollupOptions: {
|
|
19
|
+
input: [ctx.entryWrapperId],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
builder: {
|
|
25
|
+
async buildApp(builder) {
|
|
26
|
+
await builder.build(builder.environments.client);
|
|
27
|
+
await builder.build(builder.environments.ssr);
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function nodeDevServerPlugin(ctx) {
|
|
35
|
+
return {
|
|
36
|
+
name: 'hono-preact:node-dev-server',
|
|
37
|
+
apply: 'serve',
|
|
38
|
+
configureServer(server) {
|
|
39
|
+
const runner = createServerModuleRunner(server.environments.ssr);
|
|
40
|
+
// Wire the WebSocket upgrade. @hono/node-ws's injectWebSocket(target)
|
|
41
|
+
// just calls target.on('upgrade', fn); we pass a shim that captures
|
|
42
|
+
// that handler so we can invoke it with Node's real upgrade args.
|
|
43
|
+
// Multiple 'upgrade' listeners coexist fine with Vite's own HMR one.
|
|
44
|
+
server.httpServer?.on('upgrade', async (req, socket, head) => {
|
|
45
|
+
try {
|
|
46
|
+
const { injectWebSocket } = await runner.import(ctx.entryWrapperId);
|
|
47
|
+
if (!injectWebSocket)
|
|
48
|
+
return;
|
|
49
|
+
let handler;
|
|
50
|
+
injectWebSocket({
|
|
51
|
+
on(event, fn) {
|
|
52
|
+
if (event === 'upgrade')
|
|
53
|
+
handler = fn;
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
handler?.(req, socket, head);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
console.error('[hono-preact] dev ws upgrade error', err);
|
|
60
|
+
socket.destroy();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
// Register the SSR middleware synchronously (not via the returned post
|
|
64
|
+
// hook). The post hook runs after Vite's spaFallbackMiddleware, which
|
|
65
|
+
// rewrites req.url to /index.html and makes the SSR app 404. Synchronous
|
|
66
|
+
// registration puts this ahead of Vite's HTML/SPA middlewares.
|
|
67
|
+
server.middlewares.use(async (req, res, next) => {
|
|
68
|
+
try {
|
|
69
|
+
// Vite-internal requests (its HMR client, source modules under
|
|
70
|
+
// /@fs and /@id, optimized deps) must reach Vite's later
|
|
71
|
+
// middlewares, or client hydration and HMR break. The SSR app only
|
|
72
|
+
// owns application routes, so pass these through. Same model as
|
|
73
|
+
// @hono/vite-dev-server's `exclude` option.
|
|
74
|
+
const path = (req.url ?? '').split('?')[0];
|
|
75
|
+
if (path.startsWith('/@') || path.startsWith('/node_modules/')) {
|
|
76
|
+
return next();
|
|
77
|
+
}
|
|
78
|
+
const { default: app } = (await runner.import(ctx.entryWrapperId));
|
|
79
|
+
const url = `http://${req.headers.host}${req.url}`;
|
|
80
|
+
const method = req.method ?? 'GET';
|
|
81
|
+
const headers = new Headers();
|
|
82
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
83
|
+
if (typeof v === 'string')
|
|
84
|
+
headers.set(k, v);
|
|
85
|
+
else if (Array.isArray(v))
|
|
86
|
+
v.forEach((vv) => headers.append(k, vv));
|
|
87
|
+
}
|
|
88
|
+
let body;
|
|
89
|
+
if (method !== 'GET' && method !== 'HEAD') {
|
|
90
|
+
const chunks = [];
|
|
91
|
+
for await (const c of req)
|
|
92
|
+
chunks.push(c);
|
|
93
|
+
if (chunks.length) {
|
|
94
|
+
const buf = Buffer.concat(chunks);
|
|
95
|
+
// Copy into a fresh ArrayBuffer: BodyInit accepts ArrayBuffer,
|
|
96
|
+
// and this sidesteps Buffer<ArrayBufferLike> typing friction.
|
|
97
|
+
body = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const request = new Request(url, { method, headers, body });
|
|
101
|
+
const response = await app.fetch(request);
|
|
102
|
+
res.statusCode = response.status;
|
|
103
|
+
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
104
|
+
if (response.body) {
|
|
105
|
+
const reader = response.body.getReader();
|
|
106
|
+
for (;;) {
|
|
107
|
+
const { done, value } = await reader.read();
|
|
108
|
+
if (done)
|
|
109
|
+
break;
|
|
110
|
+
res.write(value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
res.end();
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
next(err);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -1,26 +1,49 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
-
|
|
2
|
+
import type { HonoPreactAdapter } from './adapter.js';
|
|
3
|
+
export interface GenerateCoreAppModuleOptions {
|
|
3
4
|
layoutAbsPath: string;
|
|
4
5
|
routesAbsPath: string;
|
|
5
6
|
apiAbsPath: string | undefined;
|
|
7
|
+
appConfigAbsPath: string | undefined;
|
|
6
8
|
}
|
|
7
|
-
export declare function
|
|
8
|
-
export type
|
|
9
|
+
export declare function generateCoreAppModule(opts: GenerateCoreAppModuleOptions): string;
|
|
10
|
+
export type ApiShadowingRoute = {
|
|
9
11
|
kind: 'wildcard';
|
|
10
12
|
method: string;
|
|
11
13
|
pattern: string;
|
|
12
14
|
line: number | undefined;
|
|
15
|
+
severity: 'error';
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'reserved';
|
|
18
|
+
method: string;
|
|
19
|
+
pattern: string;
|
|
20
|
+
line: number | undefined;
|
|
21
|
+
severity: 'error';
|
|
13
22
|
} | {
|
|
14
23
|
kind: 'notFound';
|
|
15
24
|
line: number | undefined;
|
|
25
|
+
severity: 'warning';
|
|
16
26
|
};
|
|
17
|
-
export declare function
|
|
18
|
-
export declare const
|
|
19
|
-
export declare
|
|
27
|
+
export declare function findApiShadowingRoutes(source: string): ApiShadowingRoute[];
|
|
28
|
+
export declare const GENERATED_CORE_APP_RELATIVE = "node_modules/.vite/hono-preact/core-app.tsx";
|
|
29
|
+
export declare const GENERATED_ENTRY_WRAPPER_RELATIVE = "node_modules/.vite/hono-preact/server-entry.tsx";
|
|
30
|
+
export declare function generatedCoreAppAbsPath(cwd?: string): string;
|
|
31
|
+
export declare function generatedEntryWrapperAbsPath(cwd?: string): string;
|
|
20
32
|
export interface ServerEntryPluginOptions {
|
|
21
33
|
layout: string;
|
|
22
34
|
routes: string;
|
|
23
35
|
api: string;
|
|
24
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Project-relative or absolute path to the user's app-config file. The
|
|
38
|
+
* `hono-preact` umbrella plugin always supplies a default
|
|
39
|
+
* (`src/app-config.ts`), so this is required here even though it's
|
|
40
|
+
* optional from the user's perspective: missing the file on disk is
|
|
41
|
+
* allowed (an inline `{ use: [] }` falls back into the generated core
|
|
42
|
+
* app), but the option name itself must be supplied.
|
|
43
|
+
*/
|
|
44
|
+
appConfig: string;
|
|
45
|
+
adapter: HonoPreactAdapter;
|
|
46
|
+
coreAppPath: string;
|
|
47
|
+
entryWrapperPath: string;
|
|
25
48
|
}
|
|
26
49
|
export declare function serverEntryPlugin(opts: ServerEntryPluginOptions): Plugin;
|