
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
What Require() should be.
eggnog is a simple, lightweight module and dependency injection framework for NodeJs.
Current Version: 0.6.0
eggnog is currently still in beta, and the API is still likely to change, though not much. If you use this and run into any questions/issues, feel free to create an issue on the Github page!
Here's what your NodeJs modules might look like:
// module.exports defines the metadata for your module: what it needs and how to initialize it
module.exports = {
locals: [ // local dependencies (local JS files, not packages defined in package.json)
'utils.logger',
'services.myService'
],
externals: [ // external (core or package.json) dependencies
'express', // from node_modules
'fs' // core module
],
globals: [
'console'
],
init: init
};
// By convention, we recommend having your init function separate.
// An eggnog object is passed to it, which provides a way to access the dependencies.
function init(eggnog) {
var log = eggnog.import('utils.logger');
var myService = eggnog.import('services.myService');
var express = eggnog.import('express');
var fs = eggnog.import('fs');
var console = eggnog.import('console');
...
eggnog.exports = {
// This is what your module.exports would have originally been
};
}
Importing dependencies with require() has several issues:
require('../../utils/logger')
is not uncommon. These are ugly and difficult to maintain.'utils.logger'
.'utils.logger'
.// module.exports defines the metadata for your module: what it needs and how to initialize it
module.exports = {
locals: [ // local dependencies (local JS files, not packages defined in package.json)
'utils.logger',
'services.myService'
],
externals: [ // external (core or package.json) dependencies
'express', // from node_modules
'fs' // core module
],
globals: [
'console'
],
init: init
};
// By convention, we recommend having your init function separate.
// An eggnog object is passed to it, which provides a way to access the dependencies.
function init(eggnog) {
var log = eggnog.import('utils.logger');
var myService = eggnog.import('services.myService');
var express = eggnog.import('express');
var fs = eggnog.import('fs');
var console = eggnog.import('console');
...
eggnog.exports = {
// This is what your module.exports would have originally been
};
}
Note the complete lack of require()
anywhere! You should no longer need to use require() in your code.
All dependencies listed in the imports in module.exports
will available on the eggnog object passed to the init() function via the import(id)
function. The init() function will only be called once all the imports have been resolved.
In this example, your logger utility is assumed to be in {root}/utils/logger.js, and so eggnog will automatically pick it up and make it available with the ID 'utils.logger'.
// server.js or app.js
var context = require('eggnog').newContext({
nodeModulesAt: __dirname // This is required if your app has dependencies in package.json
});
context.addDirectory(__dirname);
context.main();
This will scan for all JS files in the current directory (__dirname) and subdirectories, and add them to the context. At this point, none of the init() methods in any files have been run, as they are only evaluated at the point they need to be.
Note: The base directory name passed to scanForFiles() will never be used in the IDs of the modules. Otherwise, Eggnog would have no way of knowing which folders should be part of the ID. (This may be change in the future.)
The context.main()
method will attempt to start the application from the module that declared itself the main module. There can only be one of these in the context at a time, and an error will be thrown if a second one is added. The main module can be declared as such:
module.exports = {
isMain: true,
imports: [ ... ],
init: init
};
...
Only after calling context.main()
will the main module be loaded. This will load all of its dependencies first, and all of their dependencies, and so on until the app has been fully loaded. A module is considered loaded onces its init() method has been called once.
Individual modules can have either singleton
or instance
scoping.
Singleton scoping is the default, and means that only one copy of the module exists for the entire application. More precisely, the init() function will only be called once per context.
With instance scoping, the init() function will be run once for every module that depends upon it. Use this if you want a module to have state, but do not want it shared across the application.
Notes:
eggnog.import(id)
multiple times in the same module will always yield the same object.In the below example, each module that imports this module will get its own counter. If the scope were singleton (or not provided), then all modules that import this one would share the same counter.
module.exports = {
scope: 'instance',
init: init
};
function init(eggnog) {
var count = 0;
eggnog.exports = {
increment: function() { return count++; }
};
}
eggnog.import(id)
will always favor local dependencies, followed by externals, and then globals.eggnog.local(id)
, eggnog.external(id)
, or eggnog.global(id)
.function init(eggnog) {
// The one in the root directory of your app's source code
var localLogger = eggnog.local('logger');
// The one you define in project.json
var externalLogger = eggnog.external('logger');
var console = eggnog.global('console');
...
}
The dependency graph can be printed to console.log for a particular module, or for the specified main module in the context:
var context = ...
context.printDependencies('utils.logger');
// OR to print everything used by the app:
context.printDependencies(context.getMainModuleId());
Because each module defines its dependencies, but not how to find them, it is possible to manually inject mock dependencies into a single module, and then run tests on that module. eggnog makes this easy and simple:
var eggnog = require('eggnog');
var context = eggnog.newSingleModuleContext(__dirname + '/myApp');
// Assume the file we want to test is 'services.myService'
// Assume it has a dependency on 'daos.userDao', 'fs', and 'console'
var service = context.buildModule('services.myService', {
locals: {
// When the service imports 'myApp.userDao', this object will be injected
'daos.userDao': {
getUser: function(userId) {
return { /* mock user object */ };
},
// other methods that service.js uses from userDao from the test...
},
externals: {
// When the service imports 'fs', this object will be injected
'fs': {
readdirSync: function(path) {
// this is the method that will be called when the service calls fs.readdirSync()
return ['testDirectory'];
}
}
},
globals: {
'cosole': {
log: function() { /* ignore */ }
}
}
});
// service now has the mocks injected to it, and tests can be run against it
Notes:
See this example app
'/home/joe/myapp/utils/logger'
is loaded with '/home/joe/myapp'
as the root, then the module ID becomes 'utils.logger'
.FAQs
What Require() should be
The npm package eggnog receives a total of 1 weekly downloads. As such, eggnog popularity was classified as not popular.
We found that eggnog demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.