Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
src/helper/ssg/ssg.test.tsx
Outdated
| expect(content).toContain('<a href="/new">Redirecting from') | ||
| }) | ||
|
|
||
| it('should skip generating a redirect HTML when 301/302 has no Location header', async () => { |
There was a problem hiding this comment.
The specification mandates that redirect responses must include a Location header 1. Current implementations simply bypass generating the file if no Location header is present, though options include displaying a warning or throwing an error could be considered.
Footnotes
| disableSSG, | ||
| onlySSG, | ||
| } from './middleware' | ||
| export { redirectPlugin } from './plugins' |
There was a problem hiding this comment.
Since built-in plugins should only handle lightweight ones, I decided to manage multiple plugins in a single plugins.ts file. This should make updating exports slightly more convenient.
src/helper/ssg/plugins.ts
Outdated
| if (res.status === 301 || res.status === 302) { | ||
| const location = res.headers.get('Location') | ||
| if (!location) return false | ||
| const html = generateRedirectHtml('', location) |
There was a problem hiding this comment.
You don't specify the from parameter. Is it expected?
There was a problem hiding this comment.
I forgot to implement it.
The Response object in afterResponseHook does not contain information about the original redirect source, so we probably need to prepare a custom header like x-hono-ssg-route to inject the request path.
I also thought of another option, but this approach might break when running in parallel. (For production use, we would need to use a FIFO queue, but in this PoC I implemented it in a straightforward way.)
export const redirectPlugin = (): SSGPlugin => {
const requestedUrls: string[] = []
return {
beforeRequestHook: (req) => {
requestedUrls.push(new URL(req.url).pathname)
return req
},
afterResponseHook: (res) => {
if (res.status === 301 || res.status === 302) {
const location = res.headers.get('Location')
if (!location) {
return false
}
const from = requestedUrls.shift()
if (!from) {
return false
}
const body = generateRedirectHtml(from, location)
return new Response(body, {
status: 200,
headers: { 'Content-Type': 'text/html; charset=utf-8' },
})
}
return res
},
}
}
There was a problem hiding this comment.
How about removing from from this PR?
At the very least, the user knows where it comes from because they access the URL directly. And I think the information about from is not so important.
src/helper/ssg/plugins.ts
Outdated
|
|
||
| const generateRedirectHtml = (from: string, to: string) => { | ||
| const html = `<!DOCTYPE html> | ||
| <title>Redirecting to: ${to}</title> |
There was a problem hiding this comment.
It's a rare case if passing the script into to, but I think you should escape it. How about html?
import { html } from '../html'
And you can esapce to with ${html`${to}`}
There was a problem hiding this comment.
I refactored the code to use the html helper and added test cases related to escaping. Double escaping might not be necessary.
Also, it could be useful if the html helper had an option to get a minified string. If you cannot think of many use cases for it, it may not be needed.
src/helper/ssg/ssg.test.tsx
Outdated
| app.get('/old', (c) => c.redirect('/new')) | ||
| app.get('/new', (c) => c.html('New Page')) | ||
|
|
||
| await toSSG(app, fsMock, { dir: './static', plugins: [redirectPlugin()] }) |
There was a problem hiding this comment.
What do you think of a situation where you use it with defaultPlugin? It works well if you put the redirecPlugin above the defaultPlugin. So, I think it's good to add a description about it on our website.
There was a problem hiding this comment.
I also think updating the documentation is essential.
Additionally, it might be helpful to add name: string and requires?: string[] to the plugin, so that we can validate the plugin order. What do you think? (I think dependency validation itself is an interesting idea, but we need to carefully discuss the interface and validation methods.)
There was a problem hiding this comment.
I added TSDoc and tests for plugin ordering.
There was a problem hiding this comment.
I think dependency validation itself is an interesting idea, but we need to carefully discuss the interface and validation methods.
Yes. Let's discuss another place.
4c565a9 to
00137c9
Compare
|
Please ping me if it's ready to review. |
|
Sorry for the delayed response. Other tasks besides ESLint rule enforcement are currently unfinished. Once I've completed the fixes and replied to comments, I'll send review request. |
2d4a115 to
7b4556b
Compare
There was a problem hiding this comment.
Build-in plugins test should be separated to plugins.test.tsx?
There was a problem hiding this comment.
The ssg.test.tsx became fat. So separating it is a good idea.
There was a problem hiding this comment.
The default plugin is an object, but the redirect plugin is a function. Should we standardize it to functions for consistency?
There was a problem hiding this comment.
Ahh, I think function is better because the user can pass options like redirectPlugin({ optionA }) if needed.
close #4389
The author should do the following, if applicable
bun run format:fix && bun run lint:fixto format the code