Skip to content

Commit

Permalink
feat: sub-app property inheritence
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Mar 3, 2024
1 parent 839b36c commit 26f4ba8
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-starfishes-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"frog": patch
---

Implemented functionality for sub-apps to inherit properties from the root Frog app when using `.route`.
4 changes: 1 addition & 3 deletions playground/src/routing.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Frog } from 'frog'

export const app = new Frog({
hubApiUrl: 'https://api.hub.wevm.dev',
})
export const app = new Frog()

app.frame('/:name', (c) => {
const name = c.req.param('name')
Expand Down
4 changes: 1 addition & 3 deletions playground/src/todos.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { Button, Frog, TextInput } from 'Frog'
import { Button, Frog, TextInput } from 'frog'

export const app = new Frog<{
index: number
todos: { completed: boolean; name: string }[]
}>({
hubApiUrl: 'https://api.hub.wevm.dev',
initialState: {
index: -1,
todos: [],
},
verify: 'silent',
})

app.frame('/', (c) => {
Expand Down
25 changes: 25 additions & 0 deletions site/pages/concepts/browser-redirects.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,31 @@ app.frame('/foo', (c) => { // [!code hl]
return c.res({/* ... */})
})

app.frame('/bar', (c) => { // [!code hl]
return c.res({/* ... */})
})
```

To include the `basePath` in the `browserLocation` path, you can use the `:basePath` template variable.

In the example below, when a user navigates to the `/api/foo` path of the website via their web browser,
they will be redirected to the `/api/foo/dev` path.

```tsx twoslash
// @noErrors
/** @jsxImportSource frog/jsx */
// ---cut---
import { Button, Frog } from 'frog'

export const app = new Frog({
basePath: '/api', // [!code hl]
browserLocation: '/:basePath/:path/dev', // [!code hl]
})

app.frame('/foo', (c) => { // [!code hl]
return c.res({/* ... */})
})

app.frame('/bar', (c) => { // [!code hl]
return c.res({/* ... */})
})
Expand Down
22 changes: 18 additions & 4 deletions src/frog-base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ export class FrogBase<
> {
// Note: not using native `private` fields to avoid tslib being injected
// into bundled code.
_imageOptions: ImageResponseOptions | undefined
_initialState: state = undefined as state

/** Path for assets. */
Expand All @@ -171,6 +170,7 @@ export class FrogBase<
hono: Hono<env, schema, basePath>
/** Farcaster Hub API URL. */
hubApiUrl: string | undefined
imageOptions: ImageResponseOptions | undefined
fetch: Hono<env, schema, basePath>['fetch']
get: Hono<env, schema, basePath>['get']
post: Hono<env, schema, basePath>['post']
Expand Down Expand Up @@ -199,7 +199,7 @@ export class FrogBase<
if (headers) this.headers = headers
if (dev) this.dev = { enabled: true, ...(dev ?? {}) }
if (hubApiUrl) this.hubApiUrl = hubApiUrl
if (imageOptions) this._imageOptions = imageOptions
if (imageOptions) this.imageOptions = imageOptions
if (secret) this.secret = secret
if (typeof verify !== 'undefined') this.verify = verify

Expand Down Expand Up @@ -262,7 +262,10 @@ export class FrogBase<
// then we will redirect the user to that location.
const browser = detect(c.req.header('user-agent'))

const browserLocation_ = parseBrowserLocation(c, browserLocation, path)
const browserLocation_ = parseBrowserLocation(c, browserLocation, {
basePath: this.basePath,
path,
})
if (browser?.name && browserLocation_)
return c.redirect(
browserLocation_.startsWith('http')
Expand Down Expand Up @@ -413,7 +416,7 @@ export class FrogBase<
const {
image,
headers = this.headers,
imageOptions = this._imageOptions,
imageOptions = this.imageOptions,
} = await handler(context)
if (typeof image === 'string') return c.redirect(image, 302)
return new ImageResponse(image, {
Expand All @@ -429,6 +432,17 @@ export class FrogBase<
subSchema extends Schema,
subBasePath extends string,
>(path: subPath, frog: FrogBase<any, subEnv, subSchema, subBasePath>) {
if (frog.assetsPath === '/') frog.assetsPath = this.assetsPath
if (frog.basePath === '/')
frog.basePath = parsePath(this.basePath) + parsePath(path)
if (!frog.browserLocation) frog.browserLocation = this.browserLocation
if (!frog.dev) frog.dev = this.dev
if (!frog.headers) frog.headers = this.headers
if (!frog.hubApiUrl) frog.hubApiUrl = this.hubApiUrl
if (!frog.imageOptions) frog.imageOptions = this.imageOptions
if (!frog.secret) frog.secret = this.secret
if (!frog.verify) frog.verify = this.verify

return this.hono.route(path, frog.hono)
}
}
7 changes: 6 additions & 1 deletion src/utils/parseBrowserLocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import type { Context } from 'hono'
export function parseBrowserLocation(
c: Context,
location_: string | undefined,
path: string,
{ basePath, path }: { basePath: string; path: string },
) {
let location = location_ || ''
if (location?.includes(':path') && !path.includes(':path'))
location = location.replace(':path', path.replace(/(^\/)|(\/$)/, ''))
else if (location?.includes(':'))
for (const [key, value] of Object.entries(c.req.param() as any))
location = location.replace(`:${key}`, value as string)
if (location.includes(':basePath'))
location = location.replace(
':basePath',
basePath.replace(/(^\/)|(\/$)/, ''),
)
location = location.replace(/^\/\//, '/')
return location
}

0 comments on commit 26f4ba8

Please sign in to comment.