import { join , resolve } from 'path' ; import { existsSync , readJsonSync } from 'fs-extra' ; import { mergeMeta } from '../lib/meta.utils' ; import type { MetaOptions , PWAContext } from '../types' ; import { isUrl , PKG_DIR } from './utils' ; export function meta ( nuxt , pwa , moduleContainer ) { const defaults = { name : process . env . npm_package_name , author : process . env . npm_package_author_name , description : process . env . npm_package_description , charset : 'utf-8' , viewport : undefined , mobileApp : true , nativeUI : false , favicon : true , mobileAppIOS : undefined , appleStatusBarStyle : undefined , theme_color : undefined , lang : 'en' , ogType : 'website' , ogSiteName : true , ogTitle : true , ogDescription : true , ogImage : true , ogHost : undefined , ogUrl : true , twitterCard : undefined , twitterSite : undefined , twitterCreator : undefined } ; const options = { ... defaults , ... pwa . manifest , ... pwa . meta } ; if ( options . viewport === undefined ) { options . viewport = options . nativeUI ? 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, minimal-ui' : 'width=device-width, initial-scale=1' ; } if ( options . mobileAppIOS === undefined ) { options . mobileAppIOS = ! ! options . nativeUI ; } const head = { title : '' , meta : [ ] , link : [ ] , htmlAttrs : { } as any } ; if ( options . charset ) { head . meta . push ( { hid : 'charset' , charset : options . charset } ) ; } if ( options . viewport ) { head . meta . push ( { hid : 'viewport' , name : 'viewport' , content : options . viewport } ) ; } if ( options . mobileApp ) { head . meta . push ( { hid : 'mobile-web-app-capable' , name : 'mobile-web-app-capable' , content : 'yes' } ) ; } if ( options . mobileAppIOS ) { head . meta . push ( { hid : 'apple-mobile-web-app-capable' , name : 'apple-mobile-web-app-capable' , content : 'yes' } ) ; } if ( options . mobileAppIOS || options . appleStatusBarStyle ) { head . meta . push ( { hid : 'apple-mobile-web-app-status-bar-style' , name : 'apple-mobile-web-app-status-bar-style' , content : options . appleStatusBarStyle || 'default' } ) ; } if ( options . icons && options . icons . length > 0 ) { const iconSmall = options . icons [ 0 ] ; const iconBig = options . icons [ options . icons . length - 1 ] ; if ( options . favicon ) { head . link . push ( { hid : 'shortcut-icon' , rel : 'shortcut icon' , href : iconSmall . src } ) ; head . link . push ( { hid : 'apple-touch-icon' , rel : 'apple-touch-icon' , href : iconBig . src , sizes : iconBig . sizes } ) ; } if ( options . mobileAppIOS && pwa . _iosSplash ) { const splashes = [ [ 'iphonese' , '(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)' ] , [ 'iphone6' , '(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)' ] , [ 'iphoneplus' , '(device-width: 621px) and (device-height: 1104px) and (-webkit-device-pixel-ratio: 3)' ] , [ 'iphonex' , '(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3)' ] , [ 'iphonexr' , '(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2)' ] , [ 'iphonexsmax' , '(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3)' ] , [ 'ipad' , '(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2)' ] , [ 'ipadpro1' , '(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2)' ] , [ 'ipadpro2' , '(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2)' ] , [ 'ipadpro3' , '(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2)' ] ] ; for ( const [ type , media ] of splashes ) { head . link . push ( { href : pwa . _iosSplash [ type ] , media , rel : 'apple-touch-startup-image' , hid : 'apple-touch-startup-image-' + type } ) ; } } } else { const favicon = join ( nuxt . options . srcDir , nuxt . options . dir . static , 'favicon.ico' ) ; if ( existsSync ( favicon ) ) { head . link . push ( { hid : 'shortcut-icon' , rel : 'shortcut icon' , href : nuxt . options . router . base + 'favicon.ico' } ) ; } } const title = options . name || options . title ; if ( title ) { head . title = options . name ; head . meta . push ( { hid : 'apple-mobile-web-app-title' , name : 'apple-mobile-web-app-title' , content : title } ) ; } if ( options . author ) { head . meta . push ( { hid : 'author' , name : 'author' , content : options . author } ) ; } if ( options . description ) { head . meta . push ( { hid : 'description' , name : 'description' , content : options . description } ) ; } if ( options . theme_color ) { head . meta . push ( { hid : 'theme-color' , name : 'theme-color' , content : options . theme_color } ) ; } if ( options . lang ) { head . htmlAttrs . lang = options . lang ; } if ( options . ogType ) { head . meta . push ( { hid : 'og:type' , name : 'og:type' , property : 'og:type' , content : options . ogType } ) ; } if ( options . ogTitle === true ) { options . ogTitle = options . name ; } if ( options . ogTitle ) { head . meta . push ( { hid : 'og:title' , name : 'og:title' , property : 'og:title' , content : options . ogTitle } ) ; } if ( options . ogSiteName === true ) { options . ogSiteName = options . name ; } if ( options . ogSiteName ) { head . meta . push ( { hid : 'og:site_name' , name : 'og:site_name' , property : 'og:site_name' , content : options . ogSiteName } ) ; } if ( options . ogDescription === true ) { options . ogDescription = options . description ; } if ( options . ogDescription ) { head . meta . push ( { hid : 'og:description' , name : 'og:description' , property : 'og:description' , content : options . ogDescription } ) ; } if ( options . ogHost && options . ogUrl === true ) { options . ogUrl = options . ogHost ; } if ( options . ogUrl && options . ogUrl !== true ) { head . meta . push ( { hid : 'og:url' , name : 'og:url' , property : 'og:url' , content : options . ogUrl } ) ; } if ( options . ogImage === true ) { if ( options . icons && options . icons . length > 0 ) { const iconBig = options . icons [ options . icons . length - 1 ] ; const [ width , height ] = iconBig . sizes . split ( 'x' ) . map ( x => parseInt ( x ) ) ; options . ogImage = { path : iconBig . src , width , height , type : iconBig . type } ; } else { options . ogImage = false ; } } else if ( typeof options . ogImage === 'string' ) { options . ogImage = { path : options . ogImage } ; } if ( options . ogImage ) { if ( options . ogHost || isUrl ( options . ogImage . path ) ) { head . meta . push ( { hid : 'og:image' , name : 'og:image' , property : 'og:image' , content : isUrl ( options . ogImage . path ) ? options . ogImage . path : options . ogHost + options . ogImage . path } ) ; if ( options . ogImage . width && options . ogImage . height ) { head . meta . push ( { hid : 'og:image:width' , name : 'og:image:width' , property : 'og:image:width' , content : options . ogImage . width } ) ; head . meta . push ( { hid : 'og:image:height' , name : 'og:image:height' , property : 'og:image:height' , content : options . ogImage . height } ) ; } if ( options . ogImage . type ) { head . meta . push ( { hid : 'og:image:type' , name : 'og:image:type' , property : 'og:image:type' , content : options . ogImage . type } ) ; } } } if ( options . twitterCard ) { head . meta . push ( { hid : 'twitter:card' , name : 'twitter:card' , property : 'twitter:card' , content : options . twitterCard } ) ; } if ( options . twitterSite ) { head . meta . push ( { hid : 'twitter:site' , name : 'twitter:site' , property : 'twitter:site' , content : options . twitterSite } ) ; } if ( options . twitterCreator ) { head . meta . push ( { hid : 'twitter:creator' , name : 'twitter:creator' , property : 'twitter:creator' , content : options . twitterCreator } ) ; } if ( pwa . _manifestMeta ) { head . link . push ( pwa . _manifestMeta ) ; } moduleContainer . addPlugin ( { src : resolve ( PKG_DIR , 'templates/meta.plugin.js' ) , fileName : 'pwa/meta.plugin.js' , options : { } } ) ; moduleContainer . addTemplate ( { src : resolve ( PKG_DIR , 'templates/meta.json' ) , fileName : 'pwa/meta.json' , options : { head } } ) ; moduleContainer . addTemplate ( { src : resolve ( PKG_DIR , 'lib/meta.utils.js' ) , fileName : 'pwa/meta.utils.js' , options : { head } } ) ; metaRuntime ( nuxt ) ; } export function metaRuntime ( nuxt ) { const spaSupport = ( ) => { try { const metaJSON = readJsonSync ( resolve ( nuxt . options . buildDir , 'pwa/meta.json' ) ) ; mergeMeta ( nuxt . options . head , metaJSON ) ; } catch ( _err ) { } } ; nuxt . hook ( 'render:resourcesLoaded' , ( ) => spaSupport ( ) ) ; }