Skip to content

Commit c6ca138

Browse files
committed
chore: use logic inside the playground
1 parent af00fe9 commit c6ca138

File tree

10 files changed

+491
-58
lines changed

10 files changed

+491
-58
lines changed

Diff for: playground/nuxt.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export default defineNuxtConfig({
2323
handlers: [
2424
{
2525
middleware: true,
26+
// route: '',
2627
handler: '~/server/image',
2728
},
2829
],

Diff for: playground/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
},
1010
"dependencies": {
1111
"nuxt": "^3.13.2",
12-
"nuxt-http-client-hints": "workspace:*"
12+
"nuxt-http-client-hints": "workspace:*",
13+
"sharp": "^0.33.5"
1314
},
1415
"devDependencies": {
1516
"typescript": "^5.6.3",

Diff for: playground/server/dev-image.ts

+38-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,42 @@
1+
import { lstat, readFile } from 'node:fs/promises'
2+
import { resolve } from 'node:path'
13
import { eventHandler } from 'h3'
4+
import { useNitro } from '@nuxt/kit'
5+
import sharp from 'sharp'
6+
import type { HttpClientHintsState } from '../../src/runtime/shared-types/types'
7+
import { extractHTTPClientHints } from './utils/image'
28

39
export default eventHandler(async (event) => {
4-
console.log('eventHandler:', event.path)
5-
console.log('eventHandler:', event.context.httpClientHintsOptions)
6-
console.log('eventHandler:', event.context.httpClientHints)
10+
console.log('dev-image:', event.path)
11+
await extractHTTPClientHints(event)
12+
const httpClientHintsState: HttpClientHintsState | undefined = event.context.httpClientHints
13+
const { widthAvailable = false, width = -1 } = httpClientHintsState?.critical ?? {}
14+
if (widthAvailable && width > -1) {
15+
const image = await convertImage(event.path, width)
16+
if (image) {
17+
console.log('dev-image:Sec-CH-Width:', width)
18+
event.node.res.setHeader('Vary', 'Sec-CH-Width')
19+
event.node.res.end(image)
20+
}
21+
}
722
})
23+
24+
async function convertImage(path: string, width: number) {
25+
if (path.startsWith('/')) {
26+
path = path.slice(1)
27+
}
28+
const nitro = useNitro()
29+
const folders = nitro.options.publicAssets
30+
let image: string
31+
for (const folder of folders) {
32+
try {
33+
console.log(folder.dir)
34+
image = resolve(folder.dir, path)
35+
const stats = await lstat(image)
36+
if (stats.isFile()) {
37+
return sharp(await readFile(image)).resize({ width }).toBuffer()
38+
}
39+
}
40+
catch (_) {}
41+
}
42+
}

Diff for: playground/server/image.ts

+60-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,61 @@
1-
export default eventHandler(async (event) => {
2-
console.log('eventHandler:', event.path)
3-
console.log('eventHandler:', event.context.httpClientHintsOptions)
4-
console.log('eventHandler:', event.context.httpClientHints)
1+
import { useAppConfig } from 'nitropack/runtime'
2+
import { parseUserAgent } from 'detect-browser-es'
3+
import type {
4+
HttpClientHintsState,
5+
ResolvedHttpClientHintsOptions,
6+
ServerHttpClientHintsOptions,
7+
} from '../../src/runtime/shared-types/types'
8+
import { extractBrowser } from '../../src/runtime/utils/detect'
9+
import { extractDeviceHints } from '../../src/runtime/utils/device'
10+
import { extractNetworkHints } from '../../src/runtime/utils/network'
11+
import { extractCriticalHints } from '../../src/runtime/utils/critical'
12+
13+
export default defineEventHandler(async (event) => {
14+
console.log('request', useAppConfig().httpClientHints)
15+
const {
16+
serverImages,
17+
...rest
18+
} = useAppConfig().httpClientHints as ServerHttpClientHintsOptions
19+
const options: ResolvedHttpClientHintsOptions = {
20+
...rest,
21+
serverImages: serverImages.map(r => new RegExp(r)),
22+
}
23+
const critical = !!options.critical
24+
const device = options.device.length > 0
25+
const network = options.network.length > 0
26+
const detect = options.detectOS || options.detectBrowser || options.userAgent.length > 0
27+
28+
try {
29+
// expose the client hints in the context
30+
const url = event.path
31+
console.log('request', { url, match: options.serverImages?.some(r => url.match(r)) })
32+
if (options.serverImages?.some(r => url.match(r))) {
33+
const userAgentHeader = event.headers.get('user-agent')
34+
const requestHeaders: { [key in Lowercase<string>]?: string } = {}
35+
for (const [key, value] of event.headers.entries()) {
36+
requestHeaders[key.toLowerCase() as Lowercase<string>] = value
37+
}
38+
const userAgent = userAgentHeader
39+
? parseUserAgent(userAgentHeader)
40+
: null
41+
const clientHints: HttpClientHintsState = {}
42+
if (detect) {
43+
clientHints.browser = await extractBrowser(options, requestHeaders as Record<string, string>, userAgentHeader ?? undefined)
44+
}
45+
if (device) {
46+
clientHints.device = extractDeviceHints(options, requestHeaders, userAgent)
47+
}
48+
if (network) {
49+
clientHints.network = extractNetworkHints(options, requestHeaders, userAgent)
50+
}
51+
if (critical) {
52+
clientHints.critical = extractCriticalHints(options, requestHeaders, userAgent)
53+
}
54+
event.context.httpClientHintsOptions = options
55+
event.context.httpClientHints = clientHints
56+
}
57+
}
58+
catch (err) {
59+
console.error(err)
60+
}
561
})

Diff for: playground/server/utils/image.ts

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { useAppConfig } from 'nitropack/runtime'
2+
import type { H3Event } from 'h3'
3+
import { parseUserAgent } from 'detect-browser-es'
4+
import { useNitro } from '@nuxt/kit'
5+
import type {
6+
HttpClientHintsState,
7+
ResolvedHttpClientHintsOptions,
8+
ServerHttpClientHintsOptions,
9+
} from '../../../src/runtime/shared-types/types'
10+
import { extractBrowser } from '../../../src/runtime/utils/detect'
11+
import { extractDeviceHints } from '../../../src/runtime/utils/device'
12+
import { extractNetworkHints } from '../../../src/runtime/utils/network'
13+
import { extractCriticalHints } from '../../../src/runtime/utils/critical'
14+
15+
export async function extractHTTPClientHints(event: H3Event) {
16+
const {
17+
serverImages,
18+
...rest
19+
} = useNitro().options.appConfig.httpClientHints as ServerHttpClientHintsOptions
20+
const options: ResolvedHttpClientHintsOptions = {
21+
...rest,
22+
serverImages: serverImages.map(r => new RegExp(r)),
23+
}
24+
const critical = !!options.critical
25+
const device = options.device.length > 0
26+
const network = options.network.length > 0
27+
const detect = options.detectOS || options.detectBrowser || options.userAgent.length > 0
28+
29+
try {
30+
// expose the client hints in the context
31+
const url = event.path
32+
console.log('request', { url, match: options.serverImages?.some(r => url.match(r)) })
33+
if (options.serverImages?.some(r => url.match(r))) {
34+
const userAgentHeader = event.headers.get('user-agent')
35+
const requestHeaders: { [key in Lowercase<string>]?: string } = {}
36+
for (const [key, value] of event.headers.entries()) {
37+
requestHeaders[key.toLowerCase() as Lowercase<string>] = value
38+
}
39+
const userAgent = userAgentHeader
40+
? parseUserAgent(userAgentHeader)
41+
: null
42+
const clientHints: HttpClientHintsState = {}
43+
if (detect) {
44+
clientHints.browser = await extractBrowser(options, requestHeaders as Record<string, string>, userAgentHeader ?? undefined)
45+
}
46+
if (device) {
47+
clientHints.device = extractDeviceHints(options, requestHeaders, userAgent)
48+
}
49+
if (network) {
50+
clientHints.network = extractNetworkHints(options, requestHeaders, userAgent)
51+
}
52+
if (critical) {
53+
clientHints.critical = extractCriticalHints(options, requestHeaders, userAgent)
54+
}
55+
event.context.httpClientHintsOptions = options
56+
event.context.httpClientHints = clientHints
57+
}
58+
}
59+
catch (err) {
60+
console.error(err)
61+
}
62+
}

0 commit comments

Comments
 (0)