Comparing version 2.1.2 to 2.1.3
var debugLoad = require('debug')('microboot:load') | ||
var debugLookup = require('debug')('microboot:lookup') | ||
var exists = require('fs').existsSync | ||
var readdirSync = require('fs').readdirSync | ||
var statSync = require('fs').statSync | ||
var join = require('path').join | ||
var basename = require('path').basename | ||
var resolve = require('path').resolve | ||
var glob = require('glob') | ||
var microloader = require('microloader') | ||
module.exports = { | ||
load: load, | ||
lookup: lookup | ||
} | ||
module.exports = load | ||
@@ -19,23 +9,19 @@ function load (paths) { | ||
if (!(paths instanceof Array)) { | ||
throw new Error('Error loading files; invalid "paths" argument', paths) | ||
} | ||
microloader(paths, { | ||
absolute: true | ||
}).forEach(function (file) { | ||
debugLoad('- loading "' + file + '"...') | ||
paths.forEach(function (path) { | ||
lookup(path).forEach(function (item) { | ||
debugLoad('- loading "' + item + '"...') | ||
var ret = require(file) | ||
var func = require(resolve(item)) | ||
if (typeof ret === 'function') { | ||
debugLoad('- loaded "' + file + '" and added' + (ret.name ? (' "' + ret.name + '"') : '') + ' to list') | ||
if (typeof func === 'function') { | ||
debugLoad('- loaded "' + item + '" and added' + (func.name ? (' "' + func.name + '"') : '') + ' to list') | ||
callbacks.push({ | ||
func: func, | ||
path: item | ||
}) | ||
} else { | ||
debugLoad('- loaded "' + item + '"') | ||
} | ||
}) | ||
callbacks.push({ | ||
func: ret, | ||
path: file | ||
}) | ||
} else { | ||
debugLoad('- loaded "' + ret + '"') | ||
} | ||
}) | ||
@@ -47,56 +33,1 @@ | ||
} | ||
function lookup (path) { | ||
debugLookup('- searching "' + path + '"') | ||
var files = [] | ||
var re = new RegExp('\\.js$') | ||
if (!exists(path)) { | ||
if (exists(path + '.js')) { | ||
path += '.js' | ||
} else { | ||
files = glob.sync(path) | ||
if (!files.length) { | ||
throw new Error('Cannot resolve path "' + path + '"') | ||
} | ||
for (var i = 0; i < files.length; i++) { | ||
if (!re.test(files[i]) || basename(files[i])[0] === '.') { | ||
files.splice(i--, 1) | ||
} else { | ||
debugLookup('- found "' + files[i] + '"') | ||
} | ||
} | ||
return files | ||
} | ||
} | ||
var stat = statSync(path) | ||
if (stat.isFile()) { | ||
return [path] | ||
} | ||
readdirSync(path).forEach(function (file) { | ||
file = join(path, file) | ||
var stat = statSync(file) | ||
if (stat.isDirectory()) { | ||
files = files.concat(lookup(file)) | ||
} | ||
if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') { | ||
return | ||
} | ||
debugLookup('- found "' + file + '"') | ||
files.push(file) | ||
}) | ||
return files | ||
} |
var debug = require('debug')('microboot') | ||
var load = require('./loader').load | ||
var load = require('./loader') | ||
var run = require('./run') | ||
@@ -4,0 +4,0 @@ |
{ | ||
"name": "microboot", | ||
"version": "2.1.2", | ||
"version": "2.1.3", | ||
"description": "Boot up your app in wee little modules with the help of glob.", | ||
@@ -13,4 +13,5 @@ "main": "index.js", | ||
"dependencies": { | ||
"debug": "^2.3.3", | ||
"glob": "^7.1.1" | ||
"debug": "^2.4.1", | ||
"glob": "^7.1.1", | ||
"microloader": "^0.4.1" | ||
}, | ||
@@ -17,0 +18,0 @@ "devDependencies": { |
@@ -32,2 +32,40 @@ # microboot | ||
## Uses | ||
I use this tool for booting APIs and microservices within our architecture. _microboot_ lends itself really well to this as each endpoint can be instantiated in a single, isolated file and the only configuration needed to hook these together is _microboot_. | ||
``` js | ||
// index.js | ||
require('microboot')(['endpoints']) | ||
``` | ||
``` js | ||
// connections/api.js | ||
const express = require('express') | ||
const api = express() | ||
api.listen(3000) | ||
module.exports = api | ||
``` | ||
``` js | ||
// endpoints/get/list.js | ||
const api = require('../../connections/api') | ||
module.exports = () => { | ||
api.get('/list', handler) | ||
} | ||
function handler (req, res) => {...} | ||
``` | ||
In that example: | ||
* `index.js` (our entry point) triggers _microboot_ to grab everything in the `endpoints` folder | ||
* `endpoints/get/list.js` is found and `require`d | ||
* `connections/api.js` is `require`d which set ups a single Express API | ||
* `endpoints/get/list.js` adds a GET endpoint | ||
* :tada: | ||
**While this is a very simple example and it could arguably be clearer having everything in a single file, the key here is _extensibility_. Having each endpoint separated into an individual file means adding or removing endpoints is as simple as adding or removing a file. No extra work needed.** | ||
## How it works | ||
@@ -34,0 +72,0 @@ |
@@ -7,187 +7,77 @@ /* global describe, it, expect */ | ||
it('should expose the "load" function', function () { | ||
expect(loader.load).to.be.a('function') | ||
expect(loader).to.be.a('function') | ||
}) | ||
it('should expose the "lookup" function', function () { | ||
expect(loader.lookup).to.be.a('function') | ||
it('should throw if not given an array or string for "paths"', function () { | ||
var err = 'invalid "paths" argument' | ||
expect(loader.bind(loader, {})).to.throw(err) | ||
expect(loader.bind(loader, null)).to.throw(err) | ||
expect(loader.bind(loader, 123)).to.throw(err) | ||
expect(loader.bind(loader)).to.throw(err) | ||
}) | ||
describe('-> load', function () { | ||
it('should throw if not given an array for "paths"', function () { | ||
var err = 'invalid "paths" argument' | ||
it('should order results by paths then callbacks', function () { | ||
var first = require(resolve('test/data/timings/first/1')) | ||
var second = require(resolve('test/data/timings/first/2')) | ||
var sidejob = require(resolve('test/data/timings/first/3/sidejob')) | ||
var third = require(resolve('test/data/timings/second/3')) | ||
var fourth = require(resolve('test/data/timings/second/4')) | ||
expect(loader.load.bind(loader.load, 'test')).to.throw(err) | ||
expect(loader.load.bind(loader.load, {})).to.throw(err) | ||
expect(loader.load.bind(loader.load, null)).to.throw(err) | ||
expect(loader.load.bind(loader.load, 123)).to.throw(err) | ||
expect(loader.load.bind(loader.load)).to.throw(err) | ||
}) | ||
var callbacks = loader([ | ||
'test/data/timings/second', | ||
'test/data/timings/first' | ||
]) | ||
it('should order results by paths then callbacks', function () { | ||
var first = require(resolve('test/data/timings/first/1')) | ||
var second = require(resolve('test/data/timings/first/2')) | ||
var sidejob = require(resolve('test/data/timings/first/3/sidejob')) | ||
var third = require(resolve('test/data/timings/second/3')) | ||
var fourth = require(resolve('test/data/timings/second/4')) | ||
expect(callbacks).to.be.an('array') | ||
expect(callbacks).to.have.lengthOf(5) | ||
var callbacks = loader.load([ | ||
'test/data/timings/second', | ||
'test/data/timings/first' | ||
]) | ||
expect(callbacks[0].func).to.equal(third) | ||
expect(callbacks[0].path).to.equal(resolve('test/data/timings/second/3.js')) | ||
expect(callbacks).to.be.an('array') | ||
expect(callbacks).to.have.lengthOf(5) | ||
expect(callbacks[1].func).to.equal(fourth) | ||
expect(callbacks[1].path).to.equal(resolve('test/data/timings/second/4.js')) | ||
expect(callbacks[0].func).to.equal(third) | ||
expect(callbacks[0].path).to.equal('test/data/timings/second/3.js') | ||
expect(callbacks[2].func).to.equal(first) | ||
expect(callbacks[2].path).to.equal(resolve('test/data/timings/first/1.js')) | ||
expect(callbacks[1].func).to.equal(fourth) | ||
expect(callbacks[1].path).to.equal('test/data/timings/second/4.js') | ||
expect(callbacks[3].func).to.equal(second) | ||
expect(callbacks[3].path).to.equal(resolve('test/data/timings/first/2.js')) | ||
expect(callbacks[2].func).to.equal(first) | ||
expect(callbacks[2].path).to.equal('test/data/timings/first/1.js') | ||
expect(callbacks[4].func).to.equal(sidejob) | ||
expect(callbacks[4].path).to.equal(resolve('test/data/timings/first/3/sidejob.js')) | ||
}) | ||
expect(callbacks[3].func).to.equal(second) | ||
expect(callbacks[3].path).to.equal('test/data/timings/first/2.js') | ||
it('should load all, but only add functions to run later', function () { | ||
var callbacks = loader(['test/data/errors']) | ||
expect(callbacks[4].func).to.equal(sidejob) | ||
expect(callbacks[4].path).to.equal('test/data/timings/first/3/sidejob.js') | ||
}) | ||
expect(global.testObj).to.be.an('object') | ||
expect(global.testObj.foo).to.equal('bar') | ||
it('should load all, but only add functions to run later', function () { | ||
var callbacks = loader.load(['test/data/errors']) | ||
expect(callbacks).to.be.an('array') | ||
expect(callbacks).to.have.lengthOf(2) | ||
expect(global.testObj).to.be.an('object') | ||
expect(global.testObj.foo).to.equal('bar') | ||
expect(callbacks[0].func).to.be.a('function') | ||
expect(callbacks[1].func).to.be.a('function') | ||
}) | ||
expect(callbacks).to.be.an('array') | ||
expect(callbacks).to.have.lengthOf(2) | ||
it('should return an empty array if no paths are given', function () { | ||
var callbacks = loader([]) | ||
expect(callbacks[0].func).to.be.a('function') | ||
expect(callbacks[1].func).to.be.a('function') | ||
}) | ||
it('should return an empty array if no paths are given', function () { | ||
var callbacks = loader.load([]) | ||
expect(callbacks).to.be.an('array') | ||
expect(callbacks).to.be.empty | ||
}) | ||
it('should return an array of callbacks if successful', function () { | ||
var callbacks = loader.load(['test/data/timings']) | ||
expect(callbacks).to.be.an('array') | ||
expect(callbacks).to.have.lengthOf(5) | ||
expect(callbacks[0].func).to.be.a('function') | ||
expect(callbacks[1].func).to.be.a('function') | ||
expect(callbacks[2].func).to.be.a('function') | ||
expect(callbacks[3].func).to.be.a('function') | ||
expect(callbacks[4].func).to.be.a('function') | ||
}) | ||
expect(callbacks).to.be.an('array') | ||
expect(callbacks).to.be.empty | ||
}) | ||
describe('-> lookup', function () { | ||
it('should throw if given a non-existent path', function () { | ||
expect(loader.lookup.bind(loader.lookup, '/test/data/does/not/exist')).to.throw('Cannot resolve path') | ||
}) | ||
it('should return an array of callbacks if successful', function () { | ||
var callbacks = loader(['test/data/timings']) | ||
it('should find a single JS file if given an extensionless path', function () { | ||
var files = loader.lookup('test/data/fake/database') | ||
expect(callbacks).to.be.an('array') | ||
expect(callbacks).to.have.lengthOf(5) | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(1) | ||
expect(files[0]).to.equal('test/data/fake/database.js') | ||
}) | ||
it('should not return hidden files', function () { | ||
var files = loader.lookup('test/data/fake') | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(2) | ||
expect(files).to.not.contain('test/data/fake/.hidden.js') | ||
}) | ||
it('should not return files that don\'t have the ".js" extension', function () { | ||
var files = loader.lookup('test/data/fake') | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(2) | ||
expect(files).to.not.contain('test/data/fake/test.jpg') | ||
}) | ||
it('should remove hidden files when searching an extensionless path', function () { | ||
var files = loader.lookup('test/data/fake/**') | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(2) | ||
expect(files).to.not.contain('test/data/fake/.hidden.js') | ||
}) | ||
it('should remove non-.js files when searching an extensionless path', function () { | ||
var files = loader.lookup('test/data/fake/**') | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(2) | ||
expect(files).to.not.contain('test/data/fake/test.jpg') | ||
}) | ||
it('should return files ordered', function () { | ||
var files = loader.lookup('test/data/timings') | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(5) | ||
expect(files[0]).to.equal('test/data/timings/first/1.js') | ||
expect(files[1]).to.equal('test/data/timings/first/2.js') | ||
expect(files[2]).to.equal('test/data/timings/first/3/sidejob.js') | ||
expect(files[3]).to.equal('test/data/timings/second/3.js') | ||
expect(files[4]).to.equal('test/data/timings/second/4.js') | ||
}) | ||
it('should find a single JS file if given a specific file', function () { | ||
var files = loader.lookup('test/data/timings/second/3.js') | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(1) | ||
expect(files[0]).to.equal('test/data/timings/second/3.js') | ||
}) | ||
it('should recursively search directories if just a path name given', function () { | ||
var files = loader.lookup('test/data/timings') | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(5) | ||
expect(files[0]).to.equal('test/data/timings/first/1.js') | ||
expect(files[1]).to.equal('test/data/timings/first/2.js') | ||
expect(files[2]).to.equal('test/data/timings/first/3/sidejob.js') | ||
expect(files[3]).to.equal('test/data/timings/second/3.js') | ||
expect(files[4]).to.equal('test/data/timings/second/4.js') | ||
}) | ||
it('should recursively search directories if path name and "**" given', function () { | ||
var files = loader.lookup('test/data/timings/**') | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(5) | ||
expect(files[0]).to.equal('test/data/timings/first/1.js') | ||
expect(files[1]).to.equal('test/data/timings/first/2.js') | ||
expect(files[2]).to.equal('test/data/timings/first/3/sidejob.js') | ||
expect(files[3]).to.equal('test/data/timings/second/3.js') | ||
expect(files[4]).to.equal('test/data/timings/second/4.js') | ||
}) | ||
it('should not recursively search directories if "*" given after path', function () { | ||
var files = loader.lookup('test/data/timings/first/*') | ||
expect(files).to.be.an('array') | ||
expect(files).to.have.lengthOf(2) | ||
expect(files[0]).to.equal('test/data/timings/first/1.js') | ||
expect(files[1]).to.equal('test/data/timings/first/2.js') | ||
}) | ||
expect(callbacks[0].func).to.be.a('function') | ||
expect(callbacks[1].func).to.be.a('function') | ||
expect(callbacks[2].func).to.be.a('function') | ||
expect(callbacks[3].func).to.be.a('function') | ||
expect(callbacks[4].func).to.be.a('function') | ||
}) | ||
}) |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
51095
27
207
8
3
323
+ Addedmicroloader@^0.4.1
+ Addedlodash@4.17.21(transitive)
+ Addedmicroloader@0.4.2(transitive)
Updateddebug@^2.4.1