I'm in the process of building an npm package which will be installed globally. Is it possible to have non-code files installed alongside code files that can be referenced from code files?
For example, if my package includes someTextFile.txt and a module.js file (and my package.json includes "bin": {"someCommand":"./module.js"}) can I read the contents of someTextFile.txt into memory in module.js? How would I do that?
The following is an example of a module that loads the contents of a file (string) into the global scope.
core.js : the main module file (entry point of package.json)
//:Understanding: module.exports
module.exports = {
reload:(cb)=>{ console.log("[>] Magick reloading to memory"); ReadSpellBook(cb)}
}
//:Understanding: global object
//the following function is only accesible by the magick module
const ReadSpellBook=(cb)=>{
require('fs').readFile(__dirname+"/spellBook.txt","utf8",(e,theSpells)=>{
if(e){ console.log("[!] The Spell Book is MISSING!\n"); cb(e)}
else{
console.log("[*] Reading Spell Book")
//since we want to make the contents of .txt accesible :
global.SpellBook = theSpells // global.SpellBook is now shared accross all the code (global scope)
cb()//callBack
}
})
}
//·: Initialize :.
console.log("[+] Time for some Magick!")
ReadSpellBook((e)=>e?console.log(e):console.log(SpellBook))
spellBook.txt
ᚠ ᚡ ᚢ ᚣ ᚤ ᚥ ᚦ ᚧ ᚨ ᚩ ᚪ ᚫ ᚬ ᚭ ᚮ ᚯ
ᚰ ᚱ ᚲ ᚳ ᚴ ᚵ ᚶ ᚷ ᚸ ᚹ ᚺ ᚻ ᚼ ᚽ ᚾ ᚿ
ᛀ ᛁ ᛂ ᛃ ᛄ ᛅ ᛆ ᛇ ᛈ ᛉ ᛊ ᛋ ᛌ ᛍ ᛎ ᛏ
ᛐ ᛑ ᛒ ᛓ ᛔ ᛕ ᛖ ᛗ ᛘ ᛙ ᛚ ᛛ ᛜ ᛝ ᛞ ᛟ
ᛠ ᛡ ᛢ ᛣ ᛤ ᛥ ᛦ ᛧ ᛨ ᛩ ᛪ ᛫ ᛬ ᛭ ᛮ ᛯ
If you require it from another piece of code, you will see how it prints to the console and initializes by itself.
If you want to achieve a manual initalization, simply remove the 3 last lines (·: Initialize :.) and use reload() :
const magick = require("core.js")
magick.reload((error)=>{ if(error){throw error}else{
//now you know the SpellBook is loaded
console.log(SpellBook.length)
})
I have built some CLIs which were distributed privately, so I believe I can illuminate a bit here.
Let's say your global modules are installed at a directory called $PATH. When your package will be installed on any machine, it will essentially be extracted at that directory.
When you'll fire up someCommand from any terminal, the module.js will be invoked which was kept at $PATH. If you initially kept the template file in the same directory as your package, then it will be present at that location which is local to module.js.
Assuming you edit the template as a string and then want to write it locally to where the user wished / pwd, you just have to use process.cwd() to get the path to that directory. This totally depends on how you code it out.
In case you want to explicitly include the files only in the npm package, then use files attribute of package.json.
As to particularly answer "how can my code file in the npm package locate the path to the globally installed npm folder in which it is located in a way that is guaranteed to work across OSes and is future proof?", that is very very different from the template thingy you were trying to achieve. Anyway, what you're simply asking here is the global path of npm modules. As a fail safe option, use the path returned by require.main.filename within your code to keep that as a reference.
When you npm publish, it packages everything in the folder, excluding things noted in .npmignore. (If you don't have an .npmignore file, it'll dig into .gitignore. See https://docs.npmjs.com/misc/developers#keeping-files-out-of-your-package) So in short, yes, you can package the text file into your module. Installing the module (locally or globally) will get the text file into place in a way you expect.
How do you find the text file once it's installed? __dirname gives you the path of the current file ... if you ask early enough. See https://nodejs.org/docs/latest/api/globals.html#globals_dirname (If you use __dirname inside a closure, it may be the path of the enclosing function.) For the near-term of "future", this doesn't look like it'll change, and will work as expected in all conditions -- whether the module is installed locally or globally, and whether others depend on the module or it's a direct install.
So let's assume the text file is in the same directory as the currently running script:
var fs = require('fs');
var path = require('path');
var dir = __dirname;
function runIt(cb) {
var fullPath = path.combine(__dirname, 'myfile.txt');
fs.readFile(fullPath, 'utf8' , function (e,content) {
if (e) {
return cb(e);
}
// content now has the contents of the file
cb(content);
}
}
module.exports = runIt;
Sweet!
Related
In Node.js, is there any way to require file from the same package without using relative paths? For example, here's a snippet of code from ESLint.
const rule = require("../../../lib/rules/accessor-pairs"),
{ RuleTester } = require("../../../lib/rule-tester");
The fact that we have to walk all the way up the tree ../../../ to get to the root is not only annoying. It's also brittle, because I can't move the code without updating all of dependency references.
Yet somehow Node.js developers seem to have lived with it the past 10 years. I can't find anything in the docs or Stack Overflow that solves this problem other than a third-party dependency called require-self. Nor have I been able to find a definitive statement that using relative paths is the only non-hacky way for a file to require another file in the same module.
If there's a way to specify a path relative to the package root in ECMAScript Modules (ESM) but not CommonJS (CJS), or vice versa, I would like to know that as well.
To be clear, I don't think there is a solution to the problem. If there is great. Otherwise, I'm looking for confirmation with an authoritative reference.
Not necessarily the same package - if you are writing libraries this won't be useful, but if you are writing the "final application" - the thing that actually gets run:
One option:
If the NODE_PATH environment variable is set to a colon-delimited list of absolute paths, then Node.js will search those paths for modules if they are not found elsewhere.
So you can do any of:
1.
export NODE_PATH=.
node app.js
NODE_PATH=. node app.js
// app.js (or whatever your entry point is) before *any* require() calls
process.env.NODE_PATH = __dirname;
require('module').Module._initPaths();
Or, another way:
The Module object representing the entry script loaded when the Node.js process launched.
https://nodejs.org/api/modules.html#modules_require_main
So you can just do:
const rule = require.main.require("./lib/rules/accessor-pairs")
anytime you want it to be relative to the root (assuming that is how you have your project structured).
You can use the package name itself as a "symlink" to the package root.
Example - foo package imports bar script relative to the foo package root.
package.json
{
"dependencies": {
"foo": "file:./foo"
}
}
index.js
const foo = require('foo');
console.log(foo.bar); // prints "hello"
foo/index.js
const bar = require('foo/bar'); // import relative to the package root
module.exports = {
bar: bar
}
foo/bar.js
module.exports = 'hello';
If you use vscode then you're in luck - !!!! jsconfig.json in project root handles this masterfully for commonjs, es6, amd, umd, etc
The jsconfig.json file specifies the root files and the options for the features provided by the JavaScript language service.
jsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"baseUrl": ".",
"paths": {
"#rules/*": ["path/to/lib/rules/"]
}
}
}
and then to use the alias:
const rule = require('#rules/accessor-pairs'),
{ RuleTester } = require('#rules/rule-tester');
Read more:
https://code.visualstudio.com/docs/languages/jsconfig
In my index.js file, I have const config = require('config'); written as one of the first lines.
And I have a file in my project folder called config.js
But I keep having my console tell my that it Cannot find module 'config'
My config file is this basically:
module.exports = {
'secretKey': 'mySecretCode12232',
'mongoUrl' : 'mongodb://localhost:27017/test'
};
This doesn't make any sense it should be working.
const config = require( path.join(__dirname, 'config'+'.js' ) );
I also have own function which loads atomaticaly from specified subdirectory at it's definition, it saves a lot of time.
When you don't provide any path selector in the require statement (eg. require('./config')), your code will search for the package named config and fail as it cannot find this specific one, as require will assume that it was the package name that was provided (and will start searching e.g. in your node_modules etc. - search path for it is not a trivial topic :) ).
If you want to require the module from another file, you have to provide a correct path to it, so assuming your config.js resides in the same catalog as your other file, the correct statement would be:
const config = require('./config'); // Extension can be omitted
I'm trying to use Browserify so that I can use an npm package in the browser. The package I'm trying to use is this
I have a fcs.js file:
// Use a Node.js core library
var FCS = require('fcs');
// What our module will return when require'd
module.exports = function(FCS) {
return FCS;
};
And an index.js file:
var FCS = require('./fcs.js');
console.log('FCS IS ');
console.log(FCS);
I then ran:
browserify index.js > bundle.js
And created an index.html file:
<html>
<script src="bundle.js"></script>
<script>
var fcs = new FCS();
</script>
</html>
But I end up with the error:
Uncaught ReferenceError: FCS is not defined
Maybe I'm not grasping the concept correctly. How can i use this package in the browser? Thanks.
Don't do this: require('./fcs.js');
Do this: require('./fcs');
When you require something, the extension is implicitly .js. Also make sure that your module, FCS, has an entry point (the default is index.js, but you can change that in the main entry in the package.json).
Also, in your index.html document, you're expecting that FCS is a global object. I haven't seen the internal code of FCS, but it will only be globally available if it's attached to the window object.
When you require something, it only makes it available to the rest of your code where it's required. If you want to make it globally available, you have to attach it to the window object, just like anything else.
In other words, the internals of your FCS module might look something like:
// node_modules -> fcs -> index.js
var FCS = function() {};
window.FCS = FCS; // this makes it globally available
module.exports = FCS; // this lets you require it
#JoshBeam's answer is correct - in order to expose assets inside the browserify bundle, you need to attach something to the window object. But instead of exposing specific assets, I wanted something more general.
Here's what I did:
in my app.js
require('this')
require('that')
require('./modules/mycode')
...
window.req = require
And in my <script> tag in my HTML:
something = req('./modules/mycode')
Notice that, instead of assigning the require function directly to window.require, I gave it a different name. The reason for this is simple: if you call it window.require, you're overwriting the original require and you'll find yourself in an infinite loop of recursion (at least until the browser runs out of stack space).
The problem is that your inline script (in index.html) is expecting a global variable called FCS to exist (when you do new FCS()). This is not the case because in index.js, your FCS variable is scoped to that file.
You should write all your scripts in separate files and bundle them all using browserify, avoiding the inline script or make FCS global, by attaching it to window.
I am using Browserify to compile a large Node.js application into a single file (using options --bare and --ignore-missing [to avoid troubles with lib-cov in Express]). I have some code to dynamically load modules based on what is available in a directory:
var fs = require('fs'),
path = require('path');
fs.readdirSync(__dirname).forEach(function (file) {
if (file !== 'index.js' && fs.statSync(path.join(__dirname, file)).isFile()) {
module.exports[file.substring(0, file.length-3)] = require(path.join(__dirname, file));
}
});
I'm getting strange errors in my application where aribtrary text files are being loaded from the directory my compiled file is loaded in. I think it's because paths are no longer set correctly, and because Browserify won't be able to require() the correct files that are dynamically loaded like this.
Short of making a static index.js file, is there a preferred method of dynamically requiring a directory of modules that is out-of-the-box compatible with Browserify?
This plugin allows to require Glob patterns: require-globify
Then, with a little hack you can add all the files on compilation and not executing them:
// Hack to compile Glob files. Don´t call this function!
function ಠ_ಠ() {
require('views/**/*.js', { glob: true })
}
And, for example, you could require and execute a specific file when you need it :D
var homePage = require('views/'+currentView)
Browserify does not support dynamic requires - see GH issue 377.
The only method for dynamically requiring a directory I am aware of: a build step to list the directory files and write the "static" index.js file.
There's also the bulkify transform, as documented here:
https://github.com/chrisdavies/tech-thoughts/blob/master/browserify-include-directory.md
Basically, you can do this in your app.js or whatever:
var bulk = require('bulk-require');
// Require all of the scripts in the controllers directory
bulk(__dirname, ['controllers/**/*.js']);
And my gulpfile has something like this in it:
gulp.task('js', function () {
return gulp.src('./src/js/init.js')
.pipe(browserify({
transform: ['bulkify']
}))
.pipe(rename('app.js'))
.pipe(uglify())
.pipe(gulp.dest('./dest/js'));
});
I am currently using requirejs to manage module js/css dependencies.
I'd like to discover the possibilities of having node do this via a centralized config file.
So instead of manually doing something like
define([
'jquery'
'lib/somelib'
'views/someview']
within each module.
I'd have node inject the dependencies ie
require('moduleA').setDeps('jquery','lib/somelib','views/someview')
Anyway, I'm interested in any projects looking at dependency injection for node.
thanks
I've come up with a solution for dependency injection. It's called injectr, and it uses node's vm library and replaces the default functionality of require when including a file.
So in your tests, instead of require('libToTest'), use injectr('libToTest' { 'libToMock' : myMock });. I wanted to make the interface as straightforward as possible, with no need to alter the code being tested. I think it works quite well.
It's just worth noting that injectr files are relative to the working directory, unlike require which is relative to the current file, but that shouldn't matter because it's only used in tests.
I've previously toyed with the idea of providing an alternate require to make a form of dependency injection available in Node.js.
Module code
For example, suppose you have following statements in code.js:
fs = require('fs');
console.log(fs.readFileSync('text.txt', 'utf-8'));
If you run this code with node code.js, then it will print out the contents of text.txt.
Injector code
However, suppose you have a test module that wants to abstract away the file system.
Your test file test.js could then look like this:
var origRequire = global.require;
global.require = dependencyLookup;
require('./code.js');
function dependencyLookup (file) {
switch (file) {
case 'fs': return { readFileSync: function () { return "test contents"; } };
default: return origRequire(file);
}
}
If you now run node test.js, it will print out "test contents", even though it includes code.js.
I've also written a module to accomplish this, it's called rewire. Just use npm install rewire and then:
var rewire = require("rewire"),
myModule = rewire("./path/to/myModule.js"); // exactly like require()
// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123
// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
readFile: function (path, encoding, cb) {
cb(null, "Success!");
}
});
myModule.readSomethingFromFileSystem(function (err, data) {
console.log(data); // = Success!
});
I've been inspired by Nathan MacInnes's injectr but used a different approach. I don't use vm to eval the test-module, in fact I use node's own require. This way your module behaves exactly like using require() (except your modifications). Also debugging is fully supported.