Skip to content

Commit e66a876

Browse files
authored
feat: Support subpath deployment (#252)
* test: subpath deployment * feat: support subpath deployment * feat: support base value without trailing slash
1 parent aed14ea commit e66a876

File tree

5 files changed

+150
-2
lines changed

5 files changed

+150
-2
lines changed

src/server/components/link.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { FC } from 'hono/jsx'
22
import type { JSX } from 'hono/jsx/jsx-runtime'
33
import type { Manifest } from 'vite'
4+
import { ensureTrailngSlash } from '../utils/path'
45

56
type Options = { manifest?: Manifest; prod?: boolean } & JSX.IntrinsicElements['link']
67

@@ -23,7 +24,12 @@ export const Link: FC<Options> = (options) => {
2324
const assetInManifest = manifest[href.replace(/^\//, '')]
2425
if (assetInManifest) {
2526
if (href.startsWith('/')) {
26-
return <link href={`/${assetInManifest.file}`} {...rest}></link>
27+
return (
28+
<link
29+
href={`${ensureTrailngSlash(import.meta.env.BASE_URL)}${assetInManifest.file}`}
30+
{...rest}
31+
></link>
32+
)
2733
}
2834

2935
return <link href={assetInManifest.file} {...rest}></link>

src/server/components/script.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Manifest } from 'vite'
2+
import { ensureTrailngSlash } from '../utils/path.js'
23
import { HasIslands } from './has-islands.js'
34

45
type Options = {
@@ -33,7 +34,7 @@ export const Script = (options: Options): any => {
3334
<script
3435
type='module'
3536
async={!!options.async}
36-
src={`/${scriptInManifest.file}`}
37+
src={`${ensureTrailngSlash(import.meta.env.BASE_URL)}${scriptInManifest.file}`}
3738
nonce={options.nonce}
3839
></script>
3940
</HasIslands>

src/server/utils/path.test.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ensureTrailngSlash } from './path'
2+
3+
describe('ensureTrailngSlash', () => {
4+
it('Should ensure trailing slash', () => {
5+
expect(ensureTrailngSlash('./')).toBe('./')
6+
expect(ensureTrailngSlash('/')).toBe('/')
7+
expect(ensureTrailngSlash('/subdir')).toBe('/subdir/')
8+
expect(ensureTrailngSlash('/subdir/')).toBe('/subdir/')
9+
expect(ensureTrailngSlash('https://example.com')).toBe('https://example.com/')
10+
expect(ensureTrailngSlash('https://example.com/subdir')).toBe('https://example.com/subdir/')
11+
})
12+
})

src/server/utils/path.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const ensureTrailngSlash = (path: string) => {
2+
return path.endsWith('/') ? path : path + '/'
3+
}

test-integration/apps.test.ts

+126
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,69 @@ describe('<Script /> component', () => {
489489
'<html><head><script type="module" src="/static/client-abc.js"></script></head><body><main><honox-island component-name="/mocks/app-script/islands/Component.tsx" data-serialized-props="{}"><p>Component</p></honox-island></main></body></html>'
490490
)
491491
})
492+
493+
describe('with base path - root relative', () => {
494+
const originalBaseURL = import.meta.env.BASE_URL
495+
496+
beforeAll(() => {
497+
// this means `base: "/base/path/"` in vite.config.ts
498+
import.meta.env.BASE_URL = '/base/path/'
499+
})
500+
501+
afterAll(() => {
502+
import.meta.env.BASE_URL = originalBaseURL
503+
})
504+
505+
it('Should convert the script path correctly', async () => {
506+
const res = await app.request('/')
507+
expect(res.status).toBe(200)
508+
expect(await res.text()).toBe(
509+
'<html><head><script type="module" src="/base/path/static/client-abc.js"></script></head><body><main><honox-island component-name="/mocks/app-script/islands/Component.tsx" data-serialized-props="{}"><p>Component</p></honox-island></main></body></html>'
510+
)
511+
})
512+
})
513+
514+
describe('with base path - root relative, without trailing slash', () => {
515+
const originalBaseURL = import.meta.env.BASE_URL
516+
517+
beforeAll(() => {
518+
// this means `base: "/base/path"` in vite.config.ts
519+
import.meta.env.BASE_URL = '/base/path'
520+
})
521+
522+
afterAll(() => {
523+
import.meta.env.BASE_URL = originalBaseURL
524+
})
525+
526+
it('Should convert the script path correctly', async () => {
527+
const res = await app.request('/')
528+
expect(res.status).toBe(200)
529+
expect(await res.text()).toBe(
530+
'<html><head><script type="module" src="/base/path/static/client-abc.js"></script></head><body><main><honox-island component-name="/mocks/app-script/islands/Component.tsx" data-serialized-props="{}"><p>Component</p></honox-island></main></body></html>'
531+
)
532+
})
533+
})
534+
535+
describe('with base path - absolute url', () => {
536+
const originalBaseURL = import.meta.env.BASE_URL
537+
538+
beforeAll(() => {
539+
// this means `base: "https://example.com/base/path/"` in vite.config.ts
540+
import.meta.env.BASE_URL = 'https://example.com/base/path/'
541+
})
542+
543+
afterAll(() => {
544+
import.meta.env.BASE_URL = originalBaseURL
545+
})
546+
547+
it('Should convert the script path correctly', async () => {
548+
const res = await app.request('/')
549+
expect(res.status).toBe(200)
550+
expect(await res.text()).toBe(
551+
'<html><head><script type="module" src="https://example.com/base/path/static/client-abc.js"></script></head><body><main><honox-island component-name="/mocks/app-script/islands/Component.tsx" data-serialized-props="{}"><p>Component</p></honox-island></main></body></html>'
552+
)
553+
})
554+
})
492555
})
493556

494557
describe('With async', () => {
@@ -555,6 +618,69 @@ describe('<Link /> component', () => {
555618
'<html><head><link href="/static/globals-abc.css" rel="stylesheet"></link></head><body><main><div></div></main></body></html>'
556619
)
557620
})
621+
622+
describe('with base path - root relative', () => {
623+
const originalBaseURL = import.meta.env.BASE_URL
624+
625+
beforeAll(() => {
626+
// this means `base: "/base/path/"` in vite.config.ts
627+
import.meta.env.BASE_URL = '/base/path/'
628+
})
629+
630+
afterAll(() => {
631+
import.meta.env.BASE_URL = originalBaseURL
632+
})
633+
634+
it('Should convert the link path correctly', async () => {
635+
const res = await app.request('/')
636+
expect(res.status).toBe(200)
637+
expect(await res.text()).toBe(
638+
'<html><head><link href="/base/path/static/globals-abc.css" rel="stylesheet"></link></head><body><main><div></div></main></body></html>'
639+
)
640+
})
641+
})
642+
643+
describe('with base path - root relative, without trailing slash', () => {
644+
const originalBaseURL = import.meta.env.BASE_URL
645+
646+
beforeAll(() => {
647+
// this means `base: "/base/path"` in vite.config.ts
648+
import.meta.env.BASE_URL = '/base/path'
649+
})
650+
651+
afterAll(() => {
652+
import.meta.env.BASE_URL = originalBaseURL
653+
})
654+
655+
it('Should convert the link path correctly', async () => {
656+
const res = await app.request('/')
657+
expect(res.status).toBe(200)
658+
expect(await res.text()).toBe(
659+
'<html><head><link href="/base/path/static/globals-abc.css" rel="stylesheet"></link></head><body><main><div></div></main></body></html>'
660+
)
661+
})
662+
})
663+
664+
describe('with base path - absolute url', () => {
665+
const originalBaseURL = import.meta.env.BASE_URL
666+
667+
beforeAll(() => {
668+
// this means `base: "https://example.com/base/path/"` in vite.config.ts
669+
import.meta.env.BASE_URL = 'https://example.com/base/path/'
670+
})
671+
672+
afterAll(() => {
673+
import.meta.env.BASE_URL = originalBaseURL
674+
})
675+
676+
it('Should convert the link path correctly', async () => {
677+
const res = await app.request('/')
678+
expect(res.status).toBe(200)
679+
expect(await res.text()).toBe(
680+
'<html><head><link href="https://example.com/base/path/static/globals-abc.css" rel="stylesheet"></link></head><body><main><div></div></main></body></html>'
681+
)
682+
})
683+
})
558684
})
559685
})
560686

0 commit comments

Comments
 (0)