Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade roc-package-web-app to use Koa@2 #30

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"env": {
"jest": true
},

"extends": "airbnb",

"parser": "babel-eslint",
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ lib
build
package-lock.json
yarn.lock
.idea
coverage/
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"eslint-plugin-import": "^1.16.0",
"eslint-plugin-jsx-a11y": "2.2.1",
"eslint-plugin-react": "6.2.0",
"jest": "^22.0.0",
"roc": "^1.0.0-rc.23",
"roc-plugin-repo": "0.0.25",
"roc-plugin-repo": "0.1.6",
"roc-plugin-repo-roc": "0.0.1"
}
}
26 changes: 13 additions & 13 deletions packages/roc-package-web-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,21 @@
"dependencies": {
"config": "~1.16.0",
"debug": "~2.2.0",
"koa": "~1.1.1",
"koa-accesslog": "~0.0.2",
"koa-add-trailing-slashes": "~1.1.0",
"koa-compressor": "~1.0.3",
"koa": "~2.4.1",
"koa-add-trailing-slashes": "~2.0.1",
"koa-compress": "~2.0.0",
"koa-conditional-get": "~1.0.3",
"koa-errors": "~1.0.1",
"koa-etag": "~2.0.0",
"koa-favicon": "~1.2.0",
"koa-helmet": "~0.3.0",
"koa-logger": "~1.3.0",
"koa-lowercase-path": "~1.0.0",
"koa-normalize-path": "~1.0.0",
"koa-remove-trailing-slashes": "~1.0.0",
"koa-static": "~2.0.0",
"koa-error": "~3.1.1",
"koa-etag": "~3.0.0",
"koa-favicon": "~2.0.0",
"koa-helmet": "~3.3.0",
"koa-logger": "~3.1.0",
"koa-lowercase-path": "~2.0.0",
"koa-normalize-path": "~2.0.0",
"koa-remove-trailing-slashes": "~2.0.0",
"koa-static": "~4.0.2",
"lodash": "4.13.1",
"moment": "~2.20.1",
"roc": "^1.0.0-rc.23",
"roc-package-webpack-node": "^1.0.0",
"roc-package-webpack-web": "^1.0.0"
Expand Down
8 changes: 4 additions & 4 deletions packages/roc-package-web-app/src/app/createServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import http from 'http';
import https from 'https';

import debug from 'debug';
import koa from 'koa';
import Koa from 'koa';
import serve from 'koa-static';
import addTrailingSlash from 'koa-add-trailing-slashes';
import normalizePath from 'koa-normalize-path';
import lowercasePath from 'koa-lowercase-path';
import removeTrailingSlash from 'koa-remove-trailing-slashes';
import { merge, getSettings, getAbsolutePath } from 'roc';

import basepathSupport from './basepathSupport';
import basepathSupport from './middlewares/basepathSupport';

/**
* Creates a server instance.
Expand Down Expand Up @@ -41,15 +41,15 @@ export default function createServer(options = {}, beforeUserMiddlewares = [], {
const logger = debug('roc:server');
logger.log = console.info.bind(console);

const server = koa();
const server = new Koa();
const runtimeSettings = merge(getSettings('runtime'), options);

// Add support for rocPath
server.use(basepathSupport(rocPath));

if (useDefaultKoaMiddlewares) {
// eslint-disable-next-line
const middlewares = require('./middlewares').default(runtimeSettings, { dev, dist });
const middlewares = require('./defaultKoaMiddlewares').default(runtimeSettings, { dev, dist });
middlewares.forEach((middleware) => server.use(middleware));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import koaErrors from 'koa-errors';
import helmet from 'koa-helmet';
import koaError from 'koa-error';
import koaHelmet from 'koa-helmet';
import koaEtag from 'koa-etag';
import koaCompressor from 'koa-compressor';
import koaCompress from 'koa-compress';
import koaFavicon from 'koa-favicon';
import koaAccesslog from 'koa-accesslog';
import koaLogger from 'koa-logger';

import accesslog from './middlewares/accesslog';

/**
* Returns the middlewares to be used.
*
Expand All @@ -16,17 +17,17 @@ export default function middlewares(config, { dev, dist }) {
const middlewaresList = [];

if (dev) {
middlewaresList.push(koaErrors());
middlewaresList.push(koaError());
}

// Security headers
middlewaresList.push(helmet());
middlewaresList.push(koaHelmet());

middlewaresList.push(koaEtag());

// We only enable gzip in dist
if (dist) {
middlewaresList.push(koaCompressor());
middlewaresList.push(koaCompress());
}

const favicon = config.favicon;
Expand All @@ -35,7 +36,7 @@ export default function middlewares(config, { dev, dist }) {
}

if (dist) {
middlewaresList.push(koaAccesslog());
middlewaresList.push(accesslog());
} else {
middlewaresList.push(koaLogger());
}
Expand Down
64 changes: 64 additions & 0 deletions packages/roc-package-web-app/src/app/defaultKoaMiddlewares.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import koaErrorMock from 'koa-error';
import koaHelmetMock from 'koa-helmet';
import koaEtagMock from 'koa-etag';
import koaLoggerMock from 'koa-logger';
import koaCompressMock from 'koa-compress';
import koaFaviconMock from 'koa-favicon';

import accessLogMock from './middlewares/accesslog';
import getKoaMiddlewares from './defaultKoaMiddlewares';

jest.mock('koa-error', () => jest.fn(() => function koaError() {}));
jest.mock('koa-helmet', () => jest.fn(() => function koaHelmet() {}));
jest.mock('koa-etag', () => jest.fn(() => function koaEtag() {}));
jest.mock('koa-logger', () => jest.fn(() => function koaLogger() {}));
jest.mock('koa-compress', () => jest.fn(() => function koaCompress() {}));
jest.mock('koa-favicon', () => jest.fn(() => function koaFavicon() {}));
jest.mock('./middlewares/accesslog', () => jest.fn(() => function accessLog() {}));


describe('defaultKoaMiddleware', () => {
afterEach(() => {
koaErrorMock.mockClear();
koaHelmetMock.mockClear();
koaEtagMock.mockClear();
koaLoggerMock.mockClear();
koaCompressMock.mockClear();
});

it('should return default array of middlewares even when all options are falsy', () => {
const middlewares = getKoaMiddlewares({}, { dev: false, dist: false });

expect(middlewares.length).toEqual(3);
expect(middlewares.map(f => f.name)).toEqual(['koaHelmet', 'koaEtag', 'koaLogger']);
expect(koaHelmetMock).toHaveBeenCalled();
expect(koaEtagMock).toHaveBeenCalled();
expect(koaLoggerMock).toHaveBeenCalled();
});

it('should include koa-error when dev is set to true', () => {
const middlewares = getKoaMiddlewares({}, { dev: true, dist: false });

expect(middlewares.length).toEqual(4);
expect(middlewares.map(f => f.name)).toEqual(['koaError', 'koaHelmet', 'koaEtag', 'koaLogger']);
expect(koaErrorMock).toHaveBeenCalled();
});

it('should include koa-compress and accessLog instead of koa-logger when dist is set to true', () => {
const middlewares = getKoaMiddlewares({}, { dev: false, dist: true });

expect(middlewares.length).toEqual(4);
expect(middlewares.map(f => f.name)).toEqual(['koaHelmet', 'koaEtag', 'koaCompress', 'accessLog']);
expect(koaCompressMock).toHaveBeenCalled();
expect(accessLogMock).toHaveBeenCalled();
});

it('should include koa-favicon when favicon is specified in config', () => {
const config = { favicon: './pathToFavicon.ico' };
const middlewares = getKoaMiddlewares(config, { dev: false, dist: false });

expect(middlewares.length).toEqual(4);
expect(middlewares.map(f => f.name)).toEqual(['koaHelmet', 'koaEtag', 'koaFavicon', 'koaLogger']);
expect(koaFaviconMock).toHaveBeenCalledWith(config.favicon);
});
});
18 changes: 18 additions & 0 deletions packages/roc-package-web-app/src/app/middlewares/accesslog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import util from 'util';

import moment from 'moment';

/*
Implementation based on koa-accesslog
*/
export default function accesslog(stream = process.stdout) {
return async function(ctx, next) {
await next();

const format = '%s - - [%s] "%s %s HTTP/1.X" %d %s\n';
const length = ctx.length ? ctx.length.toString() : '-';
const date = moment().format('D/MMM/YYYY:HH:mm:ss ZZ');

stream.write(util.format(format, ctx.ip, date, ctx.method, ctx.path, ctx.status, length));
};
}
52 changes: 52 additions & 0 deletions packages/roc-package-web-app/src/app/middlewares/accesslog.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { format as formatMock } from 'util';

import createAccessLogMiddleware from './accesslog';

jest.mock('util', () => ({
format: jest.fn(),
}));

describe('middlewares/accesslog', () => {
const next = async () => {};

afterEach(() => {
formatMock.mockClear();
});

it('should call write method of passed stream', async () => {
const streamWriteMock = jest.fn();
const streamMock = {
write: streamWriteMock,
};
const accessLogMiddleware = createAccessLogMiddleware(streamMock);
await accessLogMiddleware({}, next);

expect(streamWriteMock).toHaveBeenCalled();
});

it('should call util.format with certain ctx properties', async () => {
const streamWriteMock = jest.fn();
const streamMock = {
write: streamWriteMock,
};
const accessLogMiddleware = createAccessLogMiddleware(streamMock);
const ctx = {
length: 42,
ip: '127.0.0.1',
method: 'GET',
path: '/app',
status: 200,
};
await accessLogMiddleware(ctx, next);

expect(formatMock).toHaveBeenCalledWith(
'%s - - [%s] \"%s %s HTTP/1.X\" %d %s\n',
ctx.ip,
expect.anything(),
ctx.method,
ctx.path,
ctx.status,
ctx.length.toString(),
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ export default function basepathSupport(basepath) {
return newPath;
}

return function* (next) {
return async function(ctx, next) {
// Do nothing if the basepath is /
if (basepath === '/') {
return yield next;
return await next();
}

const newPath = matcher(this.path);
const newPath = matcher(ctx.path);
if (newPath) {
this.path = newPath;
return yield next;
ctx.path = newPath;
return await next();
}

// If the path does not match Koa will render a default 404 Not Found page.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import createBasepathSupportMiddleware from './basepathSupport';

describe('middlewares/basepathSupport', () => {
const next = async () => {};

it('should throw an error when basepath does not start with "/"', () => {
const veryWrongBasepath = 'veryWrongBasepath';
expect(() => {
createBasepathSupportMiddleware(veryWrongBasepath);
}).toThrowError(`The basepath must start with "/", was ${veryWrongBasepath}`);
});

it('should set new ctx.path with eased basepath', async () => {
const basepath = '/basepath';
const ctx = { path: '/basepath/application' };
const basepathMiddleware = createBasepathSupportMiddleware(basepath);
await basepathMiddleware(ctx, next);

expect(ctx.path).toEqual('/application');
});

it('should set new ctx.path to "/" if its the same as basepath', async () => {
const basepath = '/basepath';
const ctx = { path: '/basepath' };
const basepathMiddleware = createBasepathSupportMiddleware(basepath);
await basepathMiddleware(ctx, next);

expect(ctx.path).toEqual('/');
});

it('should return an undefined when basepath is different than "/" and no new path is set', async () => {
const basepath = '/';
const ctx = { path: '/' };
const basepathMiddleware = createBasepathSupportMiddleware(basepath);
const result = await basepathMiddleware(ctx, next);

expect(result).toEqual(undefined);
});
});
7 changes: 5 additions & 2 deletions roc.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
module.exports = {
settings: {
repo: {
targets: ['cjs'],
babelPresetEnv: {
targets: {
node: '6',
node: '8',
},
},
mono: [
Expand All @@ -13,6 +12,10 @@ module.exports = {
'packages',
'plugins',
],
release: {
includeBody: true,
changelogTypes: true,
},
},
},
};