[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add json-ld website schema

+133
+4
app/app.vue
··· 15 15 }, 16 16 }) 17 17 18 + if (import.meta.server) { 19 + setJsonLd(createWebSiteSchema()) 20 + } 21 + 18 22 // Global keyboard shortcut: "/" focuses search or navigates to search page 19 23 function handleGlobalKeydown(e: KeyboardEvent) { 20 24 const target = e.target as HTMLElement
+129
app/composables/useJsonLd.ts
··· 1 + // JSON-LD Schema Types 2 + interface JsonLdBase { 3 + '@context': 'https://schema.org' 4 + '@type': string 5 + } 6 + 7 + interface WebSiteSchema extends JsonLdBase { 8 + '@type': 'WebSite' 9 + 'name': string 10 + 'url': string 11 + 'description'?: string 12 + 'potentialAction'?: SearchActionSchema 13 + } 14 + 15 + interface SearchActionSchema { 16 + '@type': 'SearchAction' 17 + 'target': { 18 + '@type': 'EntryPoint' 19 + 'urlTemplate': string 20 + } 21 + 'query-input': string 22 + } 23 + 24 + interface SoftwareApplicationSchema extends JsonLdBase { 25 + '@type': 'SoftwareApplication' 26 + 'name': string 27 + 'description'?: string 28 + 'applicationCategory': 'DeveloperApplication' 29 + 'operatingSystem': 'Cross-platform' 30 + 'url': string 31 + 'softwareVersion'?: string 32 + 'dateModified'?: string 33 + 'datePublished'?: string 34 + 'license'?: string 35 + 'author'?: PersonSchema | OrganizationSchema | (PersonSchema | OrganizationSchema)[] 36 + 'maintainer'?: PersonSchema | OrganizationSchema | (PersonSchema | OrganizationSchema)[] 37 + 'offers'?: OfferSchema 38 + 'downloadUrl'?: string 39 + 'codeRepository'?: string 40 + 'keywords'?: string[] 41 + } 42 + 43 + interface PersonSchema extends JsonLdBase { 44 + '@type': 'Person' 45 + 'name': string 46 + 'url'?: string 47 + } 48 + 49 + interface OrganizationSchema extends JsonLdBase { 50 + '@type': 'Organization' 51 + 'name': string 52 + 'url'?: string 53 + 'logo'?: string 54 + 'description'?: string 55 + 'sameAs'?: string[] 56 + } 57 + 58 + interface OfferSchema { 59 + '@type': 'Offer' 60 + 'price': string 61 + 'priceCurrency': string 62 + } 63 + 64 + interface BreadcrumbListSchema extends JsonLdBase { 65 + '@type': 'BreadcrumbList' 66 + 'itemListElement': BreadcrumbItemSchema[] 67 + } 68 + 69 + interface BreadcrumbItemSchema { 70 + '@type': 'ListItem' 71 + 'position': number 72 + 'name': string 73 + 'item'?: string 74 + } 75 + 76 + interface ProfilePageSchema extends JsonLdBase { 77 + '@type': 'ProfilePage' 78 + 'name': string 79 + 'url': string 80 + 'mainEntity': PersonSchema | OrganizationSchema 81 + } 82 + 83 + type JsonLdSchema = 84 + | WebSiteSchema 85 + | SoftwareApplicationSchema 86 + | PersonSchema 87 + | OrganizationSchema 88 + | BreadcrumbListSchema 89 + | ProfilePageSchema 90 + 91 + /** 92 + * Inject JSON-LD script into head 93 + */ 94 + export function setJsonLd(schema: JsonLdSchema | JsonLdSchema[]) { 95 + const schemas = Array.isArray(schema) ? schema : [schema] 96 + 97 + useHead({ 98 + script: schemas.map((s, i) => ({ 99 + type: 'application/ld+json', 100 + innerHTML: JSON.stringify(s), 101 + key: `json-ld-${i}`, 102 + })), 103 + }) 104 + } 105 + 106 + /** 107 + * Create WebSite schema with search action 108 + */ 109 + export function createWebSiteSchema(options?: { 110 + name?: string 111 + description?: string 112 + }): WebSiteSchema { 113 + const siteUrl = 'https://npmx.dev' 114 + return { 115 + '@context': 'https://schema.org', 116 + '@type': 'WebSite', 117 + 'name': options?.name ?? 'npmx', 118 + 'url': siteUrl, 119 + 'description': options?.description ?? 'A fast, modern browser for the npm registry', 120 + 'potentialAction': { 121 + '@type': 'SearchAction', 122 + 'target': { 123 + '@type': 'EntryPoint', 124 + 'urlTemplate': `${siteUrl}/search?q={search_term_string}`, 125 + }, 126 + 'query-input': 'required name=search_term_string', 127 + }, 128 + } 129 + }