consume browserified modules in javascript - javascript

I am new to javascript and I am facing some issues working with npm modules on the browser side. Could someone please direct me int he right direction ?
I have a module structure which is something like
add.js
function add (a,b) {
return a+b;
}
module.exports.add = add;
multiply.js
function multiply (a,b) {
return a*b;
}
module.exports.multiply = multiply;
I have a consumer module called calculator.js which looks like:
var adder=require('./add');
var multiplier=require('./multiply');
console.log(adder.add(1,2));
console.log(multiplier.multiply(1,2));
function sum(a,b){
return adder.add (a,b);
}
function product(a,b){
return multiplier.add (a,b);
}
module.exports.sum = sum;
module.exports.product=product;
When I do a 'node calculator.js', I get 3 & respectively.
I would now like to use this Calculator module in my browser. So I did the following :
browserify calculator.js > cal.js
Created an html file
<html>
<body>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.js"></script>
<script src='cal.js'></script>
</body>
</html>
This prints out 3 and 2 on console.
However I would like to use the calculator's methods doing a require of cal like
<script>
var cal=require('cal');
</script>
This throws me an error saying
'Module name "cal" has not been loaded yet for context: _. Use
require([])'
Any idea how do I get this running ? Thanks in advance for help.

You don't need require.js for that.
Instead, you can tell Browserify to make the calculator.js module available for require(). This will also make a require function available for use in a browser:
$ browserify -r ./calculator.js:cal > cal.js
To use this in a browser:
<script src="cal.js"></script>
<script>
var cal = require('cal');
...
</script>
More information here.

Related

"ReferenceError: window is not defined" when compiling with r.js

I've been using RequireJS and it works perfectly. I use a lot of "window.document" to manipulate different DOM elements, but when I try to optimize it with r.js i get a ReferenceError: window is not defined which only happens with r.js.
Here is a minimal example of code that reproduces the issue:
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body >
<div id="commentbox">
</div>
<script data-main="code/main" src="code/require.js"></script>
</body>
</html>
main.js:
require(["roomManager"], function (roomManager){
return {
}
});
roomManager.js:
define(["commentManager"], function(commentManager){
var commentHand = new commentManager.commentHand();
commentHand.init();
return{
}
});
commentManager.js:
define([], function(){
function commManager(getDisplayIdVariable){
var messagebox = window.document.getElementById("commentbox");
this.init = function(){
messagebox.innerHTML = "hi!";
}
}
return{
commentHand : commManager
}
});
This version works correctly without r.js but when I try to compile it by running r.js main.js. I get this:
var messagebox = window.document.getElementById("commentbox);
ReferenceError: window is not defined
at new new commManager
You cannot just do r.js main.js.
For one thing, you have to specify -o so that r.js performs the optimization. (r.js can be used for other things.)
You also have to pass configuration to r.js, either in a file, or on the command line. One possibility for you would be:
r.js -o name=main out=built.js
I've tried this with the code you show in your question and I get no errors.
I strongly suggest going over this documentation for r.js.
if your code is optional you can use
if (typeof window !== 'undefined') {
// Inside browser
}
{
// outside browser
}

How is it possible to make json query client side [duplicate]

I have the following Node.js project (which is a Minimal Working Example of my problem):
module1.js:
module.exports = function() {
return "this is module1!";
};
module2.js:
var module1 = require('./module1');
module.exports = function() {
return module1()+" and this is module2!";
};
server.js:
var module2 = require('./module2');
console.log(module2()); // prints: "this is module1! and this is module2!"
Now I want to create a client.html file that will also use module2.js. Here is what I tried (and failed):
naive version:
<script src='module2.js'></script>
<script>alert(module2());</script> // should alert: "this is module1! and this is module2!"
This obviously doesn't work - it produces two errors:
ReferenceError: require is not defined.
ReferenceError: module2 is not defined.
Using Node-Browserify: After running:
browserify module2.js > module2.browserified.js
I changed client.html to:
<script src='require.js'></script>
<script>
var module2 = require('module2');
alert(module2());
</script>
This doesn't work - it produces one error:
ReferenceError: module2 is not defined.
Using Smoothie.js by #Torben :
<script src='require.js'></script>
<script>
var module2 = require('module2');
alert(module2());
</script>
This doesn't work - it produces three errors:
syntax error on module2.js line 1.
SmoothieError: unable to load module2 (0 )
TypeError: module2 is not a function
I looked at require.js but it looks too complicated to combine with Node.js - I didn't find a simple example that just takes an existing Node.js module and loads it into a web page (like in the example).
I looked at head.js and lab.js but found no mention of Node.js's require.
So, what should I do in order to use my existing Node.js module, module2.js, from an HTML page?
The problem is that you're using CJS modules, but still try to play old way with inline scripts. That won't work, it's either this or that.
To take full advantage of CJS style, organize your client-side code exactly same way as you would for server-side, so:
Create client.js:
var module2 = require('./module2');
console.log(module2()); // prints: "this is module1! and this is module2!"
Create bundle with Browserify (or other CJS bundler of your choice):
browserify client.js > client.bundle.js
Include generated bundle in HTML:
<script src="client.bundle.js"></script>
After page is loaded you should see "this is module1! and this is module2!" in browser console
You can also try simq with which I can help you.
Your problems with Smoothie Require, were caused by a bug (https://github.com/letorbi/smoothie/issues/3). My latest commit fixed this bug, so your example should work without any changes now.

ES6 + Babel + Gulp + Browserify, Uncaught ReferenceError [duplicate]

I am new to nodejs and browserify. I started with this link .
I have file main.js which contains this code
var unique = require('uniq');
var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];
this.LogData =function(){
console.log(unique(data));
};
Now I Install the uniq module with npm:
npm install uniq
Then I bundle up all the required modules starting at main.js into a single file called bundle.js with the browserify command:
browserify main.js -o bundle.js
The generated file looks like this:
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var unique = require('uniq');
var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];
this.LogData =function(){
console.log(unique(data));
};
},{"uniq":2}],2:[function(require,module,exports){
"use strict"
function unique_pred(list, compare) {
var ptr = 1
, len = list.length
, a=list[0], b=list[0]
for(var i=1; i<len; ++i) {
b = a
a = list[i]
if(compare(a, b)) {
if(i === ptr) {
ptr++
continue
}
list[ptr++] = a
}
}
list.length = ptr
return list
}
function unique_eq(list) {
var ptr = 1
, len = list.length
, a=list[0], b = list[0]
for(var i=1; i<len; ++i, b=a) {
b = a
a = list[i]
if(a !== b) {
if(i === ptr) {
ptr++
continue
}
list[ptr++] = a
}
}
list.length = ptr
return list
}
function unique(list, compare, sorted) {
if(list.length === 0) {
return []
}
if(compare) {
if(!sorted) {
list.sort(compare)
}
return unique_pred(list, compare)
}
if(!sorted) {
list.sort()
}
return unique_eq(list)
}
module.exports = unique
},{}]},{},[1])
After including bundle.js file into my index.htm page, how do I call logData function ??
The key part of bundling standalone modules with Browserify is the --s option. It exposes whatever you export from your module using node's module.exports as a global variable. The file can then be included in a <script> tag.
You only need to do this if for some reason you need that global variable to be exposed. In my case the client needed a standalone module that could be included in web pages without them needing to worry about this Browserify business.
Here's an example where we use the --s option with an argument of module:
browserify index.js --s module > dist/module.js
This will expose our module as a global variable named module.
Source.
Update:
Thanks to #fotinakis. Make sure you're passing --standalone your-module-name. If you forget that --standalone takes an argument, Browserify might silently generate an empty module since it couldn't find it.
Hope this saves you some time.
By default, browserify doesn't let you access the modules from outside of the browserified code – if you want to call code in a browserified module, you're supposed to browserify your code together with the module. See http://browserify.org/ for examples of that.
Of course, you could also explicitly make your method accessible from outside like this:
window.LogData =function(){
console.log(unique(data));
};
Then you could call LogData() from anywhere else on the page.
#Matas Vaitkevicius's answer with Browserify's standalone option is correct (#thejh's answer using the window global variable also works, but as others have noted, it pollutes the global namespace so it's not ideal). I wanted to add a little more detail on how to use the standalone option.
In the source script that you want to bundle, make sure to expose the functions you want to call via module.exports. In the client script, you can call these exposed functions via <bundle-name>.<func-name>. Here's an example:
My source file src/script.js will have this:
module.exports = {myFunc: func};
My browserify command will look something like this:
browserify src/script.js --standalone myBundle > dist/bundle.js
And my client script dist/client.js will load the bundled script
<script src="bundle.js"></script>
and then call the exposed function like this:
<script>myBundle.myFunc();</script>
There's no need to require the bundle name in the client script before calling the exposed functions, e.g. <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script> isn't necessary and won't work.
In fact, just like all functions bundled by browserify without standalone mode, the require function won't be available outside of the bundled script. Browserify allows you to use some Node functions client-side, but only in the bundled script itself; it's not meant to create a standalone module you can import and use anywhere client-side, which is why we have to go to all this extra trouble just to call a single function outside of its bundled context.
I just read through the answers and seems like nobody mentioned the use of the global variable scope? Which is usefull if you want to use the same code in node.js and in the browser.
class Test
{
constructor()
{
}
}
global.TestClass = Test;
Then you can access the TestClass anywhere.
<script src="bundle.js"></script>
<script>
var test = new TestClass(); // Enjoy!
</script>
Note: The TestClass then becomes available everywhere. Which is the same as using the window variable.
Additionally you can create a decorator that exposes a class to the global scope. Which is really nice but makes it hard to track where a variable is defined.
Read README.md of browserify about --standalone parameter
or google "browserify umd"
Minimal runnable example
This is basically the same as: https://stackoverflow.com/a/43215928/895245 but with concrete files that will allow you to just run and easily reproduce it yourself.
This code is also available at: https://github.com/cirosantilli/browserify-hello-world
index.js
const uniq = require('uniq');
function myfunc() {
return uniq([1, 2, 2, 3]).join(' ');
}
exports.myfunc = myfunc;
index.html
<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>Browserify hello world</title>
</head>
<body>
<div id="container">
</body>
</div>
<script src="out.js"></script>
<script>
document.getElementById('container').innerHTML = browserify_hello_world.myfunc();
</script>
</html>
Node.js usage:
#!/usr/bin/env node
const browserify_hello_world = require('./index.js');
console.log(browserify_hello_world.myfunc());
Generate out.js for browser usage:
npx browserify --outfile out.js --standalone browserify_hello_world index.js
Both the browser and the command line show the expected output:
1 2 3
Tested with Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10.
To have your function available from both the HTML and from server-side node:
main.js:
var unique = require('uniq');
function myFunction() {
var data = [1, 2, 2, 4, 3];
return unique(data).toString();
}
console.log ( myFunction() );
// When browserified - we can't call myFunction() from the HTML, so we'll externalize myExtFunction()
// On the server-side "window" is undef. so we hide it.
if (typeof window !== 'undefined') {
window.myExtFunction = function() {
return myFunction();
}
}
main.html:
<html>
<head>
<script type='text/javascript' src="bundle.js"></script>
<head>
<body>
Result: <span id="demo"></span>
<script>document.getElementById("demo").innerHTML = myExtFunction();</script>
</body>
</html>
Run:
npm install uniq
browserify main.js > bundle.js
and you should get same results when opening main.html in a browser as when running
node main.js
Whole concept is about wrapping.
1.) Alternative - Object "this"
for this purpose I'll assume you have "only 1 script for whole app {{app_name}}" and "1 function {{function_name}}"
add function {{function_name}}
function {{function_name}}(param) { ... }
to object this
this.{{function_name}} = function(param) { ... }
then you have to name that object to be available - you will do it add param "standalone with name" like others advised
so if you use "watchify" with "browserify" use this
var b = browserify({
...
standalone: '{{app_name}}'
});
or command line
browserify index.js --standalone {{app_name}} > index-bundle.js
then you can call the function directly
{{app_name}}.{{function_name}}(param);
window.{{app_name}}.{{function_name}}(param);
2.) Alternative - Object "window"
add function {{function_name}}
function {{function_name}}(param) { ... }
to object window
window.{{function_name}} = function(param) { ... }
then you can call the function directly
{{function_name}}(param);
window.{{function_name}}(param);
You have a few options:
Let plugin browserify-bridge auto-export the modules to a generated entry module. This is helpful for SDK projects or situations where you don't have to manually keep up with what is exported.
Follow a pseudo-namespace pattern for roll-up exposure:
First, arrange your library like this, taking advantage of index look-ups on folders:
/src
--entry.js
--/helpers
--- index.js
--- someHelper.js
--/providers
--- index.js
--- someProvider.js
...
With this pattern, you define entry like this:
exports.Helpers = require('./helpers');
exports.Providers = require('./providers');
...
Notice the require automatically loads the index.js from each respective sub-folder
In your subfolders, you can just include a similar manifest of the available modules in that context:
exports.SomeHelper = require('./someHelper');
This pattern scales really well and allows for contextual (folder by folder) tracking of what to include in the rolled-up api.
You can also call your function from the html file like this:
main.js: (will be in bundle.js)
window.onload = function () {
document.getElementById('build-file')
.addEventListener('click', buildFile)
}
function buildFile() {
...
}
index.html:
<button id="build-file"">Build file</button>
window.LogData =function(data){
return unique(data);
};
Call the function simply by LogData(data)
This is just a slight modification to thejh's answer but important one
For debugging purposes I added this line to my code.js:
window.e = function(data) {eval(data);};
Then I could run anything even outside the bundle.
e("anything();");

Run function loaded from ecmascript 6 module

I try to use ecmascript 6 modules system for first time. I use traceur compiler. Given two es6 files:
// app.js
export function row() {
alert('row');
}
// init.js
import { row } from 'public_js/app';
row();
Traceur (I use grunt-traceur task) compiles them to:
// app.js
System.register("public_js/app", [], function() {
"use strict";
var __moduleName = "public_js/app";
function row() {
alert('row');
}
return {get row() {
return row;
}};
});
// init.js
System.register("public_js/init", [], function() {
"use strict";
var __moduleName = "public_js/init";
var row = System.get("public_js/app").row;
row();
return {};
});
I include compiled version of init.js to my HTML via simple script tag:
<script src="/path/to/compiled/init.js" type="module"></script>
And nothing happens. I don't see my alert. What am I doing wrong?
By pre-compiling your code as modules to ES5, you are now taking it out of the world of the automatic import/module loading system in ES6 and you need to use ES5 mechanisms to load it. So, you need to include the compiled code without the type=module attribute and then get() the module that kicks off the rest of the world.
So, the following works for me:
<script src="/path/to/compiled/app.js"></script>
<script src="/path/to/compiled/init.js"></script>
<script>
System.get('public_js/init');
</script>
Since you are pre-compiling the code, I recommend that you concatenate all of the compiled code into a single JS file to avoid including them all.
If you use Traceur without compiling your code first, then you can live within the ES6 constructs. This includes type="module" and/or import 'module-name'.
Edit
Thinking about this further, app.js is correctly compiled as a module. init.js, however, doesn't need to be compiled as a module. You are compiling the code with the --module flag. If, instead, you compile init.js with the --script flag, it will not encapsulate the init code as a module, and you don't need to call System.get by hand. Just something to think about.

Importing other .js files in Buster.js tests

I'm making my first attempt at Javascript testing, with Buster.js
I've followed the instructions at the Buster site to run "states the obvious" test. However, I haven't been able to import any of my existing .js files into the tests.
For instance, I have a file js/testLibrary.js, containing:
function addTwo(inp) {
return inp+2;
}
and a file test/first-test.js, containing:
// Node.js tests
var buster = require("buster");
var testLibrary = require("../js/testLibrary.js");
var assert = buster.referee.assert;
buster.testCase("A module", {
"Test The Library": function() {
result = addTwo(3);
console.log(result);
assert(true, 'a message for you');
}
});
Running buster-test gives:
Error: A module Test The Library
ReferenceError: addTwo is not defined
[...]
Replacing result = addTwo(3); with result = testLibrary.addTwo(3); gives:
Error: A module Test The Library
TypeError: Object #<Object> has no method 'addTwo'
[...]
I'm probably missing something really basic, but at present, I'm completely stumped. Can someone point me in the right direction?
That is because you are not exporting this function from the module.
Take a look at that:
http://nodejs.org/api/modules.html#modules_module_exports

Categories

Resources