@wider/registry
v1.0.10
Published
A WIDER foundation component, registering and validating members of other @WIDER public solutions
Downloads
5
Readme
@wider/registry
WIDER modules make significant use of each other. They are published individually as many are of use to the community on a stand alone basis.
The way npm works, it is possible for the same module to be loaded multiple times (same or different versions) from different installed packages within a project and thus if they have what is intended to be working data that spans all activity within the project then the integrity required of a single instance for you thread/cluster is lost. This issue is well documented in chatrooms and is the reason this module was created - to stop it happening or in despair let you know it is happening.
Any module that wants to ensure that it is instantiated only once (per thread/cluster) may invoke this tool which does the following
- ensures that this tool itself is not loaded multiple times
- throws an
Error.code === "INVALID_MODULE_NAME"
if the request does not register a valid module as required for this package to operate - detects if the given module has already been loaded and throws an
Error.code === "DUPLICATE_CALL"
if this is a duplicate.
Errors are logged to the console before being thrown. You can choose to handle the error or let it bubble so that the app exits. All errors have a property called .code
which is a programming oriented code if your app wishes to analyse problems.
You can register ES and commonJS modules.
Internally a registry is maintained. You can optionally use this registry as a bundle of your own modules so you only need a single require or import for the bulk of your applications. For commonJS modules you can indicate that they are to be loaded just in time, if and only if they are used. This feature is entirely transparent to your code accessing those modules - it enables you to place large numbers of modules in your package without adverse run time bloat if they are not used.
While there is a small overhead to create a bundle, both you as developer and the runtime processor save time if there are multiple uses of the bundle.
If you have the same problems then this will assist you to identify when the problem arises. Used basically as a registry then you add some lines of code to each module - there is no other impact to your code unless an issue is identified. If you use the advanced features you can avoid using import/require and access your registered modules directly from a cache.
There are reports that are produced in markdown format for you to include in your documentation.
TO COME as separate packages making use of the registry
- a package builder and publisher that deduces from the registry the dependency order to rebuild packages and publish them to the extent required after your code changes
- a documentation website builder that integrates the jsDoc components of each of your packages into a solution wide information source
Documentation
Install
npm install @wider/utils_require
Basic Use
Each module you wish to manage should self register. If the module has not been loaded before then it is added to the list of loaded modules. You can use register.describe_loaded()
to get a list of these and also a note of the time from the start of the application that the module load was completed. Place the following code at the start of your module, or at least before it starts to create any side effects.
//ES
import register from "@wider/registry";
const $moduleName = "my-module-name";
register($moduleName);
// commonJS
const register = require("@wider/registry");
const $moduleName = "my-module-name";
register($moduleName);
Soft registration
With this mode, you do not add the registry to your dependencies at all nor do you import or require it (ie you skip the first line in each example above). Your module package can be distributed without any dependency to this package. If another package loads the registry, such as the applications main module, then your package will register - otherwise nothing happens. Put this at the very start of your code
process.nextTick(()) => { if (global.$wider) global.$wider.registry.register( $moduleName )};
Advanced use
With advanced use, you also provide your object that is being registered and may choose to influence where how is cached and accessed. We illustrate creating the object using require but any means may be used to create the value to be processed including import for es modules
module.exports = whatEverYouCreated;
whatEverYouCreated.$moduleName = "somethingOrOther"; // what another package would use require or import to get this module
whatEverYouCreated.$objectName = "sOO"; // the name you or others generally use to refer to the default or primary exported component
const $wider = register.bundle(whatEverYouCreated); // gives you $wider.somethingOrOther
The dictionary $wider will contain at least $wider.register
and $wider.sOO
. The $wider object will acquire new members as new objects are registered.
You may choose to save your object in a specific section in the repository so as to maintain an orderly structure. But please note that you cannot by error use multiple sections to cause the same module to be loaded into different locations
const $wider = register.bundle(whatEverYouCreated, "nameOfMySection");
This give you $wider.nameOfMySection.sOO
Registering third party modules
If importing or requiring other modules, you should leave it to them to register themselves if they are under your control - you can just require or import them or access them through the registry.
If however they are third party objects, then they won't have registered themselves. If you wish them to be registered, then you can call registry.register()
as their proxy. Provided you ensure that you do not try and register the same module twice (throws an error, you can check in $wider) you may use the various methods already given.
You may also submit an array of objects to be submitted as a bundle, here illustrated some code internal to the registry - so these items are always registered for you and you can simply use them.
// load or import these objects and ensure each one has a property (preferably not enumerable) called $moduleName and then ...
global.$wider.registry.bundle([Module, Require, fs, path, whereAmI]);
In this mode, the registry constructs the $objectName if it is not provided. You can find out what value is used using the .describe_...()
methods. If you do not like the guesses we make as to these values then set them yourself as required in each object.
Deferring load
Deferred load modules are loaded just in time if your application makes use of them. This is transparent to your code. ES modules will not import synchronously but this is necessary to maintain transparency to your code. So you may not defer any ES modules. You may however defer JSON objects, and other objects loadable via @wider/utils_require
extensions such as markdown, xml, and other objects.
Defer is used similarly to .bundle()
except you provide module names as used by .require()
. If your object does not provide an .$objectName
then you can provide it. The example shows both ways.
const myXMLprocessor = {
$moduleName : "saxon-js",
$objectName : "SaxonJS"
}
global.$wider.registry.bundle(["@wider/utils_markup", myXMLprocessor]);
Merging a load
With merge, the members of the object you provide will generate additional properties to the pre-existing object already registered. If the pre-existing object is deferred, it will first be loaded so that the new properties can be added to it.
Merge cannot overwrite properties that are already present. In the recipient object, it creates enumerable immutable readonly getters that return the same named property/method in your supplied merge object.
This means that your methods in your merge objects may continue to update other merged properties it provides, and these will be seen via the recipient object, but access via the recipient object cannot modify them
Properties .$moduleName
, $objectName
, and $register
are never merged.
Your recipient module will be found regardless of the section it is filed in
const recipient = "somethingOrOther"; // your recipient object's $moduleName
const registry = $wider.registry;
// define something or other extension
const sOOE = [
addition1: {...something1...},
addition2: {...something2...}
];
// add the SOOE members to sOOname
registry.merge(sOOE, recipient)
This might result in
$wider
.somethingOrOther
.initialEntry1
.initialEntry2
.addition1
.addition2
Summary of Features
registry.register()
the full featured do it all yourself version - generally it is easier to use the other methodsregistry.bundle()
act as proxy to submit a bundle of other packages that do not register themselvesregistry.defer()
as *registry.bundle()
except that you provide package names and these are not required/loaded into memory individually unless and until actually used - only supports commonJS modulesregistry.merge()
merge new properties into an existing registered bundleregistry.find()
given a moduleName retrieves its objectregistry.describe_loaded()
a markdown like string documenting the registered modules and their access pointsregistry.describe_published()
a markdown like string documenting the top level structure of $wider and its sections.registry.DICTIONARY
a symbol that can be used as the value of property.$register
in a plain object to mark it as containing documentable properties when seen viaregistry.describe_published()
. Used as a property value in javascript{ $registry: registry.DICTIONARY}
or in a json file as{ "$registry": "DICTIONARY" }
registry.ROOT
can be used as the moduleName for the root (ie not a section) level of @wider.