Skip to content

Commit

Permalink
[update] speed improvements and code re-factor to use getters and set…
Browse files Browse the repository at this point in the history
…ters.
  • Loading branch information
calvintwr committed May 17, 2020
1 parent 0993e93 commit 050c28a
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 85 deletions.
250 changes: 185 additions & 65 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Express Route-Magic v0.0.3
* Express Route-Magic v0.2.0
* (c) 2020 Calvin Tan
* Released under the MIT License.
*/
Expand All @@ -8,62 +8,129 @@
// modules
const fs = require('fs')
const path = require('path')
const util = require('util')

// defaults
const Magic = {
_moduleName: 'express-routemagic',
_routesFolder: 'routes',
_allowSameName: false,
_debug: console.log,
_logMapping: false
}

const Magic = {}

Magic.ignoreSuffix = null
Magic.routeFolder = null
Magic.allowSameName = false
Magic.debug = console.log
Magic.printRoutes = false
Magic.invokerPath = null
// properties that require getters and setters
Object.defineProperties(Magic, {

invokerPath: {
get() {
return this._invokerPath
},
set(val) {
let fail = _argFail('string', val, 'invokerPath', 'The path of where you invoked magic must be a valid `string` and passed in as 2nd argument. Typically it is `__dirname`.')
if (fail) throw new Error(fail)
this._invokerPath = val
}
},

routesFolder: {
get() {
return this._routesFolder
},
set(val) {
let fail = _argFail('string', val, 'routesFolder', 'This value defaults to \'routes\'. If you change your folder structure to follow you won\'t need this option.')
if (fail) throw new Error(fail)
this._routesFolder = val
}
},

ignoreSuffix: {
get() {
return this._ignoreSuffix
},
set(val) {
let fail = _argFail(['string', 'array'], val, 'ignoreSuffix')
if (fail) throw new Error(fail)
this._ignoreSuffix = Array.isArray(val) ? val : [val]
}
},

allowSameName: {
get() {
return this._allowSameName
},
set(val) {
let fail = _argFail('boolean', val, 'allowSameName')
if (fail) throw new Error(fail)
this._allowSameName = val
}
},

logMapping: {
get() {
return this._logMapping
},
set(val) {
let fail = _argFail('boolean', val, 'logMapping')
if (fail) throw new Error(fail)
this._logMapping = val
}
},

debug: {
get() {
return this._debug
},
set(val) {
let fail = _argFail('function', val, 'debug')
if (fail) throw new Error(fail)
this._debug = val
}
}
})

Magic.use = function (app, invokerPath, options) {

// methods
Magic.use = function(app, invokerPath, options) {
if (!app) throw new Error('Invalid argument: Express `app` instance must be passed in as 1st argument.')

Magic.app = app

if (typeof invokerPath !== 'string') throw new Error('Invalid argument: The path of where you invoked magic must be a valid `string` and passed in as 2nd argument. Typically it is `__dirname`.')
this.app = app
this.invokerPath = invokerPath

if (typeof options === 'string') this.routeFolder = options
if (typeof options === 'string') this.routesFolder = options

if (typeof options === 'object') {
// may need debugging module downstream, so assign first.
if (options.debug) this.debug = options.debug

if (!options.routeFolder || typeof options.routeFolder !== 'string') {
throw new Error('Invalid argument: `routeFolder` must be a valid `string`.')
}
this.routeFolder = options.routeFolder

if (options.ignoreSuffix) {
if (typeof options.ignoreSuffix === 'string') {
this.ignoreSuffix = [options.ignoreSuffix]
} else if (Array.isArray(options.ignoreSuffix)) {
this.ignoreSuffix = options.ignoreSuffix
} else {
throw new Error('`Invalid argument`: `ignoreSuffix` must either be `string` or `array`.')
}

}
/* All BCs */

if (options.allowSameName) {
if (typeof options.allowSameName !== 'boolean') throw new Error('Invalid argument: `allowSameName` must be a valid `boolean`.')
this.allowSameName = true
}
// BC >= 0.0.1, since 0.2.0
_depre({
options,
old: 'routeFolder',
new: 'routesFolder'
})

if (options.debug) this.debug = options.debug
// BC >= 0.0.1, since 0.2.0
_depre({
options,
old: 'printRoutes',
new: 'logMapping'
})

if (options.printRoutes) {
if (typeof options.printRoutes !== 'boolean') throw new Error('Invalid argument: `printRoutes` must be a valid `boolean`.')
this.printRoutes = true
}
_applyOpts(this, options, [
'routesFolder',
'ignoreSuffix',
'allowSameName',
'debug',
'logMapping'
])
}
this.scan(path.join(invokerPath, this.routeFolder))
this.scan(path.join(invokerPath, this.routesFolder))
}

Magic.scan = function (directory) {

Magic.scan = function(directory) {
let _folders = []
let _files = []

Expand All @@ -82,13 +149,11 @@ Magic.scan = function (directory) {
return (file.indexOf('.js') === file.length - '.js'.length)

}).forEach(file => {

if (file === 'index.js') {
_files.unshift(file)
} else {
this.push(_files, file)
}

})

this.checkConflict(_files, _folders, directory)
Expand All @@ -102,51 +167,106 @@ Magic.scan = function (directory) {
})
}

Magic.push = function (array, payload, isDirectory) {
Magic.push = function(array, payload, isDirectory) {
if (!this.toIgnore(payload, isDirectory)) array.push(payload)
}

Magic.toIgnore = function (payload, isDirectory) {

Magic.toIgnore = function(payload, isDirectory) {
if (!isDirectory) payload = payload.replace('.js', '')

let toIgnore = false

if(this.ignoreSuffix !== null){
if (this.ignoreSuffix) {
this.ignoreSuffix.forEach(suffix => {
if (payload.indexOf(suffix) !== -1 && payload.indexOf(suffix) === payload.length - suffix.length) {
toIgnore = true
return null
}
})
}

return toIgnore
}

Magic.checkConflict = function (files, folders, directory) {
Magic.checkConflict = function(files, folders, directory) {
if (this.allowSameName) return false
files.forEach(file => {
if (folders.indexOf(file.replace('.js', '')) !== -1) throw new Error('Folder and file with conflict name: `' + file.replace('.js', '') + '` in directory: `' + directory + '`.')
if (folders.indexOf(file.replace('.js', '')) !== -1) throw new Error(`Folder and file with conflict name: \`${file.replace('.js', '')}\` in directory: \`${directory}\`.`)
})
}

Magic.require = function (directory, files) {
Magic.require = function(directory, files) {
let apiPath = directory.replace(path.join(this.invokerPath, this.routesFolder), '') + '/'
files.forEach(file => {
let subPath = (file === 'index.js') ? apiPath : apiPath + file.replace('.js', '')
let pathRelativeToInvoker = path.join(directory, file).replace(this.invokerPath, '')

let apiPath = directory.replace(path.join(this.invokerPath, this.routeFolder), '') + '/'
this.app.use(subPath, require('./../..' + pathRelativeToInvoker))
if (this.logMapping) this.debug(subPath + ' => .' + pathRelativeToInvoker)
})
}

files.forEach(file => {
if (file === 'index.js') {
let pathRelativeToInvoker = path.join(directory, file).replace(this.invokerPath, '')
this.app.use(apiPath, require('./../..' + pathRelativeToInvoker))
if (this.printRoutes) this.debug(apiPath + ' => ' + path.join(directory, file).replace(this.invokerPath, '.'))
} else {
let pathRelativeToInvoker = path.join(directory, file).replace(this.invokerPath, '')
let subPath = apiPath + file.replace('.js', '')
this.app.use(subPath, require('./../..' + pathRelativeToInvoker))
if (this.printRoutes) this.debug(subPath + ' => ' + path.join(directory, file).replace(this.invokerPath, '.'))
}
// localised helpers
function _argFail(expect, got, name, note) {
if (!Array.isArray(expect)) expect = [expect]
got = _type(got)
if (_found(got)) return false
return _msg()

function _found(got) {
let found = expect.find(el => _vet(el) === got)
return typeof found !== 'undefined'
}

function _msg() {
let msg = 'Invalid Argument'
msg += name ? ' ' + name : ''
msg += `: Expect type ${_list(expect)} but got \`${got}\`.`
msg += note ? ` Note: ${note}` : ''
return msg
}

function _vet(el) {
const valid = [
'string',
'number',
'array',
'object',
'function',
'boolean',
'null',
'undefined'
// no support for symbol. should we care?
]
if (typeof el !== 'string') throw new Error(`Internal error: Say what you expect to check in string. Not ${el} with type \`${typeof el}\`.`)
if (valid.indexOf(el) === -1) throw new Error(`Internal error: \`${el}\` is not a valid type to check for. Please use only ${_list(valid)}.`)
return el
}

function _list(array) {
return array.map(el => {
return `\`${el}\``
}).join(' or ')
}

// get rid of all the problems typeof [] is `object`.
function _type(got) {
if (typeof got !== 'object') return typeof got
if (Array.isArray(got)) return 'array'
if (got === null) return 'null'
return 'object'
}
}

function _applyOpts(obj, opts, props) {
props.forEach(prop => {
if (opts[prop] !== undefined) obj[prop] = opts[prop]
})
}

function _depre(obj) {
if (typeof obj.options[obj.old] !== 'undefined') {
util.deprecate(() => {
obj.options[obj.new] = obj.options[obj.old]
}, `\`${obj.old}\` is deprecated. Please use \`${obj.new}\` instead.`)()
}
}

module.exports = Object.create(Magic)
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "express-routemagic",
"version": "0.1.0",
"description": "A simple and fast module to automatically require all your express routes without bloating your code without `app.use('i/will/repeat/this', require('./i/will/repeat/this')`.",
"version": "0.2.0",
"description": "A simple and fast module to automatically require all your express routes without bloating your code without `app.use('i/will/repeat/this', require('./i/will/repeat/this')`. 把 Express 路由图给自动化。",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
Expand All @@ -14,12 +14,15 @@
"express",
"router",
"automatic",
"require"
"require",
"路由",
"路由图"
],
"author": "calvintwr",
"license": "MIT",
"bugs": {
"url": "https://github.com/calvintwr/express-routemagic/issues"
},
"homepage": "https://github.com/calvintwr/express-routemagic#readme"
"homepage": "https://github.com/calvintwr/express-routemagic#readme",
"dependencies": {}
}
Loading

0 comments on commit 050c28a

Please sign in to comment.