diff --git a/package-lock.json b/package-lock.json index ddc8191b..4826c3f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7127,6 +7127,11 @@ "lodash.keys": "^3.0.0" } }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", diff --git a/package.json b/package.json index 4c6d2a67..82cc373e 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "babel-polyfill": "^6.26.0", "eslint": "^5.5.0", "eslint-plugin-jest": "^21.22.0", + "lodash.clonedeep": "^4.5.0", "lodash.pick": "^4.4.0", "path-to-regexp": "^1.7.0", "prop-types": "^15.6.1", diff --git a/src/Routes.js b/src/Routes.js index 309027a8..718a305d 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -3,7 +3,7 @@ import pick from 'lodash.pick' import NextLink from 'next/link' import NextRouter from 'next/router' import Route from './Route' -import { generateRouteFromObjectName, redirectToLocalizedHome } from './helpers/routeHelper' +import { generateRouteFromObjectName, redirectToLocalizedHome, detectLocale } from './helpers/routeHelper' import MiddlewareManager from './middleware/MiddlewareManager' export default class Routes { @@ -125,18 +125,27 @@ export default class Routes { req.siteUrl = this.siteUrl req.getMultilanguageUrls = () => this.getMultilanguageUrls(route, query) - MiddlewareManager(route.middlewares, { req, res, route, query })((err, data) => { - if (err) throw err + const middleware = MiddlewareManager(route.middlewares, { req, res, route, query }) + middleware((err, data) => { + if (err) { + const { pathname } = parsedUrl + const { statusCode = 500 } = err + res.statusCode = statusCode + app.renderError(err, req, res, pathname, query) + return + } req.nextData = data + renderRoute(app, customHandler, { req, res, route, query }) }) - renderRoute(app, customHandler, { req, res, route, query }) return } + if (req.url === '/' && this.forceLocale) { - redirectToLocalizedHome(res, this.locale) + const detectedLocale = detectLocale({ req, routes: this.routes, defaultLocale: this.locale }) + redirectToLocalizedHome(res, detectedLocale) return } diff --git a/src/helpers/routeHelper.js b/src/helpers/routeHelper.js index ffd6e5e9..816f0b03 100644 --- a/src/helpers/routeHelper.js +++ b/src/helpers/routeHelper.js @@ -24,4 +24,11 @@ export const redirectToLocalizedHome = (res, locale) => { res.writeHead(301, { 'Location': `/${locale}` }) res.end() } +} + +export const detectLocale = ({ req, routes, defaultLocale }) => { + const acceptsLangs = typeof req.acceptsLanguages === 'function' && req.acceptsLanguages() + const langs = !acceptsLangs ? [] : acceptsLangs.filter(lang => lang.length === 2) + const routesLangs = routes.map(({ locale }) => locale) + return routesLangs.indexOf(langs[0]) > -1 ? langs[0] : defaultLocale } \ No newline at end of file diff --git a/src/middleware/MiddlewareManager.js b/src/middleware/MiddlewareManager.js index 50c88e46..3fa4bba2 100644 --- a/src/middleware/MiddlewareManager.js +++ b/src/middleware/MiddlewareManager.js @@ -1,10 +1,12 @@ 'use strict' import async from 'async' +import clonedeep from 'lodash.clonedeep' export default (middlewares = [], initialData = {}) => { + const reversed = clonedeep(middlewares).reverse() return async.compose( - ...middlewares.reverse(), + ...reversed, function (cb) { cb(null, initialData) }, diff --git a/test/helpers/routeHelper.test.js b/test/helpers/routeHelper.test.js index 7aabea8b..38a459ef 100644 --- a/test/helpers/routeHelper.test.js +++ b/test/helpers/routeHelper.test.js @@ -1,4 +1,4 @@ -import { generateRouteFromObjectName } from './../../src/helpers/routeHelper' +import { generateRouteFromObjectName, detectLocale } from './../../src/helpers/routeHelper' describe('generateRouteFromObject()', () => { it('thrown err if name not exist', () => { @@ -22,4 +22,26 @@ describe('generateRouteFromObject()', () => { expect(result).toHaveProperty('update') expect(result).not.toHaveProperty('foo') }) + + it('return detected locale', () => { + const req = { + acceptsLanguages: () => { return ['it-IT', 'it', 'en', 'es'] } + } + + const routes = [{ locale: 'en' }, { locale: 'it' }] + const result = detectLocale({ req, routes, defaultLocale: 'en' }) + + expect(result).toBe('it') + }) + + it('return default locale if detected is non supoorted', () => { + const req = { + acceptsLanguages: () => { return ['it-IT', 'es'] } + } + + const routes = [{ locale: 'en' }, { locale: 'it' }] + const result = detectLocale({ req, routes, defaultLocale: 'en' }) + + expect(result).toBe('en') + }) }) \ No newline at end of file diff --git a/test/routes.test.js b/test/routes.test.js index 123aaa44..7a94a816 100644 --- a/test/routes.test.js +++ b/test/routes.test.js @@ -236,4 +236,29 @@ describe('middleware()', () => { expect(routes.routes[0].middlewares).toBeInstanceOf(Array) expect(routes.routes[0].middlewares[0]).toBe(testFunct) }) + + test('can render error page if a middleware callback has error', () => { + const testFunct = (params, cb) => { + const error = new Error('this is an error') + error.statusCode = 418 + cb(error) + } + + const routes = nextRoutes({ locale: 'it' }) + routes.add('a', 'en', '/').middleware([testFunct]) + + const app = { + getRequestHandler: () => { }, + renderError: jest.fn() + } + const requestHandler = routes.getRequestHandler(app) + const req = { url: '/en' } + const res = {} + + requestHandler(req, res) + + expect(app.renderError.mock.calls.length).toBe(1) + expect(res.statusCode).toBe(418) + + }) })