import fetch , { RequestInit , Response } from 'node-fetch' ; import { Nuxtent } from '../../types' ; import { Nuxt } from '../../types/nuxt' ; import { Context , ErrorParams } from '@nuxt/vue-app' ; let config = { api : { apiBrowserPrefix : '/_nuxt/content' , apiServerPrefix : '/content-api' , baseURL : 'http://localhost:3000' , browserBaseURL : '' , host : 'localhost' , port : '3000' , } , } ; try { config = require ( './nuxtent-config' ) ; config = config . default || config ; } catch ( error ) { } const api = config . api ; api . port = process . env . PORT || process . env . NUXT_PORT || process . env . NUXTENT_PORT || api . port ; api . host = process . env . HOST || process . env . NUXT_HOST || process . env . NUXTENT_HOST || api . host ; const DEBUG = true ; class ApiError extends Error { public response : null ; public status : number ; private isObject : boolean ; constructor ( message , data , status ) { super ( message ) ; let response = null ; let isObject = false ; try { response = JSON . parse ( data ) ; isObject = true ; } catch ( e ) { response = data ; } this . name = 'Nuxtent Error' ; this . response = response ; this . message = message ; this . status = status ; this . isObject = isObject ; Object . setPrototypeOf ( this , ApiError . prototype ) ; } public toString ( ) { return `${ this . message }\nResponse:\n${ this . isObject ? JSON . stringify ( this . response , null , 2 ) : this . response }` ; } } const jsonRoutes = process . static ? process . server : true ; const API_URL = process . static && process . browser ? api . apiBrowserPrefix : process . browser ? api . apiServerPrefix : api . baseURL + api . apiServerPrefix ; function fetchResource ( path , userOptions = { } ) { const defaultOptions = { } ; const defaultHeaders = { } ; const options = { ... defaultOptions , ... userOptions , headers : { ... defaultHeaders , ... userOptions . headers , } , } ; const url = path ? `${ API_URL }/${ path . replace ( /^\// , '' ) }` : API_URL ; if ( options . body && typeof options . body === 'object' ) { options . body = JSON . stringify ( options . body ) ; } let response = null ; return ( fetch ( url , options ) . then ( ( responseObject ) => { response = responseObject ; if ( response . status === 401 ) { } if ( response . status < 200 || response . status >= 300 ) { return response . text ( ) ; } return response . json ( ) ; } ) . then ( parsedResponse => { if ( response . status < 200 || response . status >= 300 ) { throw parsedResponse ; } return parsedResponse ; } ) . catch ( error => { if ( response ) { throw new ApiError ( `Request failed with status ${ response . status }.` , { pemralink : url , error } , response . status ) ; } else { throw new ApiError ( error . toString ( ) , null , 500 ) ; } } ) ) ; } function urlJoin ( ... elts ) { const re1 = new RegExp ( '^\\/|\\/$' , 'g' ) ; return elts . map ( element => element . replace ( re1 , '' ) ) . join ( '/' ) ; } class Content { public isAPI : boolean ; public isStatic : boolean ; public cache : { [ path ] : any ; } ; public $fetch : typeof fetchResource ; public queryString : string ; public contentDir : string ; public nuxtError : any ; public states : { IDLE : string ; WORKING : string ; } ; public state : string = 'IDLE' ; constructor ( nuxtError ) { this . isAPI = process . static ? process . server : true ; this . isStatic = process . static ; this . cache = { } ; this . nuxtError = nuxtError ; this . $fetch = fetchResource ; this . queryString = '' ; this . contentDir = '' ; this . states = { IDLE : 'IDLE' , WORKING : 'WORKING' , } ; } public toQuery ( options = { } ) { const exclude = options . exclude ; if ( ! exclude ) { return '' ; } if ( Array . isArray ( exclude ) ) { return 'exclude=' + exclude . join ( ',' ) ; } return 'exclude=' + exclude ; } get self ( ) { return { cache : this . cache , contentDir : this . contentDir , queryString : this . queryString , } ; } public async fetchContent ( permalink , query = '' ) { let apiPath ; if ( this . isAPI ) { apiPath = urlJoin ( this . contentDir , permalink + ( query . startsWith ( '?' ) ? query : `?${ query }` ) ) ; } else { const allButFirstSlash = /(?!^\/)(\/)/g ; const serializedPermalink = permalink . replace ( allButFirstSlash , '.' ) ; apiPath = urlJoin ( this . contentDir , serializedPermalink ) + '.json' ; } this . queryString = '' ; this . contentDir = '' ; if ( ! this . cache [ apiPath ] ) { return ( this . cache [ apiPath ] = await this . $fetch ( apiPath ) . then ( ( data ) => { return data ; } ) . catch ( error => { throw error ; } ) ) ; } this . state = this . states . IDLE ; return this . cache [ apiPath ] ; } param { String } contentDir public requestMethod ( contentDir ) { this . queryString = '' ; this . contentDir = contentDir ; return this ; } public query ( options = { } ) { if ( DEBUG ) { console . log ( 'nuxtent' , 'Query' , this . self ) ; } this . queryString = this . toQuery ( options ) ; return this ; } public get ( permalink ) { if ( typeof permalink !== 'string' ) { throw Error ( `Permalink must be a string.` ) ; } if ( DEBUG ) { console . log ( 'nuxtent' , 'Get' , { query : this . queryString , permalink } ) ; } return this . fetchContent ( permalink , this . queryString ) ; } public getBetween ( permalink , num1or2 , num2 = '' ) { const endpoint = this . isAPI ? '/' : '_between' ; const betweenQuery = 'between=' + [ permalink , num1or2 , num2 ] . join ( ',' ) ; const fullQuery = betweenQuery + '&' + this . queryString ; if ( DEBUG ) { console . log ( 'nuxtent' , 'getBetween' , { endpoint , fullQuery } ) ; } return this . fetchContent ( endpoint , fullQuery ) ; } public getOnly ( startIndex , endIndex ) { const endpoint = this . isAPI ? '/' : '_only' ; const onlyQuery = 'only=' + [ startIndex , endIndex ] . join ( ',' ) ; const fullQuery = onlyQuery + '&' + this . queryString ; if ( DEBUG ) { console . log ( 'nuxtent' , 'getonly' , { endpoint , fullQuery } ) ; } return this . fetchContent ( endpoint , fullQuery ) ; } public getAll ( ) { const permalink = this . isAPI ? '/' : '_all' ; if ( DEBUG ) { console . log ( 'nuxtent' , 'getall' , permalink ) ; } return this . fetchContent ( permalink , this . queryString ) ; } } export default ( { isStatic , isHMR , route , error } , inject ) => { const isNotContentReq = isHMR || route . fullPath . includes ( '__webpack_hmr?' ) || route . fullPath . includes ( '.hot-update.' ) ; if ( isNotContentReq ) { return ; } const nuxtent = new Content ( error ) ; inject ( 'nuxtent' , nuxtent ) ; inject ( 'content' , ( contentDir ) => nuxtent . requestMethod ( contentDir ) ) ; } ;