Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 0 additions & 2 deletions .eslintrc

This file was deleted.

15 changes: 15 additions & 0 deletions .github/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: unit-tests
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run test-ci
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
# OS #
###################
.DS_Store
.idea
Thumbs.db
tmp/
temp/


# Node.js #
###################
node_modules
package-lock.json
yarn.lock
npm-debug.log
yarn-debug.log
yarn-error.log

# Build #
###################
dist
build

# NYC #
###################
coverage
*.lcov
.nyc_output
5 changes: 5 additions & 0 deletions .huskyrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"hooks": {
"pre-commit": "npm run lint"
}
}
9 changes: 0 additions & 9 deletions .travis.yml

This file was deleted.

11 changes: 11 additions & 0 deletions .xo-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"prettier": true,
"space": true,
"extends": [
"xo-lass"
],
"ignores": [
"test/**",
"examples/**"
]
}
40 changes: 20 additions & 20 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# koa-mount

Mount other Koa applications as middleware. The `path` passed to `mount()` is stripped
from the URL temporarily until the stack unwinds. This is useful for creating entire
apps or middleware that will function correctly regardless of which path segment(s)
they should operate on.
Mount other Koa applications as middleware. The `path` passed to `mount()` is stripped
from the URL temporarily until the stack unwinds. This is useful for creating entire
apps or middleware that will function correctly regardless of which path segment(s)
they should operate on.

## Installation

Expand All @@ -13,14 +13,14 @@ $ npm install koa-mount

## Examples

View the [./examples](examples) directory for working examples.
View the [./examples](examples) directory for working examples.

### Mounting Applications

Entire applications mounted at specific paths. For example you could mount
a blog application at "/blog", with a router that matches paths such as
"GET /", "GET /posts", and will behave properly for "GET /blog/posts" etc
when mounted.
Entire applications mounted at specific paths. For example you could mount
a blog application at "/blog", with a router that matches paths such as
"GET /", "GET /posts", and will behave properly for "GET /blog/posts" etc
when mounted.

```js
const mount = require('koa-mount');
Expand All @@ -30,7 +30,7 @@ const Koa = require('koa');

const a = new Koa();

a.use(async function (ctx, next){
a.use(async function (ctx, next) {
await next();
ctx.body = 'Hello';
});
Expand All @@ -39,7 +39,7 @@ a.use(async function (ctx, next){

const b = new Koa();

b.use(async function (ctx, next){
b.use(async function (ctx, next) {
await next();
ctx.body = 'World';
});
Expand All @@ -55,7 +55,7 @@ app.listen(3000);
console.log('listening on port 3000');
```

Try the following requests:
Try the following requests:

```
$ GET /
Expand All @@ -70,19 +70,19 @@ World

### Mounting Middleware

Mount middleware at specific paths, allowing them to operate independently
of the prefix, as they're not aware of it.
Mount middleware at specific paths, allowing them to operate independently
of the prefix, as they're not aware of it.

```js
const mount = require('koa-mount');
const Koa = require('koa');

async function hello(ctx, next){
async function hello(ctx, next) {
await next();
ctx.body = 'Hello';
}

async function world(ctx, next){
async function world(ctx, next) {
await next();
ctx.body = 'World';
}
Expand All @@ -98,7 +98,7 @@ console.log('listening on port 3000');

### Optional Paths

The path argument is optional, defaulting to "/":
The path argument is optional, defaulting to "/":

```js
app.use(mount(a));
Expand All @@ -107,8 +107,8 @@ app.use(mount(b));

## Debugging

Use the __DEBUG__ environement variable to whitelist
koa-mount debug output:
Use the **DEBUG** environement variable to whitelist
koa-mount debug output:

```
$ DEBUG=koa-mount node myapp.js &
Expand All @@ -124,4 +124,4 @@ $ GET /foo/bar/baz

## License

MIT
MIT
73 changes: 37 additions & 36 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@

/**
* Module dependencies.
*/

const debug = require('debug')('koa-mount')
const compose = require('koa-compose')
const assert = require('assert')
const util = require('node:util');
const assert = require('node:assert');
const compose = require('koa-compose');

const debug = util.debuglog('koa-mount');

/**
* Expose `mount()`.
*/

module.exports = mount
module.exports = mount;

/**
* Mount `app` with `prefix`, `app`
Expand All @@ -24,51 +25,51 @@ module.exports = mount
* @api public
*/

function mount (prefix, app) {
function mount(prefix, app) {
if (typeof prefix !== 'string') {
app = prefix
prefix = '/'
app = prefix;
prefix = '/';
}

assert.equal(prefix[0], '/', 'mount path must begin with "/"')
assert.equal(prefix[0], '/', 'mount path must begin with "/"');

// compose
let downstream = app
let downstream = app;
if (Array.isArray(app)) {
downstream = compose(app)
downstream = compose(app);
} else if (app.middleware) {
downstream = compose(app.middleware)
downstream = compose(app.middleware);
}

// don't need to do mounting here
if (prefix === '/') return downstream
if (prefix === '/') return downstream;

const trailingSlash = prefix.slice(-1) === '/'
const trailingSlash = prefix.slice(-1) === '/';

const name = app.name || 'unnamed'
debug('mount %s %s', prefix, name)
const name = app.name || 'unnamed';
debug('mount %s %s', prefix, name);

return async function (ctx, upstream) {
const prev = ctx.path
const newPath = match(prev)
debug('mount %s %s -> %s', prefix, name, newPath)
if (!newPath) return await upstream()
const prev = ctx.path;
const newPath = match(prev);
debug('mount %s %s -> %s', prefix, name, newPath);
if (!newPath) return upstream();

ctx.mountPath = prefix
ctx.path = newPath
debug('enter %s -> %s', prev, ctx.path)
ctx.mountPath = prefix;
ctx.path = newPath;
debug('enter %s -> %s', prev, ctx.path);

try {
await downstream(ctx, async () => {
ctx.path = prev
await upstream()
ctx.path = newPath
})
ctx.path = prev;
await upstream();
ctx.path = newPath;
});
} finally {
debug('leave %s -> %s', prev, ctx.path)
ctx.path = prev
debug('leave %s -> %s', prev, ctx.path);
ctx.path = prev;
}
}
};

/**
* Check if `prefix` satisfies a `path`.
Expand All @@ -85,15 +86,15 @@ function mount (prefix, app) {
* @api private
*/

function match (path) {
function match(path) {
// does not match prefix at all
if (path.indexOf(prefix) !== 0) return false
if (path.indexOf(prefix) !== 0) return false;

const newPath = path.replace(prefix, '') || '/'
if (trailingSlash) return newPath
const newPath = path.replace(prefix, '') || '/';
if (trailingSlash) return newPath;

// `/mount` does not match `/mountlkjalskjdf`
if (newPath[0] !== '/') return false
return newPath
if (newPath[0] !== '/') return false;
return newPath;
}
}
Loading