Javascript, multi file modules and Require.Js - javascript

I am designing a not-trivial application in Javascript.
From what i read so far a common practice is to avoid cluttering the global namespace by defining everything into modules.
And for convenience and code clarity a module can be divided into separate files using the module Augmentation pattern
var MODULE = (function (my) {
// functions, objects, etc ...
return my;
}(MODULE || {}));
Now when having many modules and module dependencies, require.Js seems like a promising tool to add order, decoupling and cleaner namespace. having all modules loaded asynchronously and make sure they run only after their dependencies are ready.
define(["depenencyModule1", "depenencyModule2"],
function(depenencyModule1, depenencyModule2) {
// define MyModule
return MyModule;
}
);
This usage however interferes with the module augmentation pattern from before, at first it seems like i am using it wrong but then i went through require.js documentation and found this:
"Only one module should be defined per JavaScript file, given the nature of the module name-to-file-path lookup algorithm."
So now i am confused, If i write my module to a single file it will be huge and maintainable, doesn't that make require.js useless?
Or perhaps Javascript concept of a module is a tiny bit of code compare to modules in other languages ?

RequireJS allows you to have a facade module which is implemented as a group of RequireJS modules. For instance, you could have:
define(function (require, exports, module) {
'use strict';
var foo = require("./foo");
var bar = require("./bar");
for(var prop in foo) {
exports[prop] = foo[prop];
}
for(var prop in bar) {
exports[prop] = bar[prop];
}
});
This module exports everything from foo and bar. From the point of view of someone importing it, it looks like a single module, even though three RequireJS modules are involved (the facade, and the two modules it imports).
Another thing I've done is declare a single class across multiple modules. I might have a foo module that exports the class Foo:
define(function (require, exports, module) {
'use strict';
var core = require("./foo_core");
require("./foo_init");
require("./foo_gui");
...
exports.Foo = core.Foo;
});
The foo_core module actually defines the class:
define(function (require, exports, module) {
'use strict';
function Foo () {
// ...
}
Foo.prototype.bar = function () { ... };
exports.Foo = Foo
});
Other modules add to it. foo_init:
define(function (require, exports, module) {
'use strict';
var Foo = require("./foo_core").Foo;
Foo.prototype.init = function () { ... };
});
foo_gui:
define(function (require, exports, module) {
'use strict';
var Foo = require("./foo_core").Foo;
Foo.prototype.render = function () { ... };
Foo.prototype.erase = function () { ... };
});
I've used both methods above to split code which from the standpoint of the API should appear as a single module but is implemented across multiple files.

Related

How to import a Backbone/RequireJS module into a React/Webpack app

We have a legacy Backbone application that we are moving over to React. An interim step we are attempting is loading a bundled Backbone module into a React page, until we have time to fully rewrite it. I am halfway there, I can bundle up the app and all its dependencies with r.js using a config like this:
({
...
baseUrl: './',
name: 'myapp',
paths: {
'myapp': './legacy/app'
},
out: 'src/appbuilt.js'
...
})
The module is set up like this:
define(function(require) {
var $ = require('jquery'),
_ = require('underscore'),
...
templates = $(require('text!templates/app.html')),
app = {};
app.View = .....
app.Model = .....
return app;
});
That bundle works on the Backbone side. Next I need to turn that into something I can import into React and render. I am trying things like this:
npx babel src/appbuilt.js --out-file src/appbuilt.es6.js --plugins=#babel/transform-modules-umd
Which works to give me a UMD module, but importing it like this:
import * as legacyapp from '../../appbuilt.es6';
Gives me warnings on the build like:
Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
And errors on page load that are probably symptoms of something:
Uncaught TypeError: name.split is not a function
What is the secret sauce to get my module converted into something I can use? I am open to modifying how the Backbone app does its imports, or making a wrapper of some kind that is more easily translatable.
I am not sure but I can guess that the format of your AMD modules is the problem here. Try converting them to regular AMD modules:
define(
['jquery', 'underscore', 'text!templates/app.html' /* add rest of dependencies here */],
function ($, underscore, templates /** add parameters for rest of dependencies here */)
{
var app = {};
// app.View = ...
// app.Model = ...
return app;
}
);

Separating functionality out into separate index.js files

I'm collaborating on a new NodeJS module with a colleague and the file structure is as follows:
index.js
let MyModule = require('./src/index.js);
MyModule = new MyModule();
module.exports = MyModule;
src/index.js
const depA = require('depA');
const MyModule = function MyModule() {
this.doStuff = function doStuff() {
console.log('doing stuff...)
}
}
module.exports = MyModule;
I'm wondering if there is any need/value to split into these two index.js files and whether it would be better to have just one top level file. Is this considered an anti-pattern? I did wonder if this approach works better when you have a complex library with functionality split out across many feature files.

Importing all exports in a module NodeJS

I want to be able to access all exports of a module without having to say module. before the export.
Let's say that I have a module:
// mymod.js
module.exports.foo = function() {
console.log("foo!");
}
module.exports.bar = "bar!";
And a main file:
// main.js
var mymod = require("./mymod.js");
mymod.foo();
Is there a way to call foo() without needing to say mymod. before? This can be achieved in python by saying import module as *.
What is the NodeJS equivalent to this?
In ES6 you can import modules in the following ways
import moduleName from "path/to/module"; // import default export from the file as moduleName object, moduleName can be anything
import { exportMemberName1, exportMemberName2, ... } from "path/to/module"; // destructured import, it will destructure import and can access the export module without prefixing anything
import * as moduleName from "path/to/module"; // import everything exported from the file as moduleName object, you can access every export members from that object, moduleName can be anything
These are the only methods provided by ES6 to import module (you can also use require).
If you have to import 100s of modules best ways is first method, import everything as an object and destructure on the go, I meant if you have lots of functions or methods, destructure what you want in that function in side that function, eg.
import * as moduleName from "path/to/file";
function function1(){
const { exportMember1, exportMember2 } = module;
}
function function2(){
const { exportMember1, exportMember5, exportMember7 } = module;
}
I want to be able to access all exports of a module without having to
say module. before the export.
Use the shorthand:
exports.myVar = myVar
exports.foo = () => {}
Or use an Object:
module.exports = {
foo,
myVar
}
// main.js
var mymod = require("./mymod.js");
mymod.foo();
Is there a way to call foo() without needing to say mymod. before?
This can be achieved in python by saying import module as *. What is
the NodeJS equivalent to this?
Use destructuring:
const { foo } = require("./mymod.js")
lets say that I have 100 exports in a file. Do I need to put commas
after every import inside the { }? There must be a better way to do
this
If you have 100 exports why would you want to import them all globally as their own functions? myMod.func is better for clarity.
A hacky workaround might be to do const myMod = require('myMod') then map it putting the functions on the global object. Or put them on the global from the start instead of exporting it.
You can use ES6 destructuring:
var { foo } = require("./mymod.js");
foo();
I have a situation where a I have a tiny-but-not-that-tiny generic utilities that is used along a couple of modules (all it's functions are used), in which there is a decent amount of modules already loaded. This functions are obviously named in a way you know there are a part of a generic utilities modules, so the "module.function" it's redundant, does not improve the readeability of the code. So, I prefered to mimick the "import * from module" of Python. Note that this is the first time I come across this situation, therefore, IMO, this mechanism, in almost every case, is not a good practice at all. The only way to do that, is iterating over the exports of the module, and adding the functions to the global object. I made a function to make the intention clear.
const importAll = () => {
return {
mod: null,
from(modName) {
this.mod = require(modName);
Object.keys(this.mod)
.forEach(exportedElementId => global[exportedElementId] = this.mod[exportedElementId]);
}
}
}
And it is used like this:
importAll().from('module-name');
Note that this only works if the module exports an object. Wont work if the module exports, for example, an array.
Here is another way, which may be a bit cleaner and more convenient in some cases: method importAll() is implemented inside export-heavy module, so it might be called immediately after require(), making this call very brief.
This works really well for large modules stuffed with simple standard functions and constants that are used across multiple projects.
Example:
// module.js
'use strict';
function func1() { return '4'; };
function func2() { return 2; };
function importAll() { delete this.importAll; Object.assign(global, this); };
module.exports = { func1, func2, importAll };
Then, in the main app, module can be unwrapped as follows:
// app.js
'use strict';
require('./module').importAll();
console.log("result: '%d'", func1() + func2());
There are few caveats though:
since properties/methods are added to global object, those might override some existing properties/methods, so be careful with naming.
those properties/methods will become available EVERYWHERE: in all modules, sub-modules, etc, so no need to call require() more than once.

How to access an AMD module ("define") from ordinary JavaScript?

I made a TypeScript code which was compiled like this:
define("Global/Global", ["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Global = (function () {
function Global() {
}
Global.transition_time = 200;
return Global;
}());
exports.Global = Global;
});
Now, in a classic "script.js" I'm trying to console.log() the "transition_time" value. But it tells me "Global is not defined".
I put a breakpoint in the console in the Global's function, but it's never triggered.
EDIT :
That's the Global's TypeScript code:
export class Global {
static transition_time: number = 200;
}
It looks like you have an AMD module there, so you'll need to use an AMD module loader such as RequireJS if you aren't using one already. Then the proper syntax to access your module from script.js is like this:
require(["Global/Global"], function(Global_module) {
console.log(Global_module.Global.transition_time);
});
The require function doesn't add anything to the global namespace; instead, you have to pass a callback that receives the module you asked for and does whatever you wanted with it. Note the Global_module.Global.transition_time; Global_module is the name I gave to the variable that receives the entire module, and Global is the name of the exported class within the module.

browserify 'require' placement

Usually all dependencies are required in the head of the file.
var Backbone = require('backbone');
var $ = require('jquery');
I wonder if it is correct to require in code body. Example:
template: require('./home.tpl.hbs'),
module.exports = Backbone.View.extend({
template: require('./home.tpl.hbs'),
render: function () {
...
return this;
}
});
It's going to work, but require function is taken from nodejs world (CommonJS modules) and you can read about best practices in its usage here. So my point is that you need to add local variable at the beggining of the file and then use it wherever you want.

Categories

Resources