@barefootjs/hono 0.30.6 → 0.31.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/src/vite.ts ADDED
@@ -0,0 +1,210 @@
1
+ /**
2
+ * `@barefootjs/hono/vite` — a Hono-specific COMPOSITION of core
3
+ * `@barefootjs/vite`'s `barefoot()`, mirroring `@barefootjs/go-template/
4
+ * vite`'s shape and naming (`barefoot`, named AND default export; a user
5
+ * never passes `adapter`, this constructs `HonoAdapter` itself).
6
+ *
7
+ * import { barefoot } from '@barefootjs/hono/vite'
8
+ *
9
+ * export default defineConfig({
10
+ * base: '/static/components/',
11
+ * build: { outDir: 'dist/static/components' },
12
+ * plugins: barefoot({
13
+ * components: ['src/components'],
14
+ * templates: 'dist/components',
15
+ * }),
16
+ * })
17
+ *
18
+ * ## Why this needs no `afterEmit`-driven combination step (unlike Go)
19
+ *
20
+ * `@barefootjs/go-template/vite` exists mainly to combine every discovered
21
+ * file's `types` fragment into ONE compilable `components.go` — Go's
22
+ * per-file fragments assume a shared `randomID` helper and a single package
23
+ * header, so they are not independently valid Go source (see that module's
24
+ * docstring). Hono's SSR marked template has no such constraint: `generate()`
25
+ * already emits a complete, self-contained `.tsx` file (its own imports,
26
+ * its own types inlined via `sections.types`) that wrangler/bun's own
27
+ * bundler compiles directly — there is nothing across files that needs
28
+ * stitching together. So this composition's core job is just what core's
29
+ * `barefoot()` already does: construct `HonoAdapter` and hand it to core.
30
+ *
31
+ * ## `assets` — the one thing this DOES need, mirroring go-template/vite
32
+ *
33
+ * A hand-written, non-component client bootstrap (e.g. this integration's
34
+ * `client/router-entry.ts`, which boots `@barefootjs/router`) isn't a
35
+ * `.tsx` component, so core's own discovery/`scriptAssets` machinery never
36
+ * sees it — but a plain `.tsx` SSR file (e.g. a blog layout) still needs a
37
+ * `<script src>` for it, and that URL is only knowable after Vite bundles it
38
+ * (dev: origin-based; build: manifest-hashed). `assets` resolves exactly
39
+ * that, into a generated TS module the SSR file can `import` — the
40
+ * TypeScript analogue of `@barefootjs/go-template/vite`'s generated
41
+ * `bf_assets.go`. See that module's docstring for the full "why a
42
+ * companion config-capture plugin" rationale (`afterEmit`'s
43
+ * `AfterEmitContext` is deliberately narrow — no `ResolvedConfig`, no dev
44
+ * origin, no manifest — so a tiny second plugin captures those via its own
45
+ * `configResolved`/`configureServer` hooks for `afterEmit`, in the same
46
+ * closure, to read).
47
+ */
48
+ import { mkdir, readFile, writeFile } from 'node:fs/promises'
49
+ import { dirname, resolve } from 'node:path'
50
+ import type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'
51
+ import { barefoot as coreBarefoot } from '@barefootjs/vite'
52
+ import type { AfterEmitContext } from '@barefootjs/vite'
53
+ import { devModuleUrl, loadManifest, resolveDevOrigin, resolveScriptAssets, toPosixRelative } from '@barefootjs/vite'
54
+ import { HonoAdapter } from './adapter/index.ts'
55
+ import type { HonoAdapterOptions } from './adapter/index.ts'
56
+
57
+ export interface HonoViteOptions {
58
+ /** Source directories to scan for `.tsx` components, relative to the
59
+ * Vite project root (or absolute). */
60
+ components: string[]
61
+ /** Where compiled SSR templates (and `ssrDefaults`) land — relative to
62
+ * the Vite project root (or absolute). This is a backend source
63
+ * directory wrangler/bun's own bundler reads, NOT `build.outDir` (Vite's
64
+ * client-asset output). */
65
+ templates: string
66
+ /** Adapter-specific options passed to `HonoAdapter` (e.g. `clientJsFilename`). */
67
+ adapterOptions?: HonoAdapterOptions
68
+ /**
69
+ * Extra, non-component script entries whose Vite-resolved URL (dev:
70
+ * origin-based; production: content-hashed manifest path) should be
71
+ * exposed to the SSR app as a generated `Assets` map — e.g. a
72
+ * hand-written client bootstrap script that isn't a `.tsx` component, so
73
+ * it never goes through core's discovery/`scriptAssets` machinery, but
74
+ * still needs a `<script src="...">` URL only knowable after bundling.
75
+ *
76
+ * Keyed by the identifier the resolved URL should appear under in the
77
+ * generated map; values are entry paths relative to the Vite project
78
+ * root. You must ALSO register the same path as a Rollup entry yourself
79
+ * via stock `build.rollupOptions.input` — this plugin never adds
80
+ * bundling configuration on your behalf; this option only resolves the
81
+ * URL Vite already bundled it to, it doesn't request the bundling.
82
+ */
83
+ assets?: Record<string, string>
84
+ /** Output path for the generated asset-map TS module, relative to the
85
+ * Vite project root. Default: 'dist/bf-assets.ts'. Ignored when `assets`
86
+ * is empty. */
87
+ assetsOutputFile?: string
88
+ }
89
+
90
+ /** write-if-changed: writes `content` to `absPath` only if it differs from
91
+ * what's already there, logging `label` when it actually wrote. Avoids
92
+ * touching mtime (and so falsely tripping a file watcher, e.g. wrangler's
93
+ * own dev rebuild) on a pass that produced byte-identical output. */
94
+ async function writeIfChanged(absPath: string, content: string, label: string): Promise<void> {
95
+ const prev = await readFile(absPath, 'utf-8').catch(() => null)
96
+ if (prev === content) return
97
+ // Unlike `@barefootjs/go-template/vite`'s `bf_assets.go` (project root by
98
+ // default), this plugin's default `assetsOutputFile` lives under `dist/`
99
+ // — a directory `vite build` may not have created yet on a clean
100
+ // checkout (nothing else in this plugin writes there before `afterEmit`
101
+ // runs), so ensure it exists before writing.
102
+ await mkdir(dirname(absPath), { recursive: true })
103
+ await writeFile(absPath, content)
104
+ console.log(`Generated: ${label}`)
105
+ }
106
+
107
+ /** Resolves ONE asset entry's URL for the current pass: manifest-hashed for
108
+ * `mode: 'build'`, dev-origin-based for `mode: 'dev'`. Throws with an
109
+ * actionable message when the entry isn't in the build manifest — almost
110
+ * always means the caller forgot to also add it to
111
+ * `build.rollupOptions.input`. */
112
+ function resolveAssetUrl(
113
+ ctx: AfterEmitContext,
114
+ config: ResolvedConfig,
115
+ devServer: ViteDevServer | undefined,
116
+ entryRelPath: string,
117
+ manifest: Record<string, { file: string }> | undefined,
118
+ ): string {
119
+ const absPath = resolve(ctx.projectDir, entryRelPath)
120
+ if (ctx.mode === 'dev') {
121
+ if (!devServer) throw new Error(`[hono/vite] asset "${entryRelPath}": dev server not ready`)
122
+ return devModuleUrl(config, resolveDevOrigin(devServer), absPath)
123
+ }
124
+
125
+ const manifestKey = toPosixRelative(config.root, absPath)
126
+ const [url] = resolveScriptAssets(manifest ?? {}, manifestKey, config.base)
127
+ if (!url) {
128
+ throw new Error(
129
+ `[hono/vite] asset "${entryRelPath}" was not found in the build manifest. ` +
130
+ `Did you also add it to build.rollupOptions.input?`,
131
+ )
132
+ }
133
+ return url
134
+ }
135
+
136
+ /** Builds and write-if-changed's the generated TS asset map. No-op when
137
+ * `assets` is empty. */
138
+ async function writeAssetMap(
139
+ ctx: AfterEmitContext,
140
+ config: ResolvedConfig,
141
+ devServer: ViteDevServer | undefined,
142
+ assets: Record<string, string>,
143
+ assetsOutputFile: string,
144
+ ): Promise<void> {
145
+ const keys = Object.keys(assets)
146
+ if (keys.length === 0) return
147
+
148
+ const manifest = ctx.mode === 'build' ? await loadManifest(ctx.outDir, config.build.manifest) : undefined
149
+
150
+ const entries = keys
151
+ .map(name => ` ${JSON.stringify(name)}: ${JSON.stringify(resolveAssetUrl(ctx, config, devServer, assets[name]!, manifest))},`)
152
+ .join('\n')
153
+
154
+ const content = [
155
+ `// Code generated by BarefootJS. DO NOT EDIT.`,
156
+ '',
157
+ `/**`,
158
+ ` * Maps a logical asset name (this map's key) to its resolved URL for`,
159
+ ` * the current build: a Vite dev-server origin URL in dev, a`,
160
+ ` * content-hashed manifest path in production. Regenerated by`,
161
+ ` * @barefootjs/hono/vite's afterEmit hook every time templates are`,
162
+ ` * (re)emitted.`,
163
+ ` */`,
164
+ `export const Assets: Record<string, string> = {`,
165
+ entries,
166
+ `}`,
167
+ ].join('\n') + '\n'
168
+
169
+ await writeIfChanged(resolve(ctx.projectDir, assetsOutputFile), content, assetsOutputFile)
170
+ }
171
+
172
+ export function barefoot(options: HonoViteOptions): Plugin[] {
173
+ const assets = options.assets ?? {}
174
+ const assetsOutputFile = options.assetsOutputFile ?? 'dist/bf-assets.ts'
175
+
176
+ // Populated by `honoAssetsConfigCapture` below (only added to the
177
+ // returned array when `assets` is non-empty). Read by `afterEmit` — see
178
+ // `@barefootjs/go-template/vite`'s identical mechanism for the ordering
179
+ // guarantee (`configResolved`/`configureServer` always run before the
180
+ // eager pass that triggers `afterEmit`, for both build and dev).
181
+ let resolvedConfig: ResolvedConfig | undefined
182
+ let devServer: ViteDevServer | undefined
183
+
184
+ const core = coreBarefoot({
185
+ adapter: new HonoAdapter(options.adapterOptions),
186
+ components: options.components,
187
+ templates: options.templates,
188
+ async afterEmit(ctx) {
189
+ if (Object.keys(assets).length > 0 && resolvedConfig) {
190
+ await writeAssetMap(ctx, resolvedConfig, devServer, assets, assetsOutputFile)
191
+ }
192
+ },
193
+ })
194
+
195
+ if (Object.keys(assets).length === 0) return [core]
196
+
197
+ const honoAssetsConfigCapture: Plugin = {
198
+ name: 'barefoot-hono-assets-config-capture',
199
+ configResolved(config) {
200
+ resolvedConfig = config
201
+ },
202
+ configureServer(server) {
203
+ devServer = server
204
+ },
205
+ }
206
+
207
+ return [core, honoAssetsConfigCapture]
208
+ }
209
+
210
+ export { barefoot as default }
package/dist/build.d.ts DELETED
@@ -1,65 +0,0 @@
1
- import type { BuildOptions } from '@barefootjs/jsx';
2
- import { HonoAdapter } from './adapter/index.ts';
3
- import type { HonoAdapterOptions } from './adapter/index.ts';
4
- export interface HonoBuildOptions extends BuildOptions {
5
- /** Inject Hono script collection wrapper (default: true) */
6
- scriptCollection?: boolean;
7
- /** Base path for client JS script URLs (default: '/static/components/') */
8
- scriptBasePath?: string;
9
- /** Adapter-specific options passed to HonoAdapter */
10
- adapterOptions?: HonoAdapterOptions;
11
- }
12
- /**
13
- * Create a BarefootBuildConfig for Hono projects.
14
- *
15
- * Uses structural typing — does not import BarefootBuildConfig to avoid
16
- * circular dependency between @barefootjs/hono and @barefootjs/cli.
17
- */
18
- export declare function createConfig(options?: HonoBuildOptions): {
19
- adapter: HonoAdapter;
20
- paths: import("@barefootjs/jsx").BarefootPaths | undefined;
21
- components: string[] | undefined;
22
- outDir: string | undefined;
23
- minify: boolean | undefined;
24
- contentHash: boolean | undefined;
25
- externals: Record<string, import("@barefootjs/jsx").ExternalSpec> | undefined;
26
- externalsBasePath: string | undefined;
27
- bundleEntries: import("@barefootjs/jsx").BundleEntry[] | undefined;
28
- localImportPrefixes: string[] | undefined;
29
- transformMarkedTemplate: ((content: string, componentId: string, clientJsPath: string) => string) | undefined;
30
- };
31
- /**
32
- * Add Hono script collection wrapper to an SSR marked template.
33
- * Injects imports, a helper function, and script collector into each
34
- * exported component function.
35
- */
36
- export declare function addScriptCollection(content: string, componentId: string, clientJsPath: string, scriptBasePath?: string): string;
37
- /**
38
- * Replace comment contents with spaces (preserving length and newlines
39
- * so indices computed against the masked text are valid in the
40
- * original). Used by `addScriptCollection` so its `function Foo(`
41
- * regex ignores JSDoc / inline comments — a docstring example like
42
- * `function MyNode(this: HTMLElement, props)` previously masqueraded
43
- * as a real function declaration (#1236).
44
- *
45
- * Handles `//` line comments and `/* ... *\/` block comments (incl.
46
- * JSDoc). String literals are intentionally NOT masked: JSX text
47
- * content routinely contains unbalanced apostrophes (`How's`) that a
48
- * string-aware masker would misread as an open quote, blanking the
49
- * rest of the file and hiding later function declarations.
50
- *
51
- * Strings inside comments are handled implicitly: the whole comment
52
- * (including any quotes it contains) is blanked.
53
- *
54
- * **Known limitation**: this function does NOT track string
55
- * boundaries, so a `//` or `/*` appearing INSIDE a string literal is
56
- * still treated as a comment delimiter. Example: in
57
- * `const u = "https://x.y" ; export function Foo() {}` the `//` in
58
- * `https://` is misread as a line comment and the rest of the line is
59
- * blanked — a `function Foo()` on that same line would be hidden from
60
- * the regex. SSR template output (the only caller) does not embed
61
- * such cases in practice. If a future caller can produce them, swap
62
- * in a real lexer rather than extending this helper.
63
- */
64
- export declare function maskComments(s: string): string;
65
- //# sourceMappingURL=build.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAE5D,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qDAAqD;IACrD,cAAc,CAAC,EAAE,kBAAkB,CAAA;CACpC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,gBAAqB;IAIvD,OAAO;IACP,KAAK;IACL,UAAU;IACV,MAAM;IACN,MAAM;IACN,WAAW;IACX,SAAS;IACT,iBAAiB;IACjB,aAAa;IACb,mBAAmB;IACnB,uBAAuB,aACT,MAAM,eAAe,MAAM,gBAAgB,MAAM;EAIlE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,GAAE,MAA8B,GAAG,MAAM,CAiItJ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAwB9C"}