How can I include additional modules in a NodeJS custom binary? - node.js

I am building a custom binary of NodeJS from the latest code base for an embedded system. I have a couple modules that I would like to ship as standard with the binary - or even run a custom script the is compiled into the binary and can be invoked through a command line option.
So two questions:
1) I vaguely remember that node allowed to include custom modules during build time but I went through the latest 5.9.0 configure script and I can't see anything related - or maybe I am missing it.
2) Did someone already do something similar? If yes, what were the best practices you came up with?
I am not looking for something like Electron or other binary bundlers but actually building into the node binary.
Thanks,
Andy

So I guess I figure it out much faster that I thought.
For anyone else, you can add any NPM module to it and just add the actual source files to the node.gyp configuration file.
Compile it and run the custom binary. It's all in there now.
> var cmu = require("cmu");
undefined
> cmu
{ version: [Function] }
> cmu.version()
'It worked!'
> `

After studying this for quite a while, I have to say that the flyandi's answer is not quite true. You cannot add any NPM module just by adding it to the node.gyp.
You can only add pure JavaScript modules this way. To be able to embed a C++ module (I deliberately don't use the word "native", because that one is quite ambiguous in nodeJS terminology - just look at the sources).
To summarize this:
To embed a JS module to your custom nodejs, just add it in the library_files section of the node.gyp file. Also note that it should be placed within the lib folder, otherwise you'll have troubles requiring the module. That's because the name/path listed in node.gyp / library_files is used to encode the id of the module in the node_javascript.cc intermediate file which is then used when searching for the built-in modules.
To embed a native module is much more difficult. The best way I have found so far is to build the module as a static library instead of dynamic, which for cmake(-js) based module you can achieve by changing the SHARED parameter to STATIC like this:
add_library(${PROJECT_NAME} STATIC ${SRC})
instead of:
add_library(${PROJECT_NAME} SHARED ${SRC})
And also changing the suffix:
set_target_properties(
${PROJECT_NAME}
PROPERTIES
PREFIX ""
SUFFIX ".lib") /* instead of .node */
Then you can link it from node.gyp by adding this section:
'link_settings': {
'libraries' : [
"path/to/my/library.lib",
#...add other static dependencies
],
},
(how to do this with node-gyp based project should be quite ease to google)
This allows you to build the module, but you won't be able to require it, because require() function in node can only be used to load built-in JS modules, external JS modules or external dynamic node modules. But now we have a built-in C++ module. Well, lot of node integrated modules are C++, but they always have a JS wrapper in /lib, and those wrappers they use process.binding() to load the C++ module. That is, process.binding() is sort of a require() function for integrated C++ modules.
That said, we also need to call require.binding() instead of require to load our integrated module. To be able to do that, we have to make our module "built-in" first.
We can do that by replacing
NODE_MODULE(mymodule, InitAll)
int the module definition with
NODE_BUILTIN_MODULE_CONTEXT_AWARE(mymodule, InitAll)
which will register it as internal module and from now on we can process.binding() it.
Note that NODE_BUILTIN_MODULE_CONTEXT_AWARE is not defined in node.h as NODE_MODULE but in node_internals.h so you either have to include that one, or copy the macro definition over to your cpp file (the first one is of course better because the nodejs API tends to change quite often...).
The last thing we need to do is to list our newly integrated module among the others so that the node knows to initialize them (that is include them within the list of modules used when searching for the modules loaded with process.binding()). In node_internals.h there is this macro:
#define NODE_BUILTIN_STANDARD_MODULES(V) \
V(async_wrap) \
V(buffer) \
V(cares_wrap) \
...
So just add the your module to the list the same way as the others V(mymodule).
I might have forgotten some step, so ask in the comments if you think I have missed something.
If you wonder why would anyone even want to do this... You can come up with several reasons, but here's one most important to me: Those package managers used to pack your project within one executable (like pkg or nexe) work only with node-gyp based modules. If you, like me, need to use cmake based module, the final executable won't work...

Related

In typescript, how to restrict a typing reference to a certain file?

I'm trying to cleanly write some universal javascript code, for node and browser.
Most of the code is env-agnostic, however, some implementation parts detect the environment (node or browser) and conditionally execute different code.
I would like to activate node typings ONLY for those specific files. However, I couldn't find a way to do so. Either:
node typings, when referenced in even one file only, are made effective for all files (bad, since I could inadvertently rely on node specificities)
if not referencing node typings at all, typescript obviously complains about a lot of unknown definitions, which would be painful to patch by hand
Do anyone has a clean way of activating some type definitions for a selected set of files ?
It's not possible at this time.
A solution: building node-dependant and node-independant files separately. This could be done automatically with a script.

Mixing typescript definition files with nodejs require over multiple files in an internal module

Are there any known issues with mixing nodejs modules (require) with typescript definition files (d.ts) multiple times over files within a module?
My scenario is that I have a module namespace per folder (much like I would in C#), then I basically compile them all via tsc to an outputted my-module.js. However I keep getting really odd errors like Could not find type HTMLElement but lots of people have pointed out that tsc includes the typescript lib file by default which contains all those types.
I have noticed a few people having odd errors when they are including the same d.ts files over multiple files which are all compiled with the --out flag to get it all into one file, so could this be causing my issues?
An example of my usage would be:
///<reference path="path/to/knockout.d.ts" />
import ko = require("knockout");
This would then be put in each file which requires knockout js, which is at least 10 files in the module i'm trying to compile currently. It just bombs out constantly saying knockout.d.ts cannot find the type HTMLElemet, Element, Document etc.
If you are using external modules (which you are if you have a top level "import" - as shown above), then you can't use the --out switch to combine multiple source files. It is a limitation that with external modules that one source file = one module. With source that is not in an external module (i.e. contributes to the 'global' scope), you can combine input source to one output JavaScript file using --out.
I have no idea about the "could not find HTMLElement" issues. If you can provide a repro (and outline which version you are using) I can take a look.

How to create a TypeScript Nodejs module spanning multiple files

I'm trying to create a Node.js module with a single namespace using TypeScript. I need the classes of the module to be in separate files, and have references to each other. I also need this module to utilize other node modules. But if I understand right, TypeScript only supports namespaces if a namespace is contained within a single file or the namespace does not use external modules.
Some people present the use of post-build tools to make the final module work, which is nice but doesn't address all the errors TypeScript throws when combining cross-file namespaces and imports during development.
Is it true that the closest solution is to create a module per file, and create a web of inter-imports?
I find that what works best for me for module dependencies is to always use source references, e.g.:
/// <reference path="other.ts" />
Since you can't generate single-file output from tsc if you use language-level imports/modules, I have resorted to using require as a function and so on.
This seems to be the same solution chosen by the TypeScript developers themselves (search for "require(" there). As suggested by silentorb's comment, you can declare the require function as some variation of the following (or use DefinitelyTyped):
declare function require(name: string): any;
Note that there's often a lot of discussion on the whole modularity topic in TS over on the TS forums (one recent example).

Node.js/npm - dynamic service discovery in packages

I was wondering whether Node.js/npm include any kind of exension mechanism comparable to Python setuptools' "entry points".
So, in short:
is there any way I can do dynamic discovery of services provided by other packages using npm?
if not, what would be the best way to implement something similar? Specifying the extension name in the main module's configuration file seems to be the logical solution, but I wonder whether something "automatic" can be done.
I'm not aware of any builtin mechanism to do this.
One viable way of doing it yourself:
I made a small tool (Jumpstart) to quickly create project scaffolding from templates with placeholders, and I used a kind of plugin mechanism for that. It basically comes down to that the Jumpstart script searches for modules named jumpstart-* "adjacent" to where the module itself is installed. So it would work for both local and global installations. If installed locally, it would search the other local modules (on the same level) and if global, it searches the other global modules.
Note that here, "search" comes down to a simple fs.exists check to see if there's a Jumpstart template module with a particular name installed. However, nothing would stand in the way to actually get a full list of all installed packages matching the jumpstart-* pattern, and loading all at once. I could also search up the entire directory tree for node_modules directories and do the same. There's no point in doing this for this particular program, however.
See https://npmjs.org/package/jumpstart for docs.
The only limitation to this technique is that all modules must be named in a consistent fashion. Start with some string, end with some string, something like that. Any rogue packages polluting the namespace could be detected by doing further checks on a package contents: What files does it contain? What kind of object does its main module export? etc.
Brunch also uses a plugin mechanism. This one actually deals with file extensions, so is more relevant: https://github.com/brunch/brunch/wiki/Plugins . See for example source of the CoffeeScript plugin https://github.com/brunch/coffee-script-brunch/blob/master/src/index.coffee .

npm package.json OS specific dependency

Is there a way to specify OS specific dependencies in a npm package.json file?
For example, I would only want to install 'dbus' (https://npmjs.org/package/dbus) as a dependency for my module if the user is running Linux. I would have a different dependency for Mac and Windows.
There's a possible good way of doing this, depending on your setup.
npm package.json supports an os key,
and also optionalDependencies
os can be used to specify which OS a module can be installed on.
optionalDependencies are module dependencies that if they cannot be installed, npm skips them and continues installing.
In this way you can have your module have an optional dependency for each OS, and only the one which works will be loaded/installed ^.^
EDIT: As #Sebastien mentions below, this approach is dangerous.
For any given OS, at least one of your dependencies is "required" and the rest "optional". Making all versions of the dependency optional means that if your installation fails for a legitimate reason, it will silently skip installation and you will be missing a dependency you really need.
I think the short answer is no. I can think of a couple of workarounds though - the simplest is to just add everything to package.json regardless of OS, and then require() the correct one at runtime.
If that doesn't work for you, you might be able to use an install script to get the result you're going for - https://docs.npmjs.com/misc/scripts
I haven't tested this but I think it would work:
Add something like this to your package.json:
,"scripts": {
"install": "node install_dependencies.js"
}
And then add a install_dependencies.js file that checks the OS and runs the appropriate npm install ... commands.
There's also the bindings-shyp module:
https://www.npmjs.com/package/bindings-shyp
Helper module for loading your native module's .node file
This is a helper module for authors of Node.js native addon modules. It is basically the "swiss army knife" of require()ing your native module's .node file.
Throughout the course of Node's native addon history, addons have ended up being compiled in a variety of different places, depending on which build tool and which version of node was used. To make matters worse, now the gyp build tool can produce either a Release or Debug build, each being built into different locations.
This module checks all the possible locations that a native addon would be built at, and returns the first one that loads successfully.
Quoting #npm_support at:
https://twitter.com/npm_support/status/968195526989512705
2/2 If you'd like to avoid installation problems related to dependencies, one route is for you to write a wrapper that's required as a regular dependency, and to make sure that it has optionalDeps (and also ensure that the wrapper verifies you have everything needed to work).
But IMHO it looks more like a workaround than solving the problem for real.
I can understand that npm wants to preserve portability and avoid to deal with platform specifics, but it has to be done anyway and IMHO doing this at runtime is not optimal (specialty if one wants do optimize code size).
So today I have no optimal solution to share but an open discussion for proposal.
Can't "conditional dependencies" be supported in npm ?
The 1st thing that came to my mind was to to add a "override" section that will change (+add, -remove, =replace) current parsed sections.
For example:
dependencies: { "common-stuff": "*" }
overrides: {
"os: { linux: { dependencies: { "+best-linux-module" } } }
}
And other option suggested by a developer I know, would be to introduce a provides keyword, then several modules could provide a same semantic than would be satisfied by resolver (a la debian), but it's generating similar overhead.
I am looking for a generic approach not only focused on OS support but also on other flavors of package (depending on engines for instance).
Do you know any related issue in NPM tracker ? if not I am considering to file a bug to be tracked at:
https://github.com/npm/npm/issues?q=dependencies+conditional
Feedback welcome on this idea.

Resources