From 238b4bdf267496019577210e36342e660e3f5735 Mon Sep 17 00:00:00 2001 From: Luke Opperman Date: Mon, 3 Feb 2020 12:51:56 -0500 Subject: [PATCH 1/5] Support nested modules with namespaced plugin/proxy/log names --- src/NxusModule.js | 69 +++++++++++++++---- src/PluginManager.js | 15 ++-- test/lib/NxusModule.js | 24 +++++++ test/lib/modules/one/index.js | 5 ++ .../one/modules/sub/controllers/nested.js | 5 ++ test/lib/modules/one/modules/sub/index.js | 14 ++++ 6 files changed, 113 insertions(+), 19 deletions(-) create mode 100644 test/lib/modules/one/index.js create mode 100644 test/lib/modules/one/modules/sub/controllers/nested.js create mode 100644 test/lib/modules/one/modules/sub/index.js diff --git a/src/NxusModule.js b/src/NxusModule.js index 8d395b8..3c5c092 100644 --- a/src/NxusModule.js +++ b/src/NxusModule.js @@ -1,17 +1,47 @@ import morph from 'morph' import path from 'path' +import fs from 'fs' import stackTrace from 'stack-trace' import ModuleProxy from './ModuleProxy' import {application} from './Application' import Logger from './Logger' import deepExtend from 'deep-extend' -function __dirName(constructorName) { +function _filenameOf(construct) { + for (let [k,v] of Object.entries(require.cache)) { + let exports = v.exports.default ? v.exports : {default: v.exports} + for (let [,ex] of Object.entries(exports)) { + if (ex == construct) { + return v.filename + } + } + } for (let site of stackTrace.get()) { - if(site.getFunctionName() == constructorName) { - return path.dirname(site.getFileName()) + if(site.getFunctionName() == construct.name) { + return site.getFileName() + } + } +} + +const EXCLUDE_DIRNAMES = ['src', 'lib', 'test', 'modules'] + +function _modulePrefix(filename) { + let dirname = path.dirname(filename) + let dirs = dirname.split(path.sep) + let result = [] + let root + while (!root && dirs.length) { + let testFile = dirs.concat(['package.json']).join(path.sep) + if (fs.existsSync(testFile)) { + root = true + } else { + let p = dirs.pop() + if (!EXCLUDE_DIRNAMES.includes(p)) { + result.unshift(p) + } } } + return result.join('/') } /** @@ -23,8 +53,9 @@ function __dirName(constructorName) { class NxusModule { constructor(app) { + this._dirName = path.dirname(_filenameOf(this.constructor)) this.__name = this.constructor._moduleName() - this.__config_name = this.constructor._configName() + this.__config_name = this.constructor._configName(this.__name) this.log = Logger(this.__name) let userConfig = this._userConfig() @@ -32,15 +63,18 @@ class NxusModule { application.setUserConfig(this.__config_name, userConfig) } - this._dirName = __dirName(this.constructor.name) - this.__proxy = application.get(this.__name) this.__proxy.use(this) } get config() { let _defaultConfig = this._defaultConfig() || {} - if(!this._config) this._config = Object.assign({}, deepExtend(_defaultConfig, application.config[this.__config_name])) + if (!this._config) { + this._config = Object.assign( + {}, + deepExtend(_defaultConfig, application.config[this.__config_name]) + ) + } return this._config } @@ -60,16 +94,27 @@ class NxusModule { return application } - static _configName() { - return morph.toSnake(this.name) + static _configName(name) { + return morph.toSnake(name) } - static _moduleName() { - return morph.toDashed(this.name) + static _moduleName(filename) { + if (filename === undefined) { + filename = _filenameOf(this) + } + let useMyName = path.basename(filename) != 'index.js' + let name = _modulePrefix(filename) + // this logic of ignoring class name for modules + // is in part to match src/PluginManager:_loadModulesFromDirectory + if (useMyName) { + name = name ? name + "/" + this.name : this.name + } + return morph.toDashed(name) } static getProxy() { - return application.get(this._moduleName()) + // force to caller file, assuming getProxy is called in same file as class definition + return application.get(this._moduleName(stackTrace.get()[1].getFileName())) } deregister() { diff --git a/src/PluginManager.js b/src/PluginManager.js index 5fdf199..80a8319 100644 --- a/src/PluginManager.js +++ b/src/PluginManager.js @@ -89,7 +89,7 @@ class PluginManager { * @param {[type]} matches [description] * @return {[type]} [description] */ - _loadModulesFromDirectory(dir, isLocal, matches) { + _loadModulesFromDirectory(dir, isLocal, matches, prefix='') { if (!fs.existsSync(dir)) return var moduleDirs = fs.readdirSync(dir) @@ -97,17 +97,18 @@ class PluginManager { moduleDirs.forEach((name) => { if(matches) name = multimatch([name], matches)[0] if(!name || (name && name[0] == ".")) return - this.app.log.debug('Loading module', name, isLocal ? "(app)": "(dep)") + let modName = prefix+name + this.app.log.debug('Loading module', modName, isLocal ? "(app)": "(dep)") let modulePath = path.resolve(path.join(dir, name)) try { var pkg = require(modulePath) - pkg._pluginInfo = {name, modulePath, isLocal} + pkg._pluginInfo = {name: modName, modulePath, isLocal} this.packages.push(pkg) + // this module prefix naming matches src/NxusModule:_moduleName we hope + let newPrefix = modName+"/" // Recurse for module modules - this._loadModulesFromDirectory(path.join(dir, name, 'modules'), isLocal) - this._loadModulesFromDirectory(path.join(dir, name, 'lib', 'modules'), isLocal) - //if(fs.existsSync(dir + "/" + name + "/node_modules")) - //this._loadModulesFromDirectory(dir + "/" + name + "/node_modules", matches) + this._loadModulesFromDirectory(path.join(dir, name, 'modules'), isLocal, newPrefix) + this._loadModulesFromDirectory(path.join(dir, name, 'lib', 'modules'), isLocal, newPrefix) } catch (e) { // kludgy message text match to distinguish subsidiary modules from primary if ((e.code === 'MODULE_NOT_FOUND') && e.message.includes(`'${modulePath}'`)) diff --git a/test/lib/NxusModule.js b/test/lib/NxusModule.js index 2e9c588..39a3a6f 100644 --- a/test/lib/NxusModule.js +++ b/test/lib/NxusModule.js @@ -8,6 +8,9 @@ import {NxusModule, application} from '../../lib/' +import OneModule from './modules/one' +import NestedSub from './modules/one/modules/sub' + class SubModule extends NxusModule { _defaultConfig() { @@ -40,6 +43,8 @@ describe("NxusModule", () => { it("should set itself in app modules with the config name (dashed)", () => { application._moduleProxies.should.have.property('sub-module') + // NestedSub + application._moduleProxies.should.have.property('one/sub') }) it("should set defaultConfig", () => { @@ -78,4 +83,23 @@ describe("NxusModule", () => { }) }) + describe("_moduleName", () => { + it("should be the module name dashed", () => { + // SubModule isn't exported so not available + //SubModule._moduleName().should.equal('sub-module') + instance.__name.should.equal('sub-module') + }) + it("should use dir name for index module", () => { + OneModule._moduleName().should.equal('one') + }) + it("should include nested directories and use dir name for index", () => { + NestedSub._moduleName().should.equal('one/sub') + }) + it("should include nested directories for arbitrary internal modules", () => { + let n = new NestedSub() + n.controller.__name.should.equal('one/sub/controllers/nested-controller') + application._moduleProxies.should.have.property('one/sub/controllers/nested-controller') + }) + }) + }) diff --git a/test/lib/modules/one/index.js b/test/lib/modules/one/index.js new file mode 100644 index 0000000..c0534b7 --- /dev/null +++ b/test/lib/modules/one/index.js @@ -0,0 +1,5 @@ +import {NxusModule} from '../../../../lib' + +export default class OneModule extends NxusModule { + +} diff --git a/test/lib/modules/one/modules/sub/controllers/nested.js b/test/lib/modules/one/modules/sub/controllers/nested.js new file mode 100644 index 0000000..f2e67a9 --- /dev/null +++ b/test/lib/modules/one/modules/sub/controllers/nested.js @@ -0,0 +1,5 @@ +import {NxusModule} from '../../../../../../../lib' + +export default class NestedController extends NxusModule { + +} diff --git a/test/lib/modules/one/modules/sub/index.js b/test/lib/modules/one/modules/sub/index.js new file mode 100644 index 0000000..908f590 --- /dev/null +++ b/test/lib/modules/one/modules/sub/index.js @@ -0,0 +1,14 @@ +import {NxusModule} from '../../../../../../lib' + +import NestedController from './controllers/nested' + +class NestedSub extends NxusModule { + constructor() { + super() + this.controller = new NestedController() + } + +} + +let nestedSub = NestedSub.getProxy() +export {NestedSub as default, nestedSub} From f43844d5e3f14eeffe7e5877fd05f69b1a6b7652 Mon Sep 17 00:00:00 2001 From: Luke Opperman Date: Mon, 3 Feb 2020 13:06:00 -0500 Subject: [PATCH 2/5] Fix configName for module disabling, use _moduleName in bootPlugin --- src/Application.js | 14 ++++++++------ src/NxusModule.js | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Application.js b/src/Application.js index ed878be..b1cef17 100755 --- a/src/Application.js +++ b/src/Application.js @@ -367,17 +367,19 @@ export default class Application extends Dispatcher { * @return {[type]} */ _bootPlugin(plugin) { - var name = plugin._pluginInfo.name - let path = plugin._pluginInfo.modulePath + let pluginInfo = plugin._pluginInfo + + if(plugin.default) + plugin = plugin.default + + let name = (plugin._moduleName && plugin._moduleName()) || pluginInfo.name let pluginInstance = null let promise = null - //if (this.config.debug) console.log(' ------- ', plugin) + if (this._pluginInstances[name] !== undefined) { this.log.error('Duplicate module found', name) process.exit() } - if(plugin.default) - plugin = plugin.default if(plugin.__appRef && plugin.__appRef() !== this) { this.log.error('Separate Nxus Core module detected in', name) @@ -393,7 +395,7 @@ export default class Application extends Dispatcher { this._pluginInstances[name] = pluginInstance promise = Promise.resolve(pluginInstance) } catch(e) { - this.log.error('Error booting module '+name+' from '+path, e) + this.log.error('Error booting module '+name+' from '+pluginInfo.modulePath, e) this.log.error(e.stack) process.exit() } diff --git a/src/NxusModule.js b/src/NxusModule.js index 3c5c092..af7f749 100644 --- a/src/NxusModule.js +++ b/src/NxusModule.js @@ -55,7 +55,7 @@ class NxusModule { constructor(app) { this._dirName = path.dirname(_filenameOf(this.constructor)) this.__name = this.constructor._moduleName() - this.__config_name = this.constructor._configName(this.__name) + this.__config_name = this.constructor._configName() this.log = Logger(this.__name) let userConfig = this._userConfig() @@ -94,8 +94,8 @@ class NxusModule { return application } - static _configName(name) { - return morph.toSnake(name) + static _configName() { + return morph.toSnake(this._moduleName()) } static _moduleName(filename) { From 31c5815ba49ebeaa8b7fc3b71d90f8cce61b056a Mon Sep 17 00:00:00 2001 From: Luke Opperman Date: Mon, 3 Feb 2020 13:38:39 -0500 Subject: [PATCH 3/5] typo on loadModulesFromDirectory recursion --- src/PluginManager.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PluginManager.js b/src/PluginManager.js index 80a8319..20ae86a 100644 --- a/src/PluginManager.js +++ b/src/PluginManager.js @@ -90,6 +90,7 @@ class PluginManager { * @return {[type]} [description] */ _loadModulesFromDirectory(dir, isLocal, matches, prefix='') { + if (!fs.existsSync(dir)) return var moduleDirs = fs.readdirSync(dir) @@ -107,8 +108,8 @@ class PluginManager { // this module prefix naming matches src/NxusModule:_moduleName we hope let newPrefix = modName+"/" // Recurse for module modules - this._loadModulesFromDirectory(path.join(dir, name, 'modules'), isLocal, newPrefix) - this._loadModulesFromDirectory(path.join(dir, name, 'lib', 'modules'), isLocal, newPrefix) + this._loadModulesFromDirectory(path.join(dir, name, 'modules'), isLocal, null, newPrefix) + this._loadModulesFromDirectory(path.join(dir, name, 'lib', 'modules'), isLocal, null, newPrefix) } catch (e) { // kludgy message text match to distinguish subsidiary modules from primary if ((e.code === 'MODULE_NOT_FOUND') && e.message.includes(`'${modulePath}'`)) From cb6c4d5576ae80f20b9c6d11a12cdc1df656656c Mon Sep 17 00:00:00 2001 From: Luke Opperman Date: Mon, 3 Feb 2020 14:24:43 -0500 Subject: [PATCH 4/5] Fix module naming to work for core modules that rely on module class name not directory --- src/NxusModule.js | 13 +++++++------ test/lib/NxusModule.js | 8 ++++---- test/lib/modules/one/index.js | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/NxusModule.js b/src/NxusModule.js index af7f749..f0b3d4c 100644 --- a/src/NxusModule.js +++ b/src/NxusModule.js @@ -9,7 +9,7 @@ import deepExtend from 'deep-extend' function _filenameOf(construct) { for (let [k,v] of Object.entries(require.cache)) { - let exports = v.exports.default ? v.exports : {default: v.exports} + let exports = v.exports && v.exports.default ? v.exports : {default: v.exports} for (let [,ex] of Object.entries(exports)) { if (ex == construct) { return v.filename @@ -28,6 +28,7 @@ const EXCLUDE_DIRNAMES = ['src', 'lib', 'test', 'modules'] function _modulePrefix(filename) { let dirname = path.dirname(filename) let dirs = dirname.split(path.sep) + let isIndex = path.basename(filename) == 'index.js' let result = [] let root while (!root && dirs.length) { @@ -41,6 +42,9 @@ function _modulePrefix(filename) { } } } + if (isIndex) { + result.pop() + } return result.join('/') } @@ -102,13 +106,10 @@ class NxusModule { if (filename === undefined) { filename = _filenameOf(this) } - let useMyName = path.basename(filename) != 'index.js' - let name = _modulePrefix(filename) + let prefix = _modulePrefix(filename) // this logic of ignoring class name for modules // is in part to match src/PluginManager:_loadModulesFromDirectory - if (useMyName) { - name = name ? name + "/" + this.name : this.name - } + let name = prefix ? prefix + "/" + this.name : this.name return morph.toDashed(name) } diff --git a/test/lib/NxusModule.js b/test/lib/NxusModule.js index 39a3a6f..0e44a2f 100644 --- a/test/lib/NxusModule.js +++ b/test/lib/NxusModule.js @@ -44,7 +44,7 @@ describe("NxusModule", () => { it("should set itself in app modules with the config name (dashed)", () => { application._moduleProxies.should.have.property('sub-module') // NestedSub - application._moduleProxies.should.have.property('one/sub') + application._moduleProxies.should.have.property('one/nested-sub') }) it("should set defaultConfig", () => { @@ -89,11 +89,11 @@ describe("NxusModule", () => { //SubModule._moduleName().should.equal('sub-module') instance.__name.should.equal('sub-module') }) - it("should use dir name for index module", () => { + it("should not duplicate module name when index module", () => { OneModule._moduleName().should.equal('one') }) - it("should include nested directories and use dir name for index", () => { - NestedSub._moduleName().should.equal('one/sub') + it("should include nested directories and use module class name", () => { + NestedSub._moduleName().should.equal('one/nested-sub') }) it("should include nested directories for arbitrary internal modules", () => { let n = new NestedSub() diff --git a/test/lib/modules/one/index.js b/test/lib/modules/one/index.js index c0534b7..ea317d0 100644 --- a/test/lib/modules/one/index.js +++ b/test/lib/modules/one/index.js @@ -1,5 +1,5 @@ import {NxusModule} from '../../../../lib' -export default class OneModule extends NxusModule { +export default class One extends NxusModule { } From fbc804a452b0f1cbfc1215c1913ff49b12f2c8a1 Mon Sep 17 00:00:00 2001 From: Luke Opperman Date: Fri, 10 Apr 2020 13:45:33 -0400 Subject: [PATCH 5/5] more node versions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 584d472..0e549fd 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ }, "homepage": "https://github.com/nxus/core", "engines": { - "node": "~6" + "node": "~6,~8,~10~,~12" }, "dependencies": { "async": "^2.0.1",