Is there a way to create a javascript micro-library (a library that has no dependencies), that support all of the following module formats:
Asynchronous Module Definition
CommonJS
exposing the library's exports as a global namespace object (no loader)
Yes, and I owe this answer to ded and his awesome modules:
(function(name, definition) {
if (typeof module != 'undefined') module.exports = definition();
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition);
else this[name] = definition();
}('mod', function() {
//This is the code you would normally have inside define() or add to module.exports
return {
sayHi: function(name) {
console.log('Hi ' + name + '!');
}
};
}));
This can then be used:
in AMD (e.g. with requireJS):
requirejs(['mod'], function(mod) {
mod.sayHi('Marc');
});
in commonJS (e.g. nodeJS):
var mod = require('./mod');
mod.sayHi('Marc');
globally (e.g. in HTML):
<script src="mod.js"></script>
<script>mod.sayHi('Marc');</script>
This method needs to get more publicity - if jQuery and co. started using it life would be much easier!
Here is a list of various cross-compatible module formats.
I suspect that the one you're looking for is what they're calling "commonjsStrict.js"
uRequire, the Universal Module & Resource Converter is the tool that does exactly that.
It mainly converts AMD and CommonJS to UMD / AMD / CommonJS / Plain script (no AMD loader required).
It allows declarative exporting of modules, with a noConflict() baked in.
It can manipulate modules (inject/replace/remove dependencies OR code) as you build them.
It converts from coffeescript, coco, Livescript, icedCoffeescript and you can add your own conversions in one liners!
Just to update a little bit on this answer in regards to #marc I too give credit to ded and have updated it a bit to be with the latest updates:
(function (name, definition, context, dependencies) {
if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); }
else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); }
else { context[name] = definition(); }
})('events', function () {
// Insert code here
return {
sayHi: function(name) {
console.log('Hi ' + name + '!');
}
};
}, (this || {}));
Object at the end is a reference to either the parent or the current scope, lets say you have a package you are writing and this is just a piece of the pie, well that context could be a name-spaced object and this is just a slice of that pie.
Also, if you wish to have dependencies, there is an optional parameter at the end after your scope which supports an array, in this case the definition parameter then can utilize each dependency as a argument. Also, the dependencies listed in an array will be required inside node-js platform for your convenience sake.
See: https://gist.github.com/Nijikokun/5192472 for a real example.
I have solved this exact problem and managed to easily support:
Dojo AMD (referencing the RequireJS specs)
jQuery (under $/jQuery.fn.[your_library_here])
node.js using vanilla require('path_to.js')
Browser window.[your_library_here]
It's using a combination of dependency injection and IIFE to get the job done.
See Below:
/*global jQuery:false, window:false */
// # A method of loading a basic library in AMD, Node.JS require(), jQuery and Javascript's plain old window namespace.
(function(exporterFunction) {
exporterFunction('cll',
function(a,b) {
return a+b;
}
);
})(
(function() { // Gets an exportFunction to normalize Node / Dojo / jQuery / window.*
if ((typeof module != 'undefined') && (module.exports)) { // Node Module
return function(library_name,what_was_exported) {
module.exports = what_was_exported;
return;
};
}
if (typeof define != 'undefined' && define.hasOwnProperty('amd') && define.amd) { // Dojo AMD
return function(library_name,what_was_exported) {
define(function() {
return what_was_exported;
});
};
}
if (typeof jQuery === 'function') { // jQuery Plugin
return function(library_name,source) {
jQuery.fn[library_name] = source;
return;
};
}
if (typeof window != 'undefined') { // Fall down to attaching to window...
return function(library_name,what_was_exported) {
window[library_name] = what_was_exported;
};
}
})(),
(function() {
// ## Other Parameters Here
// You could add parameters to the wrapping function, to include extra
// functionalilty which is dependant upon the environment... See
// https://github.com/forbesmyester/me_map_reduce for ideas.
return 'this_could_be_more_arguments_to_the_main_function';
})()
);
Public Gist available at https://gist.github.com/forbesmyester/5293746
This is based on Nijikokun's answer. Since RequireJS discourages the use of explicit module names this has been omitted in this version. The second argument to the loader describe the dependencies. Pass [] if you don't need to load any.
var loader = function(name, dependencies, definition) {
if (typeof module === 'object' && module && module.exports) {
dependencies = dependencies.map(require);
module.exports = definition.apply(context, dependencies);
} else if (typeof require === 'function') {
define(dependencies, definition);
} else {
window[name] = definition();
}
};
loader('app', ['jquery', 'moment'], function($, moment) {
// do your thing
return something;
}
I've posted a solution to github that should work with any browser. Its based on how jQuery and underscore.js implemented their libraries but added a private anonymous object allowing you to use this to reference its private namespace; making for easy automated extension and scoping. Of course it has an _export object to expose your public APIs.
I also posted sample of how to define an internal library that has similar private scope and public exports.
My code is posted here: https://github.com/JavaScriptDude/JSLibraryTemplate
Note: I have not tested outside of the browser but it should work in node.
Related
I use the Refactor function of vscode to rename the variable name. I found that there is no problem with the smart modification of the global variable or the local variable, but the property of the object does not take effect. Is there any method or extension that can do this?
Vscode info:
Version: 1.36.1 (user setup)
Submit: 2213894ea0415ee8c85c5eea0d0ff81ecc191529
Date: 2019-07-08T22:59:35.033Z
Electron: 4.2.5
Chrome: 69.0.3497.128
Node.js: 10.11.0
V8: 6.9.427.31-electron.0
OS: Windows_NT x64 10.0.18922
g = se.prototype;
g.eb = function (a) {
this.pf = this.Sr(); //this.Sr will be refactored
a = this.cu(a);
return Pc(a, null)
};
g.au = function (a) {
this.pf && "*" == this.Me && (a.id = this.pf)
};
g.Tt = function (a) {
if (!this.zi)
return a;
a = de("<div>" + a + "</div>");
he(a);
return a.innerHTML
};
g.Sr = function () { //want to smart rename the g.Sr to g.sanitizer
var a = !("STYLE" in this.Pe) && "STYLE" in this.Qe;
return "*" == this.Me && a ? "sanitizer-" + (Math.floor(2147483648 * Math.random()).toString(36) + Math.abs(Math.floor(2147483648 * Math.random()) ^ Ja()).toString(36)) : this.Me
};
VS Code's built-in JavaScript IntelliSense is not able to understand that code, which is why rename does not work. The code relies a lot on dynamic behavior such as assigning g to se.prototype and then further writing properties on to g. The IntelliSense engine just can't understand that this.Sr inside eb refers to the same g.Sr below (it's also not obvious what is going on to a programmer at first glance either)
You can investigate this by hovering over various variables in your source. If the hover information shows any, it means that the VS Code IntelliSense engine cannot properly infer what type the variable is so semantic operations like rename will not work. You can also add // #ts-check to the top of your file to enable some basic type checking of your code
For the existing snippet however, your best bet is to do a text based find/replace.
I'm looking to open source an angular directive on npm and I'm trying to come up with the most universal pattern for doing so. How's this? I have 3 questions:
!function(name, make) {
make = make()
// 1. Is this line needed?
var angular = require('angular')
// 2. Is this line needed?
angular.module(name, []).directive(name, make)
if (typeof module != 'undefined') module.exports = make
else this[name] = make
// 3. Is this line needed?
if (typeof define == 'function') define(function() { return make })
}('exampleDirective', function() {
return function() {
return {
link: function (scope, label, atts) {}
}
}
});
Is require('angular') needed or is it safe to assume the angular variable exists?
Is it necessary to call angular.module and angular.directive in my definition or should only the consuming app(s) do this?
Do AMD environments need this or does the module.exports or global suffice?
1
// 1. Is this line needed?
var angular = require('angular')
No. Applications that use your library must always import their own version of AngularJS.
2
// 2. Is this line needed?
angular.module(name, []).directive(name, make)
Yes. Applications will need to list your module name in their list of dependencies like this:
var myApp = angular.module('myApp',[name]);
3
// 3. Is this line needed?
if (typeof define == 'function') define(function() { return make })
}('exampleDirective', function() {
return function() {
return {
link: function (scope, label, atts) {}
}
}
});
No. You can just put the directive on the module and other developers will be able to use it.
im an javascript newbie and google didnt helps:
I tryed to load ByteBuffer.js in an require.js module:
define(['js/ByteBufferAB'], function (ByteBufferAB) {
var MessageBase = function () {
this._version = 0; // unsinged int 16 bits
this._dataType = "";
};
MessageBase.prototype.toBytes = function () {
//console.log( new ByteBufferAB(58));
var headerBytes = new ByteBufferAB(58); // <-- here comes the error
headerBytes.clear();
return headerBytes;
};
return MessageBase;
});
with the same syntax math.js is properly loaded.
But with ByteBufferAB.js the following error comes:
Uncaught TypeError: undefined is not a function
What am I doing wrong?
Thank you for your help
In your define call you refer to the module as js/ByteBufferAB so RequireJS looks for a module named js/ByteBufferAB. However, the module defines itself as ByteBuffer:
/* AMD */ else if (typeof define === 'function' && define["amd"])
define("ByteBuffer", ["Long"], function(Long) { return loadByteBuffer(Long); });
Because the module name is hardcoded, you need to have a mapping like this in your paths in the configuration you give to RequireJS:
ByteBuffer: "js/ByteBufferAB"
and you need to refer to the module as ByteBuffer in your define call.
None of this would be required if the developers for this library had not hardcoded a name but they have, and so here we are.
I got js code from a design company, but I do not understand the reason for passing a function to a self executing function.
Here is the outline of the code.
(function(core) {
if (typeof define === "function" && define.amd) {
define("abc", function() {
var abc;
abc = window.Abc || core(window, window.jQuery, window.document);
abc.load = function(res, req, onload, config) {
var base, i, load, resource, resources;
resources = res.split(",");
load = [];
base = (config.config && config.config.abc && config.config.abc.base ? config.config.abc.base : "").replace(/\/+$/g, "");
if (!base) {
throw new Error("Please define base path to Abc in the requirejs config.");
}
i = 0;
while (i < resources.length) {
resource = resources[i].replace(/\./g, "/");
load.push(base + "/components/" + resource);
i += 1;
}
req(load, function() {
onload(abc);
});
};
return abc;
});
}
if (!window.jQuery) {
throw new Error("Abc requires jQuery");
}
if (window && window.jQuery) {
core(window, window.jQuery, window.document);
}
})(function(global, $, doc) {
var _c = {};
...
return _c;
});
Is there benefit of writing code such way over something like below?
(function( core, $, undefined) {
...
} (window.core= window.core|| {}, jQuery )};
Is this some advanced technique?
Basically, ....kinda.
In Javascript, functions are treated as first-class objects. This means you can pass them around in variables and whatnot. The first part, (function(core) { ... }), creates an anonymous function, taking a single argument called core. The parentheses around the function basically just resolve to a function. The second part, (function(global, $, doc) { ... }), is creating another function, which is passed immediately into a call to the first function as the value of core.
Put this way, here's what's happening.
// Define the first function (the one that takes core)
var firstFunc = function (core) { /* ... */ };
// Define the second function (the one that takes global, $, and doc)
var secondFunc = function (global, $, doc) {
var _c = {};
/* ... */
return _c;
};
// Call the first, passing in the second.
firstFunc(secondFunc);
The above code and the code you posted accomplish the same thing. One purpose for writing something like this would be to sandbox the second function so that the first can specify its own local versions of variables global, $, and doc, which avoids conflicts with, say, active-running versions of jQuery (which typically declares its own globally-scoped $ variable).
EDIT: Now that the code in the first function has been filled in, we can say for sure that the reason for doing this is to resolve dependencies and ensure they are present before manually passing them into the second function. From the look of the code provided, it appears that this is enforcing the presence of abc (which I'm assuming is some dependency) via require.js, as well as ensuring that jQuery is present. In addition, it looks like the values in _c returned from the function are used as a part of that dependency enforcement process, though I can't tell exactly how by looking at it.
my current plugin is getting really big (just over 8000 lines right now) and i would like to know if there is a way to sort it into different files.
Just liike the require function in node.js, but for jquery, so it would be sorted into more files and thus, be more clearly arranged.
As #jcubic mentioned you need to have your code separated into individual modules / functionality purposes.
Store all of your methods in a method object of some sort (this could also be within the plugins namespace of course). This can also easily be added to, or even extended from a different file.
var methods = (function ($) {
return {
init : function () { initMethod(); },
another : function () { anotherMethod(); },
thirdThing : function () { thirdThing(); },
etcEtc : function () { etcEtc(); }
};
}(jQuery));
I highly recommend the method calling way of creating jQuery plugins, which would utilize this object.
$.fn.pluginName = function (method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.tooltip');
}
};
Now everything is separated, and you call your modules by doing $('whatever').pluginName('methodhere'); etc