Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/angular/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
export { useHead, useHeadSafe, useScript, useSeoMeta, useUnhead } from './composables'
export { headSymbol, UnheadInjectionToken } from './context'
export { Head } from './head.component'
export { defineLink, defineScript } from 'unhead'
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { hookImports } from './autoImports'
export { Head } from './components'
export { useHead, useHeadSafe, useScript, useSeoMeta, useUnhead } from './composables'
export { defineLink, defineScript } from 'unhead'
1 change: 1 addition & 0 deletions packages/solid-js/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { hookImports } from './autoImports'
export { useHead, useHeadSafe, useScript, useSeoMeta, useUnhead } from './composables'
export { defineLink, defineScript } from 'unhead'
1 change: 1 addition & 0 deletions packages/svelte/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { autoImports } from './autoImports'
export { useHead, useHeadSafe, useScript, useSeoMeta, useUnhead } from './composables'
export { UnheadContextKey } from './context'
export { defineLink, defineScript } from 'unhead'
5 changes: 3 additions & 2 deletions packages/unhead/src/types/schema/link.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { DeepReadonly } from '../util'
import type { DataKeys } from './attributes/data'
import type { HttpEventAttributes } from './attributes/event'
import type { GlobalAttributes } from './attributes/global'
Expand Down Expand Up @@ -847,8 +848,8 @@ type MatchLinkByRel<R>
export type InferLink<T>
= T extends { rel: infer R }
? R extends KnownLinkRel
? MatchLinkByRel<R>
? DeepReadonly<MatchLinkByRel<R>>
: R extends string
? GenericLink & { rel: R }
? DeepReadonly<GenericLink> & { rel: R }
: never
: never
8 changes: 4 additions & 4 deletions packages/unhead/src/types/schema/metaFlat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface MetaFlatArticle {
* Writers of the article.
* @example ['https://example.com/some.html', 'https://example.com/one.html']
*/
articleAuthor?: string[]
articleAuthor?: readonly string[]

/**
* When the article is out of date after.
Expand Down Expand Up @@ -36,15 +36,15 @@ export interface MetaFlatArticle {
* Tag words associated with this article.
* @example ['Apple', 'Steve Jobs']
*/
articleTag?: string[]
articleTag?: readonly string[]
}

export interface MetaFlatBook {
/**
* Who wrote this book.
* @example ['https://example.com/some.html', 'https://example.com/one.html']
*/
bookAuthor?: string[]
bookAuthor?: readonly string[]

/**
* The ISBN.
Expand All @@ -62,7 +62,7 @@ export interface MetaFlatBook {
* Tag words associated with this book.
* @example ['Apple', 'Steve Jobs']
*/
bookTag?: string[]
bookTag?: readonly string[]
}

export interface MetaFlatProfile {
Expand Down
10 changes: 5 additions & 5 deletions packages/unhead/src/types/schema/script.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Booleanable } from '../util'
import type { Booleanable, DeepReadonly } from '../util'
import type { DataKeys } from './attributes/data'
import type { HttpEventAttributes } from './attributes/event'
import type { GlobalAttributes } from './attributes/global'
Expand Down Expand Up @@ -489,7 +489,7 @@ export type InferScript<T>
= T extends { type: infer U }
? U extends string
? U extends KnownScriptType
? MatchScriptByType<U>
: GenericScript & { type: U }
: Script
: Script
? DeepReadonly<MatchScriptByType<U>>
: DeepReadonly<GenericScript> & { type: U }
: DeepReadonly<Script>
: DeepReadonly<Script>
10 changes: 5 additions & 5 deletions packages/unhead/src/types/schema/struct/speculationRules.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { ReferrerPolicy } from '../shared'

export interface SpeculationRules {
prefetch?: (SpeculationRuleList | SpeculationRuleDocument)[]
prerender?: (SpeculationRuleList | SpeculationRuleDocument)[]
prefetch?: readonly (SpeculationRuleList | SpeculationRuleDocument)[]
prerender?: readonly (SpeculationRuleList | SpeculationRuleDocument)[]
}

export interface SpeculationRuleBase {
Expand All @@ -23,7 +23,7 @@ export interface SpeculationRuleBase {
*
* @see https://github.com/WICG/nav-speculation/blob/main/triggers.md#requirements
*/
requires?: 'anonymous-client-ip-when-cross-origin'[]
requires?: readonly 'anonymous-client-ip-when-cross-origin'[]
/**
* Indicating where the page expects the prerendered content to be activated.
*
Expand All @@ -40,11 +40,11 @@ export interface SpeculationRuleBase {

export interface SpeculationRuleList extends SpeculationRuleBase {
source: 'list'
urls: string[]
urls: readonly string[]
}

export type SpeculationRuleFn = 'and' | 'or' | 'href_matches' | 'selector_matches' | 'not'
export type SpeculationRuleWhere = Partial<Record<SpeculationRuleFn, Partial<(Record<SpeculationRuleFn, (Partial<Record<SpeculationRuleFn, string>>) | string>)>[]>>
export type SpeculationRuleWhere = Partial<Record<SpeculationRuleFn, readonly Partial<(Record<SpeculationRuleFn, (Partial<Record<SpeculationRuleFn, string>>) | string>)>[]>>

export interface SpeculationRuleDocument extends SpeculationRuleBase {
source: 'document'
Expand Down
13 changes: 13 additions & 0 deletions packages/unhead/src/types/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ export type ResolvableUnion<T> = T extends string | number | boolean
? DeepResolvableProperties<T>
: ResolvableValue<T>

/**
* Recursively marks all properties and arrays as readonly.
* Applied to `InferScript`/`InferLink` return types so that
* `defineScript`/`defineLink` accept both mutable and `as const` inputs.
*/
export type DeepReadonly<T> = T extends (...a: any[]) => any
? T
: T extends ReadonlyArray<infer U>
? readonly DeepReadonly<U>[]
: T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T

export type DeepResolvableProperties<T> = {
[K in keyof T]?: T[K] extends string | object
? T[K] extends string
Expand Down
137 changes: 137 additions & 0 deletions packages/unhead/test/unit/define.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,141 @@ describe('defineScript', () => {
],
})
})

// Nuxt cross-origin-prefetch: speculationrules with structured innerHTML object
it('accepts speculationrules with object innerHTML without defineScript', () => {
const head = createHead()
useHead(head, {
script: [{
type: 'speculationrules',
key: 'speculationrules',
innerHTML: {
prefetch: [
{
source: 'list',
urls: ['https://example.com'],
requires: ['anonymous-client-ip-when-cross-origin'],
},
],
},
}],
})
})

// Nuxt importmap: structured innerHTML with imports object
it('accepts importmap with object innerHTML without defineScript', () => {
const head = createHead()
useHead(head, {
script: [{
type: 'importmap',
innerHTML: { imports: { '#entry': '/entry.mjs' } },
}],
})
})

// as const speculationrules should work directly with useHead
it('accepts as const speculationrules directly in useHead', () => {
const spec = {
type: 'speculationrules',
innerHTML: {
prerender: [{ source: 'list', urls: ['/next-page'] }],
},
} as const
const head = createHead()
useHead(head, { script: [spec] })
})

// as const importmap should work directly with useHead
it('accepts as const importmap directly in useHead', () => {
const map = {
type: 'importmap',
innerHTML: { imports: { '#entry': '/entry.mjs' } },
} as const
const head = createHead()
useHead(head, { script: [map] })
})

// Nuxt cross-origin-prefetch: helper function returning script for useHead
it('works from a helper function without defineScript', () => {
function generateRules() {
return {
type: 'speculationrules' as const,
key: 'speculationrules',
innerHTML: {
prefetch: [
{
source: 'list' as const,
urls: ['https://a.com', 'https://b.com'],
requires: ['anonymous-client-ip-when-cross-origin'] as const,
},
],
},
}
}
const head = createHead()
useHead(head, { script: [generateRules()] })
})

// defineScript still works for custom types that need it
it('accepts as const objects via defineScript', () => {
const spec = {
type: 'speculationrules',
innerHTML: {
prerender: [{ source: 'list', urls: ['/next-page'] }],
},
} as const
defineScript(spec)
})
})

describe('useHead β€” as const link patterns', () => {
// Nuxt renderer: preload link directly in useHead
it('accepts preload link directly in useHead', () => {
const head = createHead()
useHead(head, {
link: [
{ rel: 'preload', as: 'fetch', crossorigin: 'anonymous', href: '/payload.json' },
],
})
})

// Nuxt renderer: stylesheet with crossorigin directly in useHead
it('accepts stylesheet with crossorigin directly', () => {
const head = createHead()
useHead(head, {
link: [
{ rel: 'stylesheet', href: '/style.css', crossorigin: '' },
],
})
})

// Nuxt renderer: preload as script directly in useHead
it('accepts preload as script directly', () => {
const head = createHead()
useHead(head, {
link: [
{ rel: 'preload', as: 'script', href: '/chunk.js' },
],
})
})

// Nuxt docs: preconnect directly in useHead
it('accepts preconnect directly', () => {
const head = createHead()
useHead(head, {
link: [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
],
})
})

// defineLink only needed for non-standard rels
it('needs defineLink only for non-standard rels', () => {
const head = createHead()
useHead(head, {
link: [
defineLink({ rel: 'openid2.provider', href: 'https://example.com/openid' }),
],
})
})
})
2 changes: 1 addition & 1 deletion packages/vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ export type * from './scripts/index'
export type * from './types'
export { resolveUnrefHeadInput } from './utils'
export { VueHeadMixin } from './VueHeadMixin'
export { createUnhead } from 'unhead'
export { createUnhead, defineLink, defineScript } from 'unhead'
2 changes: 1 addition & 1 deletion packages/vue/src/scripts/useScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface VueScriptInstance<T extends Record<symbol | string, any>> exten
}

export type UseScriptInput = string | (ResolvableProperties<Omit<GenericScript & DataKeys & SchemaAugmentations['script'], 'src'>> & { src: string })
export interface UseScriptOptions<T extends Record<symbol | string, any> = Record<string, any>> extends Omit<HeadEntryOptions, 'head'>, Pick<BaseUseScriptOptions<T>, 'use' | 'eventContext' | 'beforeInit'> {
export interface UseScriptOptions<T extends Record<symbol | string, any> = Record<string, any>> extends Omit<HeadEntryOptions, 'head'>, Partial<Pick<BaseUseScriptOptions<T>, 'use' | 'eventContext' | 'beforeInit'>> {
/**
* The trigger to load the script:
* - `undefined` | `client` - (Default) Load the script on the client when this js is loaded.
Expand Down
2 changes: 2 additions & 0 deletions test/exports/react.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.:
defineLink: function
defineScript: function
Head: function
hookImports: object
useHead: function
Expand Down
2 changes: 2 additions & 0 deletions test/exports/solid-js.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.:
defineLink: function
defineScript: function
hookImports: object
useHead: function
useHeadSafe: function
Expand Down
2 changes: 2 additions & 0 deletions test/exports/svelte.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.:
autoImports: object
defineLink: function
defineScript: function
UnheadContextKey: symbol
useHead: function
useHeadSafe: function
Expand Down
2 changes: 2 additions & 0 deletions test/exports/vue.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.:
createUnhead: function
defineLink: function
defineScript: function
headSymbol: string
injectHead: function
resolveUnrefHeadInput: function
Expand Down
Loading