@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/dist/adapter/hono-adapter.d.ts +43 -6
- package/dist/adapter/hono-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +44 -8
- package/dist/app.d.ts +11 -55
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +1 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -8
- package/dist/preload.d.ts +10 -1
- package/dist/preload.d.ts.map +1 -1
- package/dist/render.d.ts +1 -1
- package/dist/scripts.d.ts +76 -0
- package/dist/scripts.d.ts.map +1 -1
- package/dist/scripts.js +81 -17
- package/dist/vite.d.ts +37 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +2749 -0
- package/package.json +19 -11
- package/src/__tests__/scaffold.test.ts +9 -2
- package/src/__tests__/script-assets.test.ts +228 -0
- package/src/__tests__/vite.test.ts +144 -0
- package/src/adapter/hono-adapter.ts +134 -15
- package/src/app.ts +12 -73
- package/src/index.ts +1 -1
- package/src/preload.tsx +15 -2
- package/src/render.ts +1 -1
- package/src/scripts.tsx +136 -0
- package/src/vite.ts +210 -0
- package/dist/build.d.ts +0 -65
- package/dist/build.d.ts.map +0 -1
- package/dist/build.js +0 -188137
- package/dist/dev.d.ts +0 -36
- package/dist/dev.d.ts.map +0 -1
- package/dist/dev.js +0 -508
- package/src/__tests__/build.test.ts +0 -299
- package/src/__tests__/dev.test.tsx +0 -123
- package/src/__tests__/import-map.test.ts +0 -98
- package/src/build.ts +0 -230
- package/src/dev.tsx +0 -154
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/hono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Hono integration for BarefootJS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,10 +38,6 @@
|
|
|
38
38
|
"types": "./dist/preload.d.ts",
|
|
39
39
|
"import": "./dist/preload.js"
|
|
40
40
|
},
|
|
41
|
-
"./dev": {
|
|
42
|
-
"types": "./dist/dev.d.ts",
|
|
43
|
-
"import": "./dist/dev.js"
|
|
44
|
-
},
|
|
45
41
|
"./dev-worker": {
|
|
46
42
|
"types": "./dist/dev-worker.d.ts",
|
|
47
43
|
"import": "./dist/dev-worker.js"
|
|
@@ -65,9 +61,9 @@
|
|
|
65
61
|
"./test-render": {
|
|
66
62
|
"bun": "./src/test-render.ts"
|
|
67
63
|
},
|
|
68
|
-
"./
|
|
69
|
-
"types": "./dist/
|
|
70
|
-
"import": "./dist/
|
|
64
|
+
"./vite": {
|
|
65
|
+
"types": "./dist/vite.d.ts",
|
|
66
|
+
"import": "./dist/vite.js"
|
|
71
67
|
},
|
|
72
68
|
"./app": {
|
|
73
69
|
"types": "./dist/app.d.ts",
|
|
@@ -88,7 +84,7 @@
|
|
|
88
84
|
],
|
|
89
85
|
"scripts": {
|
|
90
86
|
"build": "bun run build:js && bun run build:types",
|
|
91
|
-
"build:js": "bun build ./src/index.ts ./src/adapter/index.ts ./src/scripts.tsx ./src/portals.tsx ./src/portal-ssr.tsx ./src/dialog-context.tsx ./src/client-shim.ts ./src/preload.tsx ./src/dev
|
|
87
|
+
"build:js": "bun build ./src/index.ts ./src/adapter/index.ts ./src/scripts.tsx ./src/portals.tsx ./src/portal-ssr.tsx ./src/dialog-context.tsx ./src/client-shim.ts ./src/preload.tsx ./src/dev-worker.ts ./src/jsx/jsx-runtime/index.ts ./src/jsx/jsx-dev-runtime/index.ts ./src/async.tsx ./src/utils.ts ./src/app.ts ./src/render.ts ./src/request-env.ts --root ./src --outdir ./dist --format esm --external hono --external @barefootjs/client --external @barefootjs/jsx --external @barefootjs/shared --external @barefootjs/vite --external vite && bun build ./src/vite.ts --outfile ./dist/vite.js --format esm --target node --external @barefootjs/vite --external vite --external typescript",
|
|
92
88
|
"build:types": "tsgo --emitDeclarationOnly --outDir ./dist",
|
|
93
89
|
"test": "bun test",
|
|
94
90
|
"clean": "rm -rf dist",
|
|
@@ -112,13 +108,25 @@
|
|
|
112
108
|
"@barefootjs/client": ">=0.2.0",
|
|
113
109
|
"@barefootjs/jsx": ">=0.2.0",
|
|
114
110
|
"@barefootjs/shared": ">=0.2.0",
|
|
115
|
-
"
|
|
111
|
+
"@barefootjs/vite": ">=0.2.0",
|
|
112
|
+
"hono": "^4.0.0",
|
|
113
|
+
"vite": "^6.0.0"
|
|
114
|
+
},
|
|
115
|
+
"peerDependenciesMeta": {
|
|
116
|
+
"@barefootjs/vite": {
|
|
117
|
+
"optional": true
|
|
118
|
+
},
|
|
119
|
+
"vite": {
|
|
120
|
+
"optional": true
|
|
121
|
+
}
|
|
116
122
|
},
|
|
117
123
|
"devDependencies": {
|
|
118
124
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
125
|
+
"@barefootjs/vite": "0.31.0",
|
|
119
126
|
"@types/jsdom": "^27.0.0",
|
|
120
127
|
"hono": "^4.6.0",
|
|
121
128
|
"jsdom": "^27.3.0",
|
|
122
|
-
"typescript": "^5.0.0"
|
|
129
|
+
"typescript": "^5.0.0",
|
|
130
|
+
"vite": "^6.0.0"
|
|
123
131
|
}
|
|
124
132
|
}
|
|
@@ -124,15 +124,22 @@ describe.skipIf(!INTEGRATION)(
|
|
|
124
124
|
expect(wrangler.name).toBe('demo-app')
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
-
test('dev script wires
|
|
127
|
+
test('dev script wires vite dev + unocss + wrangler dev --live-reload', () => {
|
|
128
128
|
const pkg = JSON.parse(
|
|
129
129
|
readFileSync(path.join(projectDir, 'package.json'), 'utf-8'),
|
|
130
130
|
) as { scripts?: Record<string, string> }
|
|
131
|
-
expect(pkg.scripts?.dev).toContain('
|
|
131
|
+
expect(pkg.scripts?.dev).toContain('vite build')
|
|
132
|
+
expect(pkg.scripts?.dev).toContain('vite dev')
|
|
132
133
|
expect(pkg.scripts?.dev).toContain('unocss --watch')
|
|
133
134
|
expect(pkg.scripts?.dev).toContain('wrangler dev --live-reload')
|
|
134
135
|
})
|
|
135
136
|
|
|
137
|
+
test('vite.config.ts composes @barefootjs/hono/vite', () => {
|
|
138
|
+
const cfg = readFileSync(path.join(projectDir, 'vite.config.ts'), 'utf-8')
|
|
139
|
+
expect(cfg).toContain("from '@barefootjs/hono/vite'")
|
|
140
|
+
expect(cfg).toContain("base: '/components/'")
|
|
141
|
+
})
|
|
142
|
+
|
|
136
143
|
test('deploy script targets Cloudflare Workers', () => {
|
|
137
144
|
expect(result.stdout).toContain('Deploy:')
|
|
138
145
|
expect(result.stdout).toMatch(/npm run deploy\s+# deploy to Cloudflare Workers/)
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage of `HonoAdapter.generate()`'s `AdapterGenerateOptions.scriptAssets`
|
|
3
|
+
* handling (see `scripts.tsx`'s `registerComponentScripts`/
|
|
4
|
+
* `wrapWithInlineScripts` docstrings and `hono-adapter.ts`'s `scriptAssets`
|
|
5
|
+
* field docstring for the full design).
|
|
6
|
+
*
|
|
7
|
+
* Mirrors `GoTemplateAdapter`'s own `scriptAssets` contract: `undefined` →
|
|
8
|
+
* no scriptAssets-driven output at all; `[]` → resolved, but nothing to
|
|
9
|
+
* register (server-only file, or a client file whose bundle isn't in the
|
|
10
|
+
* manifest yet) — no dead codegen; non-empty → bake exactly these URLs in
|
|
11
|
+
* via `registerComponentScripts`.
|
|
12
|
+
*/
|
|
13
|
+
import { describe, test, expect } from 'bun:test'
|
|
14
|
+
import { compileJSX } from '@barefootjs/jsx'
|
|
15
|
+
import type { ComponentIR } from '@barefootjs/jsx'
|
|
16
|
+
import { HonoAdapter } from '../adapter'
|
|
17
|
+
|
|
18
|
+
function compileMarkedTemplate(source: string, scriptAssets: string[] | undefined, file = 'Demo.tsx'): string {
|
|
19
|
+
const adapter = new HonoAdapter()
|
|
20
|
+
const result = compileJSX(source, file, { adapter, scriptAssets })
|
|
21
|
+
const errors = result.errors.filter((e) => e.severity === 'error')
|
|
22
|
+
expect(errors).toEqual([])
|
|
23
|
+
const tmpl = result.files.find((f) => f.type === 'markedTemplate')
|
|
24
|
+
expect(tmpl).toBeDefined()
|
|
25
|
+
return tmpl!.content
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const CLIENT_COMPONENT = `'use client'
|
|
29
|
+
import { createSignal } from '@barefootjs/client'
|
|
30
|
+
|
|
31
|
+
export function Counter(props: { initial: number }) {
|
|
32
|
+
const [count, setCount] = createSignal(props.initial)
|
|
33
|
+
return <button onClick={() => setCount(count() + 1)}>{count()}</button>
|
|
34
|
+
}
|
|
35
|
+
`
|
|
36
|
+
|
|
37
|
+
const CLIENT_COMPONENT_IF_ROOT = `'use client'
|
|
38
|
+
import { createSignal } from '@barefootjs/client'
|
|
39
|
+
|
|
40
|
+
export function Toggle(props: { asChild?: boolean }) {
|
|
41
|
+
const [open, setOpen] = createSignal(false)
|
|
42
|
+
|
|
43
|
+
if (props.asChild) {
|
|
44
|
+
return <span onClick={() => setOpen(!open())}>child</span>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return <button onClick={() => setOpen(!open())}>toggle</button>
|
|
48
|
+
}
|
|
49
|
+
`
|
|
50
|
+
|
|
51
|
+
describe('HonoAdapter scriptAssets codegen', () => {
|
|
52
|
+
test('undefined scriptAssets: no scriptAssets-driven codegen at all', () => {
|
|
53
|
+
const output = compileMarkedTemplate(CLIENT_COMPONENT, undefined)
|
|
54
|
+
expect(output).not.toContain('registerComponentScripts')
|
|
55
|
+
expect(output).not.toContain('wrapWithInlineScripts')
|
|
56
|
+
expect(output).not.toContain("@barefootjs/hono/scripts'")
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('empty scriptAssets: resolved-but-empty emits no registration codegen', () => {
|
|
60
|
+
const output = compileMarkedTemplate(CLIENT_COMPONENT, [])
|
|
61
|
+
expect(output).not.toContain('registerComponentScripts')
|
|
62
|
+
expect(output).not.toContain('wrapWithInlineScripts')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('non-empty scriptAssets: bakes registerComponentScripts + wraps the return, no separate runtime script', () => {
|
|
66
|
+
const output = compileMarkedTemplate(CLIENT_COMPONENT, ['/static/build/assets/Counter-abc123.js'])
|
|
67
|
+
|
|
68
|
+
expect(output).toContain("import { registerComponentScripts, wrapWithInlineScripts } from '@barefootjs/hono/scripts'")
|
|
69
|
+
expect(output).toContain('const __bfInlineScripts = registerComponentScripts(["/static/build/assets/Counter-abc123.js"])')
|
|
70
|
+
expect(output).toContain('return wrapWithInlineScripts((')
|
|
71
|
+
expect(output).toContain('), __bfInlineScripts)')
|
|
72
|
+
// No hand-rolled `barefoot.js` registration — the runtime is a shared
|
|
73
|
+
// ESM chunk the resolved entry already imports.
|
|
74
|
+
expect(output).not.toContain('barefoot.js')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('multiple scriptAssets URLs are all baked into one registerComponentScripts call', () => {
|
|
78
|
+
const output = compileMarkedTemplate(CLIENT_COMPONENT, [
|
|
79
|
+
'http://localhost:5173/@vite/client',
|
|
80
|
+
'http://localhost:5173/src/components/Counter.tsx',
|
|
81
|
+
])
|
|
82
|
+
expect(output).toContain(
|
|
83
|
+
'registerComponentScripts(["http://localhost:5173/@vite/client","http://localhost:5173/src/components/Counter.tsx"])',
|
|
84
|
+
)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('if-statement root: both branches wrap their return with wrapWithInlineScripts', () => {
|
|
88
|
+
const output = compileMarkedTemplate(CLIENT_COMPONENT_IF_ROOT, ['/static/build/assets/Toggle-abc123.js'])
|
|
89
|
+
|
|
90
|
+
const wrapOpens = output.match(/return wrapWithInlineScripts\(\(/g) ?? []
|
|
91
|
+
const wrapCloses = output.match(/\), __bfInlineScripts\)/g) ?? []
|
|
92
|
+
// One wrapped return per branch (the `if` consequent and the trailing
|
|
93
|
+
// `return (...)` alternate) — an if-statement root never emits a bare,
|
|
94
|
+
// unwrapped `return (`.
|
|
95
|
+
expect(wrapOpens.length).toBe(2)
|
|
96
|
+
expect(wrapCloses.length).toBe(2)
|
|
97
|
+
// No branch falls back to a bare, unwrapped `return (`.
|
|
98
|
+
expect(output).not.toMatch(/return \(\n/)
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Coverage of `HonoAdapter.generate()`'s `AdapterGenerateOptions.preloadAssets`
|
|
104
|
+
* handling — the sibling option to `scriptAssets` above. See
|
|
105
|
+
* `scripts.tsx`'s `registerComponentPreloads`/`wrapWithInlineScripts`
|
|
106
|
+
* docstrings and `hono-adapter.ts`'s `preloadAssets` field docstring for
|
|
107
|
+
* the full design.
|
|
108
|
+
*
|
|
109
|
+
* Same `undefined`/`[]`/non-empty contract as `scriptAssets`, with the
|
|
110
|
+
* added constraint that `preloadAssets` is only meaningful alongside a
|
|
111
|
+
* non-empty `scriptAssets` — see `AdapterGenerateOptions.preloadAssets`.
|
|
112
|
+
*/
|
|
113
|
+
/** IR for a source, for the tests that drive `generate()` directly rather
|
|
114
|
+
* than through `compileJSX` — `AdapterGenerateOptions`-only fields (e.g.
|
|
115
|
+
* `skipScriptRegistration`) have no `CompileOptions` counterpart to travel
|
|
116
|
+
* through. Mirrors the same helper in every other adapter's suite. */
|
|
117
|
+
function compileToIR(source: string): ComponentIR {
|
|
118
|
+
const result = compileJSX(source.trimStart(), 'test.tsx', {
|
|
119
|
+
adapter: new HonoAdapter(),
|
|
120
|
+
outputIR: true,
|
|
121
|
+
})
|
|
122
|
+
const irFile = result.files.find(f => f.type === 'ir')
|
|
123
|
+
if (!irFile) throw new Error('No IR output')
|
|
124
|
+
return JSON.parse(irFile.content) as ComponentIR
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function compileMarkedTemplateWithPreloads(
|
|
128
|
+
source: string,
|
|
129
|
+
scriptAssets: string[] | undefined,
|
|
130
|
+
preloadAssets: string[] | undefined,
|
|
131
|
+
file = 'Demo.tsx',
|
|
132
|
+
): string {
|
|
133
|
+
const adapter = new HonoAdapter()
|
|
134
|
+
const result = compileJSX(source, file, { adapter, scriptAssets, preloadAssets })
|
|
135
|
+
const errors = result.errors.filter((e) => e.severity === 'error')
|
|
136
|
+
expect(errors).toEqual([])
|
|
137
|
+
const tmpl = result.files.find((f) => f.type === 'markedTemplate')
|
|
138
|
+
expect(tmpl).toBeDefined()
|
|
139
|
+
return tmpl!.content
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
describe('HonoAdapter preloadAssets codegen', () => {
|
|
143
|
+
test('preloadAssets bakes registerComponentPreloads and wraps the return with the preload list, before the script registration', () => {
|
|
144
|
+
const output = compileMarkedTemplateWithPreloads(
|
|
145
|
+
CLIENT_COMPONENT,
|
|
146
|
+
['/static/build/assets/Counter-abc123.js'],
|
|
147
|
+
['/static/build/assets/index-def456.js', '/static/build/assets/TodoItem-ghi789.js'],
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
expect(output).toContain("import { registerComponentScripts, registerComponentPreloads, wrapWithInlineScripts } from '@barefootjs/hono/scripts'")
|
|
151
|
+
expect(output).toContain(
|
|
152
|
+
'const __bfInlinePreloads = registerComponentPreloads(["/static/build/assets/index-def456.js","/static/build/assets/TodoItem-ghi789.js"])',
|
|
153
|
+
)
|
|
154
|
+
const preloadIdx = output.indexOf('registerComponentPreloads(')
|
|
155
|
+
const scriptIdx = output.indexOf('registerComponentScripts(')
|
|
156
|
+
expect(preloadIdx).toBeGreaterThanOrEqual(0)
|
|
157
|
+
expect(scriptIdx).toBeGreaterThan(preloadIdx)
|
|
158
|
+
expect(output).toContain('return wrapWithInlineScripts((')
|
|
159
|
+
expect(output).toContain('), __bfInlineScripts, __bfInlinePreloads)')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('preloadAssets: [] emits no registerComponentPreloads codegen', () => {
|
|
163
|
+
const output = compileMarkedTemplateWithPreloads(
|
|
164
|
+
CLIENT_COMPONENT,
|
|
165
|
+
['/static/build/assets/Counter-abc123.js'],
|
|
166
|
+
[],
|
|
167
|
+
)
|
|
168
|
+
expect(output).not.toContain('registerComponentPreloads')
|
|
169
|
+
expect(output).not.toContain('__bfInlinePreloads')
|
|
170
|
+
// scriptAssets codegen is unaffected — same wrap shape as before.
|
|
171
|
+
expect(output).toContain('), __bfInlineScripts)')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('preloadAssets: undefined emits no registerComponentPreloads codegen', () => {
|
|
175
|
+
const output = compileMarkedTemplateWithPreloads(
|
|
176
|
+
CLIENT_COMPONENT,
|
|
177
|
+
['/static/build/assets/Counter-abc123.js'],
|
|
178
|
+
undefined,
|
|
179
|
+
)
|
|
180
|
+
expect(output).not.toContain('registerComponentPreloads')
|
|
181
|
+
expect(output).not.toContain('__bfInlinePreloads')
|
|
182
|
+
expect(output).toContain('), __bfInlineScripts)')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
test('preloadAssets is ignored when scriptAssets is empty (only meaningful alongside a non-empty scriptAssets)', () => {
|
|
186
|
+
const output = compileMarkedTemplateWithPreloads(CLIENT_COMPONENT, [], ['/static/build/assets/index-def456.js'])
|
|
187
|
+
expect(output).not.toContain('registerComponentPreloads')
|
|
188
|
+
expect(output).not.toContain('registerComponentScripts')
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
// `skipScriptRegistration` lives on `AdapterGenerateOptions`, NOT on
|
|
192
|
+
// `CompileOptions` — a parent adapter sets it when emitting a child, so it
|
|
193
|
+
// never travels through `compileJSX`. Exercising it therefore means calling
|
|
194
|
+
// `generate()` directly, the same way every other adapter's suite does.
|
|
195
|
+
//
|
|
196
|
+
// Note what actually enforces this: `hasPreloadAssets()` is conjoined with
|
|
197
|
+
// `hasScriptAssets()`, so suppressing scripts suppresses preloads
|
|
198
|
+
// structurally — mutating the `skipScriptRegistration` branch alone cannot
|
|
199
|
+
// make this test fail. The independent guarantee is pinned by the
|
|
200
|
+
// "ignored when scriptAssets is empty" test above, which DOES go red when
|
|
201
|
+
// that conjunct is dropped. Both are kept: this one states the intent a
|
|
202
|
+
// reader looks for, that one holds the line.
|
|
203
|
+
test('skipScriptRegistration suppresses the preload registration as well as the script registration', () => {
|
|
204
|
+
const ir = compileToIR(CLIENT_COMPONENT)
|
|
205
|
+
const { template } = new HonoAdapter().generate(ir, {
|
|
206
|
+
skipScriptRegistration: true,
|
|
207
|
+
scriptAssets: ['/static/build/assets/Counter-abc123.js'],
|
|
208
|
+
preloadAssets: ['/static/build/assets/index-def456.js'],
|
|
209
|
+
})
|
|
210
|
+
expect(template).not.toContain('registerComponentPreloads')
|
|
211
|
+
expect(template).not.toContain('registerComponentScripts')
|
|
212
|
+
expect(template).not.toContain('wrapWithInlineScripts')
|
|
213
|
+
expect(template).not.toContain("@barefootjs/hono/scripts'")
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test('if-statement root: both branches wrap with wrapWithInlineScripts passing __bfInlinePreloads', () => {
|
|
217
|
+
const output = compileMarkedTemplateWithPreloads(
|
|
218
|
+
CLIENT_COMPONENT_IF_ROOT,
|
|
219
|
+
['/static/build/assets/Toggle-abc123.js'],
|
|
220
|
+
['/static/build/assets/index-def456.js'],
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
const wrapOpens = output.match(/return wrapWithInlineScripts\(\(/g) ?? []
|
|
224
|
+
const wrapCloses = output.match(/\), __bfInlineScripts, __bfInlinePreloads\)/g) ?? []
|
|
225
|
+
expect(wrapOpens.length).toBe(2)
|
|
226
|
+
expect(wrapCloses.length).toBe(2)
|
|
227
|
+
})
|
|
228
|
+
})
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage of `@barefootjs/hono/vite`'s `barefoot()`:
|
|
3
|
+
*
|
|
4
|
+
* - one real `vite build()` end to end (mirrors `@barefootjs/go-template/
|
|
5
|
+
* vite`'s own `vite.test.ts` rigor — a plugin that only passes mocked
|
|
6
|
+
* unit tests hasn't been shown to work) against a checked-in fixture
|
|
7
|
+
* (not a system tmpdir) so `@barefootjs/client` resolves through the
|
|
8
|
+
* monorepo's real node_modules symlinks;
|
|
9
|
+
* - the `assets` → generated `bf-assets.ts` behavior, exercised the same
|
|
10
|
+
* way (a real build, since it needs the real manifest Vite writes).
|
|
11
|
+
*
|
|
12
|
+
* Unlike Go, there is no `afterEmit`-driven combination step to test here
|
|
13
|
+
* (see `vite.ts`'s module docstring) — Hono's SSR template is
|
|
14
|
+
* self-contained per component, so `barefoot()` always returns a
|
|
15
|
+
* single-element array unless `assets` is set.
|
|
16
|
+
*/
|
|
17
|
+
import { describe, test, expect } from 'bun:test'
|
|
18
|
+
import { build } from 'vite'
|
|
19
|
+
import { mkdtemp, rm, readFile } from 'node:fs/promises'
|
|
20
|
+
import { tmpdir } from 'node:os'
|
|
21
|
+
import { join, resolve } from 'node:path'
|
|
22
|
+
import { barefoot, barefoot as defaultBarefoot } from '../vite.ts'
|
|
23
|
+
|
|
24
|
+
const FIXTURE_ROOT = resolve(import.meta.dirname, '../../e2e-fixture')
|
|
25
|
+
|
|
26
|
+
describe('@barefootjs/hono/vite: real vite build', () => {
|
|
27
|
+
test('exports the same function as both named `barefoot` and default', () => {
|
|
28
|
+
expect(defaultBarefoot).toBe(barefoot)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('returns a single-element plugin array when `assets` is omitted', () => {
|
|
32
|
+
const plugins = barefoot({ components: ['src/components'], templates: 'views' })
|
|
33
|
+
expect(plugins).toHaveLength(1)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// core's `barefoot()` attaches `api.options` (see `@barefootjs/vite`'s
|
|
37
|
+
// `BarefootPluginApi`) to the SAME plugin object this wrapper returns
|
|
38
|
+
// unchanged as `plugins[0]` — this pins that composition doesn't lose it,
|
|
39
|
+
// for either array shape (`assets` omitted vs. present, which adds a
|
|
40
|
+
// SECOND, unrelated companion plugin).
|
|
41
|
+
test('surfaces core\'s plugin.api.options unchanged on the returned plugin, with the HonoAdapter it constructed', () => {
|
|
42
|
+
const plugins = barefoot({ components: ['src/components', '../shared/blog'], templates: 'views' }) as any[]
|
|
43
|
+
const core = plugins[0]
|
|
44
|
+
expect(core.name).toBe('barefoot')
|
|
45
|
+
expect(core.api.options.components).toEqual(['src/components', '../shared/blog'])
|
|
46
|
+
expect(core.api.options.templates).toBe('views')
|
|
47
|
+
expect(core.api.options.adapter.constructor.name).toBe('HonoAdapter')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('writes a self-contained SSR template with scriptAssets baked in, same as core alone would do', async () => {
|
|
51
|
+
const outDir = await mkdtemp(join(tmpdir(), 'barefoot-hono-vite-dist-'))
|
|
52
|
+
const templatesDir = await mkdtemp(join(tmpdir(), 'barefoot-hono-vite-views-'))
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
await build({
|
|
56
|
+
configFile: false,
|
|
57
|
+
root: FIXTURE_ROOT,
|
|
58
|
+
base: '/static/build/',
|
|
59
|
+
logLevel: 'warn',
|
|
60
|
+
build: { outDir, emptyOutDir: true },
|
|
61
|
+
plugins: barefoot({
|
|
62
|
+
components: ['src/components'],
|
|
63
|
+
templates: templatesDir,
|
|
64
|
+
}),
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const template = await readFile(join(templatesDir, 'Counter.tsx'), 'utf8')
|
|
68
|
+
// The scriptAssets-driven codegen path (see hono-adapter.ts).
|
|
69
|
+
expect(template).toContain('registerComponentScripts(')
|
|
70
|
+
expect(template).toContain("from '@barefootjs/hono/scripts'")
|
|
71
|
+
// No separate runtime registration — the shared `@barefootjs/client`
|
|
72
|
+
// chunk arrives as an ESM import the bundled entry already makes.
|
|
73
|
+
expect(template).not.toContain('barefoot.js')
|
|
74
|
+
} finally {
|
|
75
|
+
await rm(outDir, { recursive: true, force: true })
|
|
76
|
+
await rm(templatesDir, { recursive: true, force: true })
|
|
77
|
+
}
|
|
78
|
+
}, 60_000)
|
|
79
|
+
|
|
80
|
+
test('`assets` resolves a non-component entry\'s manifest-hashed URL into a generated `Assets` TS map', async () => {
|
|
81
|
+
const outDir = await mkdtemp(join(tmpdir(), 'barefoot-hono-vite-dist-assets-'))
|
|
82
|
+
const templatesDir = await mkdtemp(join(tmpdir(), 'barefoot-hono-vite-views-assets-'))
|
|
83
|
+
const assetsPath = join(FIXTURE_ROOT, 'dist/bf-assets.ts')
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await build({
|
|
87
|
+
configFile: false,
|
|
88
|
+
root: FIXTURE_ROOT,
|
|
89
|
+
base: '/static/build/',
|
|
90
|
+
logLevel: 'warn',
|
|
91
|
+
build: {
|
|
92
|
+
outDir,
|
|
93
|
+
emptyOutDir: true,
|
|
94
|
+
// Registering the non-component entry is the CALLER's job (stock
|
|
95
|
+
// Vite config) — `assets` below only resolves the URL Vite
|
|
96
|
+
// already bundled it to, it doesn't request the bundling.
|
|
97
|
+
rollupOptions: { input: { bootstrap: resolve(FIXTURE_ROOT, 'client/bootstrap.ts') } },
|
|
98
|
+
},
|
|
99
|
+
plugins: barefoot({
|
|
100
|
+
components: ['src/components'],
|
|
101
|
+
templates: templatesDir,
|
|
102
|
+
assets: { Bootstrap: 'client/bootstrap.ts' },
|
|
103
|
+
}),
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const manifest = JSON.parse(await readFile(join(outDir, '.vite/manifest.json'), 'utf8'))
|
|
107
|
+
const expectedUrl = `/static/build/${manifest['client/bootstrap.ts'].file}`
|
|
108
|
+
|
|
109
|
+
const content = await readFile(assetsPath, 'utf8')
|
|
110
|
+
expect(content).toContain('export const Assets: Record<string, string>')
|
|
111
|
+
expect(content).toContain(`"Bootstrap": ${JSON.stringify(expectedUrl)}`)
|
|
112
|
+
} finally {
|
|
113
|
+
await rm(outDir, { recursive: true, force: true })
|
|
114
|
+
await rm(templatesDir, { recursive: true, force: true })
|
|
115
|
+
await rm(join(FIXTURE_ROOT, 'dist'), { recursive: true, force: true })
|
|
116
|
+
}
|
|
117
|
+
}, 60_000)
|
|
118
|
+
|
|
119
|
+
test('`assets` throws an actionable error when the entry was never registered as a Rollup input', async () => {
|
|
120
|
+
const outDir = await mkdtemp(join(tmpdir(), 'barefoot-hono-vite-dist-assets-missing-'))
|
|
121
|
+
const templatesDir = await mkdtemp(join(tmpdir(), 'barefoot-hono-vite-views-assets-missing-'))
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
await expect(
|
|
125
|
+
build({
|
|
126
|
+
configFile: false,
|
|
127
|
+
root: FIXTURE_ROOT,
|
|
128
|
+
base: '/static/build/',
|
|
129
|
+
logLevel: 'silent',
|
|
130
|
+
build: { outDir, emptyOutDir: true },
|
|
131
|
+
plugins: barefoot({
|
|
132
|
+
components: ['src/components'],
|
|
133
|
+
templates: templatesDir,
|
|
134
|
+
assets: { Bootstrap: 'client/bootstrap.ts' },
|
|
135
|
+
}),
|
|
136
|
+
}),
|
|
137
|
+
).rejects.toThrow(/was not found in the build manifest/)
|
|
138
|
+
} finally {
|
|
139
|
+
await rm(outDir, { recursive: true, force: true })
|
|
140
|
+
await rm(templatesDir, { recursive: true, force: true })
|
|
141
|
+
await rm(join(FIXTURE_ROOT, 'dist'), { recursive: true, force: true })
|
|
142
|
+
}
|
|
143
|
+
}, 60_000)
|
|
144
|
+
})
|