@dinamomx/nuxtent
Version:
Seamlessly use content files in your Nuxt.js sites.
255 lines (252 loc) • 8.51 kB
JavaScript
import fetch from 'node-fetch';
// @ts-ignore
let config = {
// Defaults
api: {
apiBrowserPrefix: '/_nuxt/content',
apiServerPrefix: '/content-api',
baseURL: 'http://localhost:3000',
browserBaseURL: '',
host: 'localhost',
port: '3000',
},
};
try {
// tslint:disable-next-line: no-var-requires
config = require('./nuxtent-config');
// @ts-ignore
config = config.default || config;
// tslint:disable-next-line: no-empty
}
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;
// ------------------------------------------------------ //
// Simple JavaScript API wrapper
// https://stanko.github.io/simple-javascript-api-wrapper
// ------------------------------------------------------ //
class ApiError extends Error {
constructor(message, data, status) {
super(message);
let response = null;
let isObject = false;
// We are trying to parse response
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;
// Set the prototype explicitly.
Object.setPrototypeOf(this, ApiError.prototype);
}
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;
// API wrapper function
function fetchResource(path, userOptions = {}) {
// Define default options
const defaultOptions = {};
// Define default headers
const defaultHeaders = {};
const options = {
// Merge options
...defaultOptions,
...userOptions,
// Merge headers
headers: {
...defaultHeaders,
...userOptions.headers,
},
};
// Build Url
const url = path ? `${API_URL}/${path.replace(/^\//, '')}` : API_URL;
// Stringify JSON data
// If body is not a file
if (options.body && typeof options.body === 'object') {
options.body = JSON.stringify(options.body);
}
// Variable which will be used for storing response
let response = null;
return (fetch(url, options)
.then((responseObject) => {
// Saving response for later use in lower scopes
response = responseObject;
// HTTP unauthorized
if (response.status === 401) ;
// Check for error HTTP error codes
if (response.status < 200 || response.status >= 300) {
// Get response as text
return response.text();
}
// Get response as json
return response.json();
})
// "parsedResponse" will be either text or javascript object depending if
// "response.text()" or "response.json()" got called in the upper scope
.then(parsedResponse => {
// Check for HTTP error codes
if (response.status < 200 || response.status >= 300) {
// Throw error
throw parsedResponse;
}
// Request succeeded
return parsedResponse;
})
.catch(error => {
// Throw custom API error
// If response exists it means HTTP error occured
if (response) {
throw new ApiError(`Request failed with status ${response.status}.`, { pemralink: url, error }, response.status);
}
else {
throw new ApiError(error.toString(), null, 500);
}
}));
}
// TODO: Analizar posible exceso de memoria con el cache
function urlJoin(...elts) {
const re1 = new RegExp('^\\/|\\/$', 'g');
return elts.map(element => element.replace(re1, '')).join('/');
}
// tslint:disable-next-line: max-classes-per-file
class Content {
constructor(nuxtError) {
this.state = 'IDLE';
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',
};
}
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,
};
}
async fetchContent(permalink, query = '') {
// replace leading slash
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];
}
/**
* $content la primera función que define de donde se traen las estas
* @param {String} contentDir El directorio del contenido
*/
requestMethod(contentDir) {
this.queryString = '';
this.contentDir = contentDir;
return this;
}
query(options = {}) {
// per page query
{
// tslint:disable-next-line: no-console
console.log('nuxtent', 'Query', this.self);
}
this.queryString = this.toQuery(options);
return this;
}
get(permalink) {
if (typeof permalink !== 'string') {
throw Error(`Permalink must be a string.`);
}
{
// tslint:disable-next-line: no-console
console.log('nuxtent', 'Get', { query: this.queryString, permalink });
}
return this.fetchContent(permalink, this.queryString);
}
getBetween(permalink, num1or2, num2 = '') {
const endpoint = this.isAPI ? '/' : '_between';
const betweenQuery = 'between=' + [permalink, num1or2, num2].join(',');
const fullQuery = betweenQuery + '&' + this.queryString;
{
// tslint:disable-next-line: no-console
console.log('nuxtent', 'getBetween', { endpoint, fullQuery });
}
return this.fetchContent(endpoint, fullQuery);
}
getOnly(startIndex, endIndex) {
const endpoint = this.isAPI ? '/' : '_only';
const onlyQuery = 'only=' + [startIndex, endIndex].join(',');
const fullQuery = onlyQuery + '&' + this.queryString;
{
// tslint:disable-next-line: no-console
console.log('nuxtent', 'getonly', { endpoint, fullQuery });
}
return this.fetchContent(endpoint, fullQuery);
}
getAll() {
const permalink = this.isAPI ? '/' : '_all';
{
// tslint:disable-next-line: no-console
console.log('nuxtent', 'getall', permalink);
}
return this.fetchContent(permalink, this.queryString);
}
}
var nuxtentRequest = ({ 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));
};
export default nuxtentRequest;