Global require with Browserify v2 - javascript

I want to use Browserify to bundle my files, but I then need to require one of the modules inside of my Browserify bundled bundle.js on the HTML page itself. This is currently not possible because there is no require function defined on the page.
It appears that the require function defined by browserify in bundle.js is inside of an IIFE, so I can't use that. Is it possible to throw this one out in place of a global require?
<script src="bundle.js"></script>
<script>
// Require the `app` module inside of `bundle.js`
var app = require('app');
app.start();
</script>
I need to do this because my app.start function requires some JSON is passed to it which can only be rendered by the server-side template.
N.B. I am using Browserify v2.

You can use -r to expose a global require() function for the files you specify:
x.js:
module.exports = function (n) { return n * 111 }
Console
$ browserify -r ./x.js > bundle.js
then in your html:
<script src="bundle.js"></script>
<script>
var x = require('./x.js');
console.log(x(3))
</script>
will print 333.
In your case, just do browserify -r app to expose require('app') to the external context.

Related

Assign namespaces to src library files

I have a website made in ReactJS. The website calls functions of a library analyzejs generated by another programming language. I can only call functions of this library; I cannot modify much the content of this library.
So far, I have been using the first version of the library: analyzejs-v1.js. To achieve this, in frontend/public/index.html of the website, I have
<head>
<script src="/lib/analyzejs-v1.js"></script>
<!-- <script src="/lib/analyzejs-v2.js"></script> -->
</head>
<body>
<div id="root"></div>
</body>
I also have a type declaration file frontend/src/defines/analyzejs-v1.d.ts as follows:
declare function f1(): string;
declare function f2(): string;
...
As a result, in the code of the website, I could call directly f1() to use analyzejs-v1.js.
Now, I would like to add another version of the library analyzejs-v2.js. In some part of the website, I want to call f1() of analyzejs-v1.js; in some part of the website, I want to call f1() of analyzejs-v2.js. So I guess I need to add namespaces such as v1 and v2 to these different versions to avoid conflict. Then, I will be able to call v1.f1() and v2.f2().
I tried to modify frontend/src/defines/analyzejs-v1.d.ts as follows:
declare namespace v1 {
function f1(): string;
function f2(): string;
...
}
And in the code of the website, I tried to use v1.f1().
The compilation did not raise any error. However, running the website and using features calling v1.f1() returned me an error on v1.f1(): Unhandled Rejection (ReferenceError): v1 is not defined.
Does anyone know how to add a namespace to such a library?
The best way would be to group the functions inside your javascript files into classes or objects
// test.js
const testNamespace = {
print = () => console.log('print was called from test.js')
}
// test1.js
const anotherNamespace = {
print = () => console.log('print was called from test1.js')
}
now you can call these functions using
anotherNamespace.print();
testNamespace.print();
If you import these files in your code like so:
import analyzejs-v1 from '/lib/analyzejs-v1.js'
import analyzejs-v2 from '/lib/analyzejs-v2.js'
in your code you can call them under different objects:
analyzejs-v1.f();
analyzejs-v2.f();
Problem - you are only using typescript type declarations, which are deleted on compilation, completely not doing anything at runtime. JavaScript actually doesn't support namespaces, only global scope, function scope and block scope.
Solution #1 - using es6 import syntax, which creates a function scope around the imported script (referred to as a module). And even better, React supports dynamic imports, which gives you both a module "namespace", and automatic code splitting, which could help with your heap memory issues:
// Page1.tsx
import("/lib/analyzejs-v1.js").then(module => {
module.f1();
module.f2();
});
// Page2.tsx
import("/lib/analyzejs-v2.js").then(module => {
module.f1();
module.f2();
});
The dynamic import splits the JS bundle so that the dynamically required JS parts are in separated files, and run only when import() is called. This could help with really large files and memory issues.
import() returns a promise, which gets the module as an argument when resolved.
Solution #2 - use the import syntax import * as moduleName from "module/url". This import method contains the functions in the module to an object with the name of your choosing, but everything will be in the main bundle as apposed to dynamic imports:
import * as moduleV1 from "/lib/analyzejs-v1.js";
import * as moduleV2 from "/lib/analyzejs-v2.js";
moduleV1.f1();
moduleV1.f2();
moduleV2.f1();
moduleV2.f2();
So you want to wrap different versions of a vanilla javascript library into a bundled script library and selectively call either in a browser?
This solution uses npm, browserify, require and module.exports.
The strategy is to add a module.exports to the vanilla libraries and then bundle them together as globals v1 and v2.
So setup a folder for building the bundled libraries.
$ mkdir bundle-analyzers
$ cd bundle-analyzers
$ npm init
package name: (bundle-analyzers)
version: (1.0.0)
description: bundle versions of analyzejs with browserify
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
Create a lib folder and copy the analyzejs files into it
+--bundle-analyzer
| +-- lib
| | analyzejs-v1.js
| | analyzejs-v2.js
*
For simplicity, let's assume both analyzejs versions have a common method named version(). Let's expose both
by adding the following to the end of each analyzejs-v*.js file:
module.exports = {
version
}
Create a file analyzejs-bundler.js which will expose both library's exports globally:
var v1 = require("./lib/analyzejs-v1.js");
var v2 = require("./lib/analyzejs-v2.js");
window["v1"] = v1;
window["v2"] = v2;
Install browserify in the root folder
$ npm i browserify
Add an index.html to the project folder which will demo the bundle:
<html>
<head>
<script src="./bundle/analyzejs-bundle.js"></script>
</head>
<body>
<pre id="versions"></pre>
<script>
window.addEventListener('load', () => {
document.getElementById('versions').innerText = [v1.version(), v2.version()].join('\n');
});
</script>
</body>
</html>
Now add a script to package.json to create the bundle:
"scripts": {
"bundler": "browserify ./analyzejs-bundler.js -o ./bundle/analyzejs-bundle.js"
}
Create the bundle
$ npm run bundle
OK so the folder structure should be
+--bundle-analyzer
| +-- bundle
| | analyzejs-bundle.js
| +-- lib
| | analyzejs-v1.js
| | analyzejs-v2.js
| +-- node_modules
| | { node stuff }
| analyzejs-bundler.js
| index.html
| package-lock.json
| package.json
*
Open up index.html from the file system and verify.
Of course if there a zillion exports from the analyzejs library something more sophisticated will be required.
Note it is also possible to create a monolith for versions of analyzejs which may be more suitable:
var v1 = require("./lib/analyzejs-v1.js");
var v2 = require("./lib/analyzejs-v2.js");
window["analyzejs"] = { v1, v2 }
YMMV. The ambient definitions for Typescript depend on the global variables defined and are simply a tedious sideshow to the solution IMO.

Browserify npm library

I have a well tested npm library (https://www.npmjs.com/package/yuml-diagram) that I would like to Browserify so it can be used in browser applications.
The full source code is here: https://github.com/jaime-olivares/yuml-diagram
I managed to build the library as a monolithic package with the following command line:
browserify index.js -g uglifyify -o ./dist/yuml-diagram.min.js
Then I tried to use it in a similar fashion as in Node-JS, as suggested in several places:
<html>
<head>
<script src="https://gist.githubusercontent.com/jaime-olivares/5cd18b40f2bdcf5e403ed78d181c3d85/raw/00f5624fe30500a22144962184e927236f1ac45f/yuml-diagram.min.js"></script>
<script>
function loadSvg()
{
var yuml_diagram = require('yuml-diagram');
var yumlText =
`// {type:sequence}
[:Computer]async test>>[:Server]
[:Computer]sync test>[:Server]`;
var yuml = new yuml_diagram();
var svg = yuml.processYumlDocument(yumlText, false);
document.body.innerHTML = svg;
}
</script>
</head>
<body onload="loadSvg();">
</body>
</html>
The require() function is not recognized, even if I use the flag --exports require in Browserify.
How can I invoke the library's processYumlDocument() function from the application script?
Browserify does not add support for require onto your page. Browserify is used on a javascript file that is using resolve internally and produces a version with the resolves statically included.
In your example you should move the content of your script block into a javascript file and then execute browserify on that file. Then include the final produced file in your page.
Found my own answer. It is required the standalone parameter in Browserify, as here:
browserify index.js --standalone yuml_diagram -g uglifyify -o ./dist/yuml-diagram.min.js
Where yuml_diagram represents the whole bundle. Then the library can be used with a couple of lines:
<html>
<head>
<script src="../dist/yuml-diagram.min.js"></script>
<script>
function loadSvg()
{
var yumlText =
`// {type:sequence}
[:Computer]async test>>[:Server]
[:Computer]sync test>[:Server]`;
// Create the object and invoke a function inside it
var yuml = new yuml_diagram();
var svg = yuml.processYumlDocument(yumlText, false);
document.body.innerHTML = svg;
}
</script>
</head>
<body onload="loadSvg();">
</body>
</html>

Using NPM package in the browser with Browserify

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.

ReferenceError: "require" is not defined

I am a beginner to node js and angular js.
I have a string which is html text and i want to convert it to jade.
I can do this with node js module 'html2jade' but when I am writing my code in js file (in controller), it is giving me a 'require' is not defined error.
This is the code:
app.controller('comicController', function($scope, resources) {
$scope.searchComic = function() {
resources.routes.charactersAPI.fetch({title: $scope.title}, function done(response) {
console.log(response);
$scope.comic = response;
//$scope.comic.description
require('html2jade').convertHtml($scope.comic.description, {}, function (err, jade) {
$scope.desc = jade;
});
});
};
});
Any help is appreciated. Thanks in advance!
You require a module on your build process.
Browsers don't have the require method defined, but Node.js does. With Browserify you can write code that uses require in the same way that you would use it in Node.
Install browserify
npm install -g browserify
Write a module
// hello.js
module.exports = function(name) {
return 'Hello ' + name + '!';
}
Use the module
// app.js
var greetings = require('./hello');
alert(greetings('Christophe'));
Create the bundle
browserify app.js -o bundle.js
Refer your bundle
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>
Browserify is a tool that lets you bundle node.js modules and consume them in the browser. In other words, it allows you to write browser-based applications using node.js-style requires.
You can check here for reference.

How to launch the script that was Browserified from a browser

I browserified a module that takes a value and returns a new one.
The original .js file was:
module.exports = function (term) {
return term + ' blabla';
}
If I want to call it from Node.Js I'd simply include it as in
var foo = require('./my-file.js');
foo('no'); // returns 'no blabla'
But how do I call this same function from a browser if I include the browserify-generated file in <script src="/javascripts/new-file.js"></script>?
Thank you!
You want to use the --standalone flag for browserify. From the documentation:
Generate a UMD bundle for the supplied export name. This bundle works with other module systems and sets the name given as a window global if no module system is found.
So if you use the --standalone flag,
browserify --standalone my_global_name my-file.js > new-file.js
you will be able to use the window.my_global_name property to access your function.
You need to compile with the -r flag, to set it up as exposing for requiring.
browserify -r my-file.js > new-file.js
Then, in your script, you should be able to do:
var foo = require('./my-file.js');
foo('no'); // returns 'no blabla'
For more information, you can read the documentation.

Categories