I would like to create my modules in a way, that they can be used with and without requirejs (without require js they should just work normally, so I have to make sure they load correctly, like putting script tags in the right order).
So jQuery does it kindof like this:
// export module
if ( typeof define === "function" && define.amd ) {
define(["dep"], function(dep){
dep.fn.test = test;
return dep;
});
}
else{
dep.fn.test = test;
}
The actual module is defined as like so
var dep = function(...){...}
This definition and the export part is within an IIFE to keep everything in out of the global scope.
Generally it works well with one exception, the dependency is not available.
This problem can be solved by defining the function within the define part, but this would mean defining it twice, in the define part and below in the else part.
How can I get this to work but only define the module once?
I have "plugin-like" extensions to a core dep which should all be in separate files so the main dep must be passed as a depenency
This works fine btw. but it would mean I write the code for test twice.
(function(){
// export module
if ( typeof define === "function" && define.amd ) {
define(["dep"], function(dep){
dep.fn.test = function(){...ssomething using dep...};
return dep;
});
}
else{
dep.fn.test = unction(){...ssomething using dep...};
}
})
Okay, I try another example
animate/animate.js (this is my main file)
define(function(){
...
return animate;
});
animate/modules/easing.js (this is a module file)
(function(){
var ease = function(){
// using animate main function from animate.js
// animate is not available here
...
};
if ( typeof define === "function" && define.amd ) {
define(["animate/animate"], function(animate){
// animate is available here
...
animate.fn.ease = ease;
return animate;
});
}
else
{
// if no requirejs, animate is global
animate.fn.ease = ease;
}
});
I think you're just writing the define incorrectly and so it's not getting registered. This is what I use.
if (typeof define === "function" && define.amd) {
define("telegraph", [], function () { return telegraph; });
}
Put in context
(function(window) {
var telegraph = function() { };
telegraph.prototype.test = function() {
// do something
};
if (typeof define === "function" && define.amd) {
define("telegraph", [], function () { return telegraph; });
}
window.telegraph = telegraph;
})(window);
EDIT
Do the question is really how do you define test and make use of dep internally so that you don't have to supply it as a dependency and can define a named dep module. One solution is to register the second-level functions in the constructor and capture this as self (or another variable) to use within the function. The key thing here is that you use define to define the named module and by using the captured context in the constructor, you don't need to supply the parent object as a dependency. Example (with working fiddle at http://jsfiddle.net/YS8v6/):
(function(){
var dep = function() {
var self = this;
self.fn.test = function() {
self.foo();
};
};
dep.prototype.foo = function() {
alert('foo');
};
dep.prototype.fn = function() {
};
if ( typeof define === "function" && define.amd ) {
define('dep', [], function() { return dep; });
}
})();
The actual problem seems to be that define is not available within the IIFE but window.define is. So passing define as an argument to the IIFE solves the problem.
(function(define){
// export module
if ( typeof define === "function" && define.amd ) {
define(["dep"], function(dep){
dep.fn.test = function(){...ssomething using dep...};
return dep;
});
}
else{
dep.fn.test = unction(){...ssomething using dep...};
}
}(window.define))
Before it would check for define, not find it and immediately try to attache it to dep.fn.test without the requirejs define part.
Related
I'm making a library which exports one function (make) on global namespace (app) for defining a module or referencing it, similar to what angular.module does.
When I call it, I make sure to not store any reference to the make(name, [deps]) call so it will always use the main app object.
Usage is like this:
// Define 'hello'
app.make('hello', []);
// Define 'one' in 'hello'
app.make('hello').
data('one', 'thing');
// Multiple calls
app.make('hello').
data('two', 'thing').
attr('third', 'no').
data('four', 'empty');
And I want the above code turn into it:
app.make('hello', []).
data('one', 'thing').
data('two', 'thing').
attr('third', 'no').
data('four', 'empty');
So it should turn multiple separated calls to the return from make into just a big one (order doesn't matter and there are no side effects).
What I tried:
I'm planning to use esprima, estraverse and escodegen, here's what I actually have:
const fs = require('fs'),
esprima = require('esprima'),
estraverse = require('estraverse');
const modules = Object.create(null);
fs.readFile('sample.js', 'utf8', (err, data) => {
const tree = esprima.parse(data);
// Find module definitions
estraverse.traverse(tree, {
enter(node) {
const args = node.arguments;
if (isDefinitionCall(node) && args.length == 2) {
modules[args[0].value] = {
node,
childs: []
};
}
}
});
// Find module usages
estraverse.traverse(tree, {
enter(node) {
if (isGetterCall(node)) {
// What to store here?
// And how to modify the AST to turn
// everything into just chained call?
}
}
});
console.log('Modules found: ' + Object.keys(modules).join(', '));
});
function isDefinitionCall(node) {
return node.type == 'CallExpression' &&
node.callee &&
node.callee.object &&
node.callee.property &&
node.callee.type == 'MemberExpression' &&
node.callee.object.name == 'app' &&
node.callee.object.type == 'Identifier' &&
node.callee.property.type == 'Identifier' &&
node.callee.property.name == 'make';
}
function isGetterCall(node) {
return node.type == 'CallExpression' &&
node.callee &&
node.callee.object &&
isDefinitionCall(node.callee.object);
}
My question is: how can I move around the AST and get what I want done?
Thanks in advance!
I have the following JavaScript pattern for creating a simple plugin:
(function(window, document, $) {
function method_1(){
}
function method_2{}
{
}
})(window, document, jQuery);
I want to have the ability to access my plugin and plugin methods in the following way:
myplugin.method_1();
myplugin.method_2();
How do I update my existing plugin pattern to enable this?!
NOTE: It has to maintain a self-executing format i.e. no variable declarations.
You could try something like this fiddle, which returns an object that includes the public functions for the plugin.
var myPlugin = (function(window, document, $) {
function privateFunction() {
}
return {
method_1: function () {
},
method_2: function () {
privateFunction(); // This works
}
};
}(window, document, jQuery));
myPlugin.method_1();
myPlugin.method_2();
myPlugin.privateFunction(); // This will throw an error
The following pattern seems to work perfectly for my requirements:
(function(root, name, make){
var $ = root['jQuery'] || root['Zepto'];
if (typeof module != 'undefined' && module['exports']) module['exports'] = make($);
else root[name] = make($);
}(window, 'myPlugin', function($) {
function method_1(){
}
function method_2{}
{
}
var myPlugin = {
method_1: method_1,
method_2: method_2
};
return myPlugin;
}));
I'm trying to create a global function where I can use it anywhere inside a .js file.
We have more than 50 javascript files joined together and inside each files I want to be able to use this library anywhere.
Localized.js
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like enviroments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
if (typeof Localized !== 'undefined') {
throw 'Localized" already in use';
}
root.Localized = factory();
}
}(this, function () {
var _strings,
_readyCallback,
_isReady = false,
_requestedStrings = false;
function ready( data ) {
_readyCallback = _readyCallback || function(){};
function domReady() {
// If the DOM isn't ready yet, repeat when it is
if ( document.readyState !== "complete" ) {
document.onreadystatechange = domReady;
return;
}
document.onreadystatechange = null;
_strings = data;
_isReady = true;
_readyCallback();
}
domReady();
}
// Get the current lang from the document's HTML element, which the
// server set when the page was first rendered. This saves us having
// to pass extra locale info around on the URL.
function getCurrentLang() {
var html = document.querySelector( "html" );
return html && html.lang ? html.lang : "en-US";
}
var Localized = {
get: function( key ) {
if ( !_strings ) {
console.error( "[goggles.webmaker.org] Error: string catalog not found." );
return "";
}
return ( _strings[ key ] || "" );
},
getCurrentLang: getCurrentLang,
// Localized strings are ready
ready: function( cb ) {
if ( !_requestedStrings ) {
_requestedStrings = true;
_readyCallback = cb;
function onload( data ) {
ready( data );
}
onload.error = console.log;
var xhr = new XMLHttpRequest();
xhr.open('GET', '/strings/' + getCurrentLang() + '?bust=' + Date.now(), false);
xhr.send(null);
if (xhr.status !== 200) {
err = new Error(id + ' HTTP status: ' + status);
err.xhr = xhr;
onload.error(err);
return;
}
onload(JSON.parse(xhr.responseText));
};
if ( _isReady ) {
_readyCallback();
}
},
isReady: function() {
return !!_isReady;
}
};
return Localized;
}));
So I want to be able to go into any of the 50 files and do Localized.get("something");
But then I don't even have the Localized object available in the web console. For example if you have jQuery you can do $ in the web console and you can do anything there.
Have you ever looked at the Three.js global function? It's super easy to understand!
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.THREE = global.THREE || {})));
}(this, (function (exports) { 'use strict';
So it turns out that my javascript is globally defined and accessible everywhere within the file that is included and it can be call from the console as well except I have to initialize that by doing Localized.ready(function(){}); then I can get it to work.
So if anyone is looking to create their own global function and make it standard they can follow this way.
amdWeb.js is what I use as a standard to create global function.
is there a way to define global namespace, so that i can call function from this namespace from all my page?
e.g
// in one file i define below code
DefineNameSpace("my.namespace.api", {
addObject: function(obj) {
// store obj into indexDB
},
readAllObject: function() {
// return array of object from indexdb
}
})
// so that in another javascript file i can do
my.namespace.api.addObject({name: "foo", desc: "bar"});
is there a way to implement "DefineNameSpace" method?
Thanks
one way to do it, which is very simple, is this:
my = {
namespace: {
api : {}
}
}
my.namespace.api.addObject = function (obj) { }
you're actually creating objects but in this way it will function as a namespace just as well :)
hm it's not the method you're implementing. But building a namespace with a method would require the function to be called before the script files are loaded where the namespace is used like that, otherwise those lines of code are called before the DefineNamespace method is called and you will run into parts of namespaces that are undefined at that point. With above solution that won't be the case, although it is not dynamic unfortunately.
building a namespace dynamically can be done in the following way:
// the root of the namespace would still be handy to have declared here
var my = {};
function defineNamespace(namespaceStr) {
var namespaceSegments = namespaceStr.split(".");
var namespaceSoFar = null;
// iterate through namespace parts
for (var i = 0; i < namespaceSegments.length; i++) {
var segment = namespaceSegments[i];
if (i == 0) {
// if namespace starts with my, use that
if (segment == "my") {
// set pointer to my
namespaceSoFar = my;
}
else {
// create new root namespace (not tested this, but think this should work)
var otherNamespace = eval(segment);
if (typeof otherNamespace == "undefined") {
eval(segment + " = {};");
}
// set pointer to created root namespace
namespaceSoFar = eval(segment);
}
}
else {
// further build the namespace
if (typeof namespaceSoFar[segment] == "undefined") {
namespaceSoFar[segment] = {};
}
// update the pointer (my -> my.namespace) for use in the next iteration
namespaceSoFar = namespaceSoFar[segment];
}
}
}
I'm attempting to write a plugin for a library (MomentJS) that is usable almost everywhere. I'm planning on using it with RequireJS, so it has to be AMD friendly, but I'd also like to go ahead and make it usable by those who load it via script tags in the browser or in Node.
After poking around, I slapped this together:
(function() {
var hasModule = typeof module !== "undefined" && module.exports;
var MY_LIB_DEF = function (moment, global) {
if(typeof moment == "undefined") {
throw "Can't find moment";
}
var MY_LIB = {
//
//DEFINE PLUGIN
//
};
if(hasModule) {
module.exports = LIB
} else if(global) {
global.LIB = LIB;
} else {
return LIB;
}
};
if(hasModule) {
moment = require('moment');
}
if (typeof define === "function" && define.amd) {
define(["moment"], MY_LIB_DEF);
} else {
MY_LIB_DEF(moment, this);
}
})();
The bottom section of MY_LIB_DEF where I determine if I'm exporting for CJS, attatching to window, or returning for AMD seems a bit clunky as does my picking of which way to kick off (CJS and script loading would share the running of the defining function. But the "global" passed into it will never be used).
While the above works, I'm thinking that this problem has to have been solved already. I just can't seem to find any examples to follow.
Anyone aware of better practices for this?
After searching around, I found some good info here to help solve the problem. Still had to massage it a bit for my purpose, but it seems to be the solution.
(function(root, definition) {
"use strict";
var moment;
if (typeof module !== 'undefined' && module.exports) {
moment = require('moment');
module.exports = definition(moment);
} else if (typeof define === 'function' && define.amd){
define(['moment'], definition);
} else {
root['MY_LIB'] = definition(root.moment);
}
}(this, function(moment) {
if(typeof moment === "undefined") {
throw "Can't find moment";
}
return {
foo: function() {
console.log('bar');
}
};
}));