import get from 'lodash/get' ; import set from 'lodash/set' ; import isEmpty from 'lodash/isEmpty' ; import each from 'lodash/each' ; import find from 'lodash/find' ; import defaultsDeep from 'lodash/defaultsDeep' ; import cloneDeep from 'lodash/cloneDeep' ; import first from 'lodash/first' ; import isEqual from 'lodash/isEqual' ; import uniqWith from 'lodash/uniqWith' ; import { isBrowser } from './utils' ; import { getTextFromHtml } from '../plugins/html-to-text' ; const defaultMeta = ( pwaSchema ) => [ { charSet : 'utf-8' , } , { name : 'robots' , content : 'all' , } , { name : 'author' , content : 'Atyantik Technologies Private Limited' , } , { httpEquiv : 'x-ua-compatible' , content : 'ie=edge' , } , { name : 'viewport' , content : 'width=device-width, initial-scale=1, shrink-to-fit=no' , } , { name : 'application-name' , content : get ( pwaSchema , 'name' , '' ) , } , { name : 'generator' , content : 'PawJS' , } , { name : 'rating' , content : 'General' , } , { name : 'mobile-web-app-capable' , content : 'yes' , } , { name : 'theme-color' , content : get ( pwaSchema , 'theme_color' , '#fff' ) , } , { name : 'apple-mobile-web-app-capable' , content : 'yes' , } , { name : 'apple-mobile-web-app-status-bar-style' , content : get ( pwaSchema , 'theme_color' , '#fff' ) , } , { name : 'apple-mobile-web-app-title' , content : get ( pwaSchema , 'name' , '' ) , } , { name : 'msapplication-tooltip' , content : get ( pwaSchema , 'description' , '' ) , } , { name : 'msapplication-starturl' , content : get ( pwaSchema , 'start_url' , '' ) , } , { name : 'msapplication-TileColor' , content : get ( pwaSchema , 'background_color' , '#fff' ) , } , { name : 'renderer' , content : 'webkit|ie-comp|ie-stand' , } , { name : 'full-screen' , content : 'yes' , } , ] ; type { [ * ] } export const metaKeys = [ 'name' , 'itemProp' , 'property' , 'charSet' , ] ; param url param baseUrl returns { * } const getFullUrl = ( url , baseUrl = '' ) => { let fullImageUrl = url ; if ( ! fullImageUrl . startsWith ( 'http' ) ) { fullImageUrl = `${ baseUrl }${ ! fullImageUrl . startsWith ( '/' ) ? '/' : '' }${ fullImageUrl }` ; } return fullImageUrl ; } ; param meta returns { boolean | string } const getMetaKey = ( meta ) : string => { let selectedMetaKey = '' ; each ( metaKeys , ( key ) => { if ( ! selectedMetaKey && get ( meta , key , false ) ) { selectedMetaKey = key ; } } ) ; return selectedMetaKey ; } ; param source { Array } param customMetas { Array } const addUpdateMeta = ( source = [ ] , customMetas = [ ] ) => { each ( customMetas , ( meta ) => { const metaKey = getMetaKey ( meta ) ; let metaUpdated = false ; if ( metaKey ) { const generatedSchemaObj = find ( source , { [ metaKey ] : meta [ metaKey ] } ) ; if ( generatedSchemaObj ) { each ( meta , ( value , key ) => { set ( generatedSchemaObj , key , value ) ; } ) ; metaUpdated = true ; } } if ( ! metaUpdated ) { source . push ( meta ) ; } } ) ; } ; param data param options returns { Array } export const generateMeta = ( data = { } , options = { baseUrl : '' , url : '' , pwaSchema : { } , seoSchema : { } , } ) => { const seoData = defaultsDeep ( data , options . seoSchema ) ; let generatedSchema = [ ] ; const descriptionText = getTextFromHtml ( seoData . description ) ; const desc155chars = descriptionText . slice ( 0 , 155 ) ; const desc200chars = descriptionText . slice ( 0 , 200 ) ; const baseUrl = options . baseUrl . replace ( /\/$/ , '' ) ; addUpdateMeta ( generatedSchema , cloneDeep ( defaultMeta ( options . pwaSchema ) ) ) ; /**
     * Manage name/title
     */ generatedSchema . push ( { name : 'title' , content : seoData . title , } ) ; generatedSchema . push ( { name : 'twitter:title' , content : seoData . title , } ) ; generatedSchema . push ( { property : 'og:title' , content : seoData . title , } ) ; /**
     * Manage keywords (allow string and array as well)
     */ if ( process . env . ENABLE_KEYWORDS && ( process . env . ENABLE_KEYWORDS === 'true' || process . env . ENABLE_KEYWORDS === '1' || process . env . ENABLE_KEYWORDS === 'yes' ) ) { if ( typeof seoData . keywords === 'string' && seoData . keywords . trim ( ) . length ) { generatedSchema . push ( { name : 'keywords' , content : seoData . keywords , } ) ; } if ( Array . isArray ( seoData . keywords ) && seoData . keywords . length ) { generatedSchema . push ( { name : 'keywords' , content : seoData . keywords . join ( ',' ) , } ) ; } } /**
     * Manage twitter site & author
     */ const twitterSite = get ( seoData , 'twitter.site' , '' ) ; if ( twitterSite . length ) { generatedSchema . push ( { name : 'twitter:site' , content : twitterSite , } ) ; } const twitterCreator = get ( seoData , 'twitter.creator' , '' ) ; if ( twitterCreator . length ) { generatedSchema . push ( { name : 'twitter:creator' , content : twitterCreator , } ) ; } /**
     * Manage facebook admins
     */ const fbAdmins = get ( seoData , 'facebook.admins' , [ ] ) ; if ( fbAdmins && fbAdmins . length ) { generatedSchema . push ( { property : 'fb:admins' , content : fbAdmins . join ( ',' ) , } ) ; } /**
     * Manage description
     */ generatedSchema . push ( { name : 'description' , content : desc155chars , } ) ; generatedSchema . push ( { name : 'twitter:description' , content : desc200chars , } ) ; generatedSchema . push ( { property : 'og:description' , content : descriptionText , } ) ; /**
     * Site name
     */ if ( seoData . site_name && seoData . site_name . length ) { generatedSchema . push ( { property : 'og:site_name' , content : seoData . site_name , } ) ; } /**
     * Manage Primary Image
     */ const hasImage = ( seoData . image && ! ! seoData . image . length ) ; if ( hasImage ) { let images = seoData . image ; if ( ! Array . isArray ( images ) ) { images = [ images ] ; } const image = first ( images ) || '' ; const fullImageUrl = getFullUrl ( image , baseUrl ) ; generatedSchema . push ( { itemProp : 'image' , content : fullImageUrl , } ) ; generatedSchema . push ( { name : 'twitter:image:src' , content : fullImageUrl , } ) ; if ( image . length > 1 ) { each ( images , ( img ) => { generatedSchema . push ( { property : 'og:image' , content : getFullUrl ( img , baseUrl ) , } ) ; } ) ; } else { generatedSchema . push ( { property : 'og:image' , content : fullImageUrl , } ) ; } generatedSchema . push ( { name : 'twitter:card' , content : 'summary_large_image' , } ) ; } else { generatedSchema . push ( { name : 'twitter:card' , content : 'summary' , } ) ; } /**
     * Manage Type article/product/music/movie etc
     */ generatedSchema . push ( { property : 'og:type' , content : seoData . type , } ) ; let twitterDataCounter = 1 ; each ( seoData . type_details , ( value , key ) => { if ( typeof value === 'object' && value !== null ) { each ( value , ( subValue , subKey ) => { if ( ! isEmpty ( subValue ) ) { generatedSchema . push ( { property : `${ seoData . type }:${ key }:${ subKey }` , content : subValue , } ) ; generatedSchema . push ( { name : `twitter:data${ twitterDataCounter }` , content : subValue , } ) ; generatedSchema . push ( { name : `twitter:label${ twitterDataCounter }` , content : subKey , } ) ; twitterDataCounter += 1 ; } } ) ; } else if ( ! isEmpty ( value ) ) { generatedSchema . push ( { property : `${ seoData . type }:${ key }` , content : value , } ) ; generatedSchema . push ( { name : `twitter:data${ twitterDataCounter }` , content : value , } ) ; generatedSchema . push ( { name : `twitter:label${ twitterDataCounter }` , content : key , } ) ; twitterDataCounter += 1 ; } } ) ; let url = get ( seoData , 'url' , get ( options , 'url' , '' ) ) ; if ( ! url . length && isBrowser ( ) ) { url = get ( window , 'location.href' , '' ) ; } if ( url . trim ( ) . length ) { generatedSchema . push ( { property : 'og:url' , content : url , } ) ; } const userMeta = get ( seoData , 'meta' , [ ] ) ; addUpdateMeta ( generatedSchema , userMeta ) ; generatedSchema = uniqWith ( generatedSchema , isEqual ) ; return generatedSchema ; } ; param str param length returns export const trimTillLastSentence = ( str , length = 0 ) => { let strr = getTextFromHtml ( str ) ; if ( ! length || ! strr . length ) { return strr ; } strr += ' ' ; let trimmedString = strr . substr ( 0 , length + 1 ) ; let separator = '.' ; if ( trimmedString . lastIndexOf ( separator ) === - 1 ) { separator = ' ' ; if ( trimmedString . lastIndexOf ( separator ) === - 1 ) { trimmedString = str . substr ( 0 , length ) ; return trimmedString ; } } return trimmedString . substr ( 0 , Math . min ( trimmedString . length - 1 , trimmedString . lastIndexOf ( separator ) ) ) . trim ( ) ; } ;