How to use node module in client code? - node.js

I've installed ReactJs module in my nodejs application, so React appeared in node_modules directory. But I can't use it in client javascript because there is no reference.
So what is the best way to add such a reference ?
(Of course I can manually copy react.js script to scripts folder and add reference manually but it leads to code duplication and looks ugly)

Browserify is the tool you are looking for. It takes Node modules and wraps them so you can require() them in client side JavaScript code.
Example from Browserify's readme
Whip up a file, main.js with some require()s in it. You can use
relative paths like './foo.js' and '../lib/bar.js' or module paths
like 'gamma' that will search node_modules/ using node's module
lookup algorithm.
var foo = require('./foo.js');
var bar = require('../lib/bar.js');
var gamma = require('gamma');
var elem = document.getElementById('result');
var x = foo(100) + bar('baz');
elem.textContent = gamma(x);
Export functionality by assigning onto module.exports or exports:
module.exports = function (n) { return n * 111 }
Now just use the browserify command to build a bundle starting at
main.js:
$ browserify main.js > bundle.js
All of the modules that main.js needs are included in the
bundle.js from a recursive walk of the require() graph using
required.
To use this bundle, just toss a <script src="bundle.js"></script>
into your html!

Related

Browserify document is undefined

i just found browserify, which sounds really cool. However i try to append a canvas element to the body(filename : Renderer.js):
window.document.body.appendChild(this.canvas)
module.exports = Renderer
And i also have a main.js
var Renderer = require("Renderer.js")
var r = new Renderer();
So i build the bundle like this:
browserify main.js -o bundle.js
And when i start the project:
node server.js
I get the following error message
ReferenceError : document is not defined (if only document.body)
And window is not defined (when window.document.body)
Can someone explain this behaviour and how to fix it?
Did you try to run that browserify code on the server? Please do not, as in the server there is not window element, whereas node only has global. Instead, you need to use your browser to download the browserified code and execute on your browser.
Note that, browserify library is mainly to bundle packages for client to use require styled call.

Unable to use requireJS and Node's Require in the same TypeScript project

I have a typescript project targeted at both Node and the browser. I'm using Node's require() in some scripts and requireJS's require() in others. My project directory looks like this:
myProject
\-- a.ts
\-- b.ts
\-- node.d.ts
\-- require.d.ts
where a.ts contains:
/// <reference path="./node.d.ts" />
var cp = require('child-process');
var a = 'hello world'
export = a
and b.ts contains:
/// <reference path="./require.d.ts" />
require('hello',function(x){console.log('world')});
var b = 'hello world'
export = b
and where require.d.ts and node.d.ts are obtained from DefinitlyTyped.
When I compile my project, I get these errors:
b.ts(2,1): error TS2346: Supplied parameters do not match any signature of call target.
require.d.ts(396,13): error TS2403: Subsequent variable declarations must have the same type. Variable 'require' must be of type 'NodeRequire', but here has type 'Require'.
I use this idiom to determine which modules to load, so I'm not loading a node module in the browser or vice versa.
if(typeof module !== 'undefined' && module.exports){
// We're in a Node process
}else{
// We're in an AMD module in the browser:
}
Is there a way to use both of these .d.ts files in the same project. It seems using them in separate modules is not enough.
Is there a way to use both of these .d.ts files in the same project
I highly recommend going with commonjs everywhere. That is what the React community has spearheaded and it's a much simpler workflow. Just use CommonJS + webpack (to get lazy require.ensure from here). 🌹
There's also a quickstart for TypeScript in the browser environment.
In the end what I needed was the ability to require() JS content that is compiled on the fly by the server -- which doesn't seem to work with web-pack.
To suppress the errors from the typescript compiler in the original question, I commented out this line from require.d.ts (the RequireJS declaration file):
declare var require: Require;
and I used the {foo:bar}['f'+'oo'] trick to get tsc to 'forget' the type of the ambient require variable when assigning it to the typed requirejs variable, like so:
var requirejs:Require; // requirejs
if(typeof module !== 'undefined' && module.exports){
// We're in a Node process
requirejs = require('requirejs');
}else{
// We're in an AMD module in the browser:
requirejs = {require:require}['req'+'ire'];
}
// use requirejs for dynamic require statements
requirejs(gather_requirements(),do_things);

Overcome differences in node and browserify path resolving

I'm writing a module for a react app that needs to be included on both the backend and frontend.
At some point in my code, I'm requiring some svg file (for which I use a browserify module, but this has nothing to do with the question).
For example I have in my ./src/js/components/tools/svg.js the following bit of code:
// ...
var BACKEND = /* code to detect if this is running on browser or on node */;
var svg;
if ( BACKEND ) {
svg = require("./../../../icon/" + this.props.icon + ".svg");
} else {
svg = require("./src/icon/" + this.props.icon + ".svg");
}
// ....
I use browserify's require option to require all the svg files at bundle-time:
browserify({
paths: ['./src/icon'],
})
.transform(/* svg tansformer */)
.require(glob.sync("./src/icon/*.svg")) // <-- svg's get added here
.add("./src/main.js"); // main entry point
However this conflicts with how node resolves the filenames. It cannot find ./src/icon/ from ./src/js/components/tools/svg.js.
This is why I have to guard the require with the BACKEN clause. This breaks my eyes though and I would like to just be able to write:
var svg = require('./src/icon/' + this.props.icon + '.svg');
I've tried two things so far:
fix node to find ./src/icon
I can use export NODE_PATH=`cwd` to allow node to look for src/icon from ./. This allows me to write:
var svg = require('src/icon/' + this.props.icon + '.svg');
in the backend. But, since browserify only accepts paths that start with ./ (thus, ignoring src/icon) this will not resolve on the frontend.
fix browserify to use ../../../icon/
Haven't got this to work either because of the same reason: browserify only accepts paths that start with ./.
It's considered bad practice doing conditional requires when using Browserify because it can't evaluate the code at "compile time" and will always attempt to load all the files.
To load different files in the browser environment than on node is easy:
Add a "browser" field to your package.json that points to the browser main file. Use "main" for the node main file. Then just require the module.
You can do the same thing with sub folders within your project. Just add a package.json file with "private": true and both, the main and the browser properties and require the folder path.

How to "require" text files with browserify?

I am using browserify (using browserify-middleware)
how can I require simple text files, something like:
var myTmpl = require("myTmpl.txt");
I cheked stringify plugin for browserify but the code in the documentation is not working with browserify V2
require() is really best for just javascript code and json files to maintain parity with node and to improve readability of your code to outsiders who expect require() to work the way it does in node.
Instead of using require() to load text files, consider using the brfs transform. With brfs, you maintain parity with node by calling fs.readFileSync() but instead of doing synchronous IO as in node, brfs will inline the file contents into the bundle in-place so
var src = fs.readFileSync(__dirname + '/file.txt');
becomes
var src = "beep boop\n";
in the bundle output.
Just compile with -t brfs:
browserify -t brfs main.js > bundle.js
More discussion about why overloading require() too much is a bad idea: http://mattdesl.svbtle.com/browserify-vs-webpack
stringify:
https://github.com/JohnPostlethwait/stringify
Here's author example:
var bundle = browserify()
.transform(stringify(['.hjs', '.html', '.whatever']))
.add('my_app_main.js');
If you really want to use require(), you may want to look at partialify:
my.txt:
Hello, world!
index.js:
alert( require( "my.txt" ) );
Where Browserify is configured:
var partialify = require( "partialify/custom" );
partialify.alsoAllow( "txt" );
bundle.add( "./index.js" );
bundle.transform( partialify );
Theoretically you will get a "Hello, world!" message in browser.
P.S. I haven't tried this myself.
Edit: note that this solution breaks NodeJS compatibility - it only works in browserified state, as NodeJS doesn't know how to require .txt files.

PhantomJS require() a relative path

In a PhantomJS script I would like to load a custom module but it seems relative paths do not works in PhantomJS ?
script.js:
var foo = require('./script/lib/foo.js');
foo.bar('hello world');
phantom.exit();
foo.js:
exports.bar = function(text){
console.log(text);
}
According to fs.workingDirectory I am in the good directory
foo.js is not in the lookup path of phantomjs
Am I missing something ?
EDIT:
inject() is not revelant because I do not need to inject a JS to an HTML page but instead load my own module like require('fs') but with a relative path.
After a lot of time searching for the same thing, here is what I understood, though I might be wrong :
PhantomJS doesn't use Node's require, but its own require, so things are different
when providing a relative path to phantomjs's require, it is always interpreted as relative to the current working directory
PhantomJS doesn't implement node's __dirname, and as such there is no direct way to have the directory of your script
The solution I found least annoying :
if using phantomjs pure, without casperjs :
require(phantom.libraryPath + '/script/lib/foo.js')
if using casperjs :
var scriptName = fs.absolute( require("system").args[3] );
var scriptDirectory = scriptName.substring(0, scriptName.lastIndexOf('/'));
require(scriptDirectory + '/script/lib/foo.js')
To load your own module, the right way to do it is to use module.exports, like this: foo.js
function bar(text) {
console.log(text);
}
exports.bar = bar
And in script.js (which is executed with phantomjs script.js):
var foo = require('./script/lib/foo');
foo.bar('hello world');
phantom.exit();
My solution to load a resource file (like let's say a json file) within a phantomjs subfolder from a outer folder like in this structure:
├── consumer.js
├── assets
├── data.json
├── loader.js
Supposed that data.json must be load by the consumer module and that this module is called by somewhere else on this machine, outside the project root folder, the fs.workingDirectory will not work, since it will be the path of the caller file.
So to solve this, I did a simple loader module within the assets folder, where the files I want to load are:
(function() {
var loader = {
load : function(fileName) {
var res=require('./'+fileName);
return res;
}
}
module.exports=loader;
}).call(this);
I therefore call the loader module from the consumer module like
var loader=require('./data/loader');
var assets=loader.load('data.json');
and that's it.
NOTE. The require here is the phantomjs require not the node version, so it works a bit differently. In this case the data.json was a json array with no module.exports declaration. The array will be backed in the assets variable directly when calling the loader.load(fileName) method.
have you tried to use injectJs(filename)
excerpt form PhantomJS documentation:
Injects external script code from the specified file. If the file can
not be found in the current directory, libraryPath is used for
additional look up.
This function returns true if injection is successful, otherwise it
returns false.
Which PhantomJS version are you running? Support for user provided modules was added in 1.7.

Resources