I am using require.js to organize my js:
define([
'underscore',
'sylvester',
'glUtils',
'GLFacade',
'Item',
'Attribute',
'Buffer',
], function(
_,
sylv,
glUtils,
GLFacade,
Item,
Attribute,
Buffer
) {
"use strict";
function Sprite() { this.init.apply(this, arguments); }
_.extend(Sprite.prototype, {
init: function(prog, img, viewPort) {
this._frameNum = 0;
this._framesPerAnimation = 4;
this._prog = prog;
this._viewPort = viewPort;
this._img = new ImageWrapper(img);
//...other initialization stuff...
},
//...other methods...
});
return Sprite;
});
but I consistently run into the error that I forget to add a module to the top of the file. Above I've forgotten to add ImageWrapper to my dependencies. When I do this, my code silently fails with no error messages, even though ImageWrapper is undefined. If I try to log console.log(ImageWrapper) I do indeed get an error.
Why doesn't the constructor call to new ImageWrapper(img) fail with an error? And is there something similar to "use strict;" that I can use to increase the error information during development?
You could lint your code using a tool like http://jshint.com/ - you will get something like:
One undefined variable
27 ImageWrapper
Depending on your setup there are different ways to automate this, some editors have built this in or plugins can extend this functionality. There also is a command line version on npm if you want to run jshint manually: https://npmjs.org/package/jshint
Your code should throw an error but only if you instantiate a new Sprite.
When I try to simplify your code like this
define('Sprite', ['underscore'], function(_) {
'use strict';
function Sprite() {
this.init.apply(this, arguments);
}
_.extend(Sprite.prototype, {
init: function() {
this._foo = new DoesntExist();
}
});
return Sprite;
});
require(['Sprite'], function(Sprite) {
var sprite = new Sprite();
});
it throws a ReferenceError as expected.
Related
I have an old IIFE that is injected into legacy pages via <script src.
However, I want to use all these old libraries in a react app. I just need to use the global function exposed.
I figure loading dependencies that will work both via script or via react's import or nodejs require
Here is an example of an example IIFE
example.js :
var $ = $;
var geocomplete = $.fn.geocomplete;
var OtherExternalLib = OtherExternalLib;
var Example = (function() {
return {
init: function () {
// stuff
}
}
)();
Where the legacy code is calling Example.init(), and likewise the react code will call the same function.
Where $ (jQuery), $.fn.geocomplete, and OtherExternalLib are all dependencies that must be loaded, either they should be loaded on-demand or just throw a big loud error message.
I suspect if the solution loads dynamically example.js would look something like
var $ = load("\libs\jquery.js");
var geocomplete = load("\libs\$.fn.geocomplete.js");
var OtherExternalLib = load("\libs\OtherExternalLib.js");
var Example = (function() {
return {
init: function () {
// stuff
}
}
)();
And the legacy application can still use <script src=example.js and React can use
import {Example} from example
Understandably this is somewhat a round-about way to of using legacy code in new applications, so I am open to other ideas on how best to expose an IIFE (with or without dependencies) and using it in React
I am using react+typescript in my project with some limitations which is why I had to dynamically import my package (my project runs in a shell project with AMD module, not having my own startup, and change the way project files get bundled).
Since I could only use the dependent modules on the fly during the run time, I had to assume them were valid while building and bundling . Most of them were IIFE.
So I used the lazy dynamic import .
something like this
import("somePolyfill");
This would be translated by TSC
new Promise(function (resolve_3, reject_3) { require(["arrayPolyfill"], resolve_3, reject_3); });
This would call the IIFE and execute the polyfills or initializing any window or global variable, so the rest of the code is aware of that.
If it returns a module or throughs error can be handled like normal promise then and catch.
So I created a wrapper
export class DepWrap {
public static Module: any = {};
public constructor() {
this.getPI();
this.getSomeModule();
}
public async getPI() {
DepWrap.Module["PI"] = 3.14;
}
public async getSomeModule() {
await import('somepath/somemodule').then(($package) => {
DepWrap.Module["somemodule"] = $package;
}).catch(() => {
window.console.log("Some Module error");
});
}
}
this got compiled to
define(["require", "exports", "tslib"], function (require, exports, tslib_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var DepWrap = /** #class */ (function () {
function DepWrap() {
this.getPI();
this.getSomeModule();
}
DepWrap.prototype.getPI = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
DepWrap.Module["PI"] = 3.14;
return [2 /*return*/];
});
});
};
DepWrap.prototype.getSomeModule = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, new Promise(function (resolve_1, reject_1) { require(['somepath/somemodule'], resolve_1, reject_1); }).then(function ($package) {
DepWrap.Module["somemodule"] = $package;
}).catch(function () {
window.console.log("Some Module error");
})];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
DepWrap.Module = {};
return DepWrap;
}());
exports.DepWrap = DepWrap;
});
with this I could use all the dependency modules from my wrapper and every time i need to import a new one I would create another function to add that to my wrap module.
import { DepWrap } from "wrapper/path";
const obj = new DepWrap(); // initialize once in the beginning of project so it would import all the dependencies one by one .
Afterwards in all file, I can import my module from the wrapper
import { DepWrap } from "wrapper/path";
const { PI, somemodule} = DepWrap.Module;
I am not sure if the code will work for your scenario as well, but I guess tweaking it a bit might come in handy for your useCase .
Plus : if you are writing unit test case it will help jest to just ignore the modules and can create a mock for this so that you can test your actual code .
I need to set the resources object within the Highcharts exporting module's exportChart method but can't seem to overwrite it.
The export module source code is located at http://code.highcharts.com/modules/exporting.src.js and the specific subsection of this that I am overwriting looks like this:
'use strict';
(function (factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory;
} else {
factory(Highcharts);
}
}(function (Highcharts) {
(function (H) {
// create shortcuts
var Chart = H.Chart,
merge = H.merge,
extend = H.extend;
//... Removed extra code not needed for example
extend(Chart.prototype, /** #lends Highcharts.Chart.prototype */ {
exportChart: function (exportingOptions, chartOptions) {
var svg = this.getSVGForExport(exportingOptions, chartOptions);
// merge the options
exportingOptions = merge(this.options.exporting, exportingOptions);
// do the post
H.post(exportingOptions.url, {
filename: exportingOptions.filename || 'chart',
type: exportingOptions.type,
// IE8 fails to post undefined correctly, so use 0
width: exportingOptions.width || 0,
scale: exportingOptions.scale,
svg: svg
}, exportingOptions.formAttributes);
}
//... Removed extra code not needed for example
});
//... Removed extra code not needed for example
}(Highcharts));
}));
To test the ability to overwrite the method I am using the following code:
(function (H) {
var Chart = H.Chart,
extend = H.extend;
extend(Chart.prototype, /** #lends Highcharts.Chart.prototype */ {
exportChart: function (exportingOptions, chartOptions) {
alert('changed it');
}
});
}(Highcharts));
The alert never fires but instead the normal export is still happening.
I've created a JSFiddle showing this issue here: https://jsfiddle.net/j005v79j/
Can anyone tell me why the overwriting of this method isn't working?
I realized I was also loading code.highcharts.com/modules/offline-exporting.js on my side which was not in the fiddle. This script overwrote the export buttons to use a different method called exportChartLocal(). Since the buttons were now tied to a method that would never be called my alert was not firing.
I've removed the call to offline-exporting for now and will write overwrites into my code for this version at a later date.
Thanks to #Kamil for pointing out the issue.
I have a worker.js file:
self.importScripts('/static/utils/utils.js')
onmessage = (e) => {
let a = e.data[0]
let b = e.data[1]
let c = func1(a,b)
postMessage(c)
}
The utils.js file looks something like this:
module.exports = {
func1: function(a,b){
return a+b
}
I keep getting error:
Uncaught ReferenceError: module is not defined
at utils.js:1
Obviously require, and import and any other server side imports aren't working but I'm not sure why it's having a problem with my importScripts - https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
The correct solution is to pack your worker with webpack. If you don't want to do that, read below.
I usually write myself a polyfill for node require:
// This will not work in normal UI thread
// None of this should make it into production
function require(moduleName) {
self.module = { exports: null };
// Nasty sttuff right here, probably should throw error instead
if (moduleName == "fs")
return null;
// This part is especially unprofessional
if (!moduleName.endsWith(".js"))
moduleName += ".js";
importScripts(moduleName);
return self.module.exports;
}
This makes use of the fact that importScripts is synchronous. Note that this will still cause errors if you try to load native node modules (eg. fs) or if other module properties are used.
Try this utils.js:
(function () {
self.func1 = function (a, b) {
return a + b
}
}());
Try to do this:
//inside worker.js
self.addEventListener("message",(event)=>{
importScripts("module.js")
utils.print1()
utils.print2()
})
//inside module.js
//setting a variable in global scope, allows worker.js to use it.
var utils = {
print1(){
console.log("This is a content from a module.")
},
print2(){
console.log("This is a another content from a module.")
}
}
I've been working on writing a custom jquery plugin for one of my web applications but I've been running into a strange error, I think it's due to my unfamiliarity with object-oriented programming.
The bug that I've been running into comes when I try to run the $(".list-group").updateList('template', 'some template') twice, the first time it works just fine, but the second time I run the same command, I get an object is not a function error. Here's the plugin code:
(function($){
defaultOptions = {
defaultId: 'selective_update_',
listSelector: 'li'
};
function UpdateList(item, options) {
this.options = $.extend(defaultOptions, options);
this.item = $(item);
this.init();
console.log(this.options);
}
UpdateList.prototype = {
init: function() {
console.log('initiation');
},
template: function(template) {
// this line is where the errors come
this.template = template;
},
update: function(newArray) {
//update code is here
// I can run this multiple times in a row without it breaking
}
}
// jQuery plugin interface
$.fn.updateList = function(opt) {
// slice arguments to leave only arguments after function name
var args = Array.prototype.slice.call(arguments, 1);
return this.each(function() {
var item = $(this), instance = item.data('UpdateList');
if(!instance) {
// create plugin instance and save it in data
item.data('UpdateList', new UpdateList(this, opt));
} else {
// if instance already created call method
if(typeof opt === 'string') {
instance[opt](args);
}
}
});
}
}(jQuery));
One thing I did notice when I went to access this.template - It was in an array so I had to call this.template[0] to get the string...I don't know why it's doing that, but I suspect it has to do with the error I'm getting. Maybe it can assign the string the first time, but not the next? Any help would be appreciated!
Thanks :)
this.template = template
Is in fact your problem, as you are overwriting the function that is set on the instance. You end up overwriting it to your args array as you pass that as your argument to the initial template function. It basically will do this:
this.template = ["some template"];
Thus the next time instance[opt](args) runs it will try to execute that array as if it were a function and hence get the not a function error.
JSFiddle
Im starting to sort of wrap my head around requirejs and the new Dojo AMD structure, but I have a problem with some early tests:
cg/signup.js:
define(['dojo/_base/fx', 'dojo/dom'], function(fx, dom){
return function(){
this.hidePreloader = function(id){
var preloader = dom.byId(id);
fx.fadeOut({node : preloader}).play()
}
}
})
This works fine. In the master cg.js file:
require(['dojo/_base/kernel', 'dojo/_base/loader'])
dojo.registerModulePath('cg', '../cg')
require(['cg/signup', 'dojo/domReady!'], function(Signup){
var sp = new Signup();
sp.hidePreloader('preloader')
})
Bam. Done. However, in using the Simplified CommonJS Wrapper structure:
define(function(require){
var fx = require('dojo/_base/fx'),
dom = require('dojo/dom');
return function(){
this.hidePreloader = function(id){
var preloader = dom.byId(id);
fx.fadeOut({node : preloader}).play()
}
}
})
I get an undefinedModule error. It seems to come from the dojo/_base/fx line, but I don't know why.
UPDATE
For clarification.
index.html scripts
<script type="text/javascript" src="js/dojo/dojo.js.uncompressed.js" data-dojo-config="isDebug:true,async:true"></script>
<script type="text/javascript" src="js/cg.js"></script>
cg.js
require(['dojo/_base/kernel', 'dojo/_base/loader'])
dojo.registerModulePath('cg', '../cg')
require(['cg/signup', 'dojo/domReady!'], function(signup){
signup.testFunc()
})
js/cg/signup.js
define(['require', 'exports'], function(require, exports){
var dom = require('dojo/_base/kernel');
// Any other require() declarations (with very very few exceptions like 'dojo/_base/array throw undefinedModule errors!!!
// without any error causing requires, this works fine.
exports.testFunc = function(){
alert("hello")
}
})
dojo completely supports the Simplified CommonJS Wrapper format. however, there is a precondition... you must have no dependencies array.
define(function (require, exports, module) {
var fx = require('dojo/_base/fx'),
dom = require('dojo/dom');
// continue...
});
this will NOT work the same
define(['require', 'exports', 'module'], function (require, exports, module) {
var fx = require('dojo/_base/fx'),
dom = require('dojo/dom');
// continue...
});
nor will this...
// in this case require, exports and module will not even exist
define([], function (require, exports, module) {
var fx = require('dojo/_base/fx'),
dom = require('dojo/dom');
// continue...
});
Here is a little wrapper around Dojo define that uses code taken from RequireJS to calculate the dependencies based on the toString of the definition function. It wraps the current "define" in the global namespace, calculates the dependencies and then call's the wrapped define.
defineWrapper.js:
// Workaround for the fact that Dojo AMD does not support the Simplified CommonJS Wrapper module definition
(function() {
var commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /require\(\s*["']([^'"\s]+)["']\s*\)/g,
ostring = Object.prototype.toString;
function isArray(it) {
return ostring.call(it) === '[object Array]';
}
function isFunction(it) {
return ostring.call(it) === '[object Function]';
}
var oldDefine = define;
define = function(name, deps, callback) {
//Allow for anonymous functions
if (typeof name !== 'string') {
//Adjust args appropriately
callback = deps;
deps = name;
name = null;
}
//This module may not have dependencies
if (!isArray(deps)) {
callback = deps;
deps = [];
}
//If no name, and callback is a function, then figure out if it a
//CommonJS thing with dependencies.
if (!deps.length && isFunction(callback)) {
//Remove comments from the callback string,
//look for require calls, and pull them into the dependencies,
//but only if there are function args.
if (callback.length) {
callback
.toString()
.replace(commentRegExp, '')
.replace(cjsRequireRegExp, function(match, dep) {
deps.push(dep);
});
//May be a CommonJS thing even without require calls, but still
//could use exports, and module. Avoid doing exports and module
//work though if it just needs require.
//REQUIRES the function to expect the CommonJS variables in the
//order listed below.
deps = (callback.length === 1 ? ['require'] :
['require', 'exports', 'module']).concat(deps);
}
}
if(name === null) {
return oldDefine(deps, callback);
} else {
return oldDefine(name, deps, callback);
}
}
})();
How would you use it?
<script src="...dojo..."></script>
<script src="defineWrapper.js"></script>
<script>require(["some_simplified_commonjs_defined_module"], function(module) {
// use module here
});</script>
Note that Dojo does not support the simplified CommonJS style for dependency detection when doing a build. This means you'll either need to use the normal dependency list style, or you'll have to duplicate all your dependencies when defining layers in the build profile.
Here's the relevant bug in their tracker:
http://bugs.dojotoolkit.org/ticket/15350
is require defined? I don't see where that argument value would come from. I think you might have to do something like
define(["require"], function(require){ ...
I started to think that maybe I wasn't meant to learn Dojo. But, it all comes together with a little more reading. I'm not sure exactly what I did different or whatever, but here's the working layout.
index.html scripts and config
<script type="text/javascript">
dojoConfig = {
async : true,
isDebug : true,
debugAtAllCosts : true,
packages : [{
name : 'cg',
location : '/../js/cg'
}]
}
</script>
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js"></script>
<script type="text/javascript" src="js/cg.js"></script>
js/cg.js
require(['cg/signup', 'dojo/ready'], function(signup){
signup.init('preloader')
})
js/cg/signup.js
define(['dojo', 'require'], function(dojo, require){
var fx = require('dojo/_base/fx')
return new function(){
this.init = function(id){
fx.fadeOut({node : dojo.byId(id)}).play()
}
}
})
Again, not entirely sure why the var fx = require(...) statement works differently in this one than the others, could be the build I downloaded vs. the CDN, who cares. It works. Some links I used to help for others possibly in the same boat:
Writing Modular JS
AMD vs CommonJS Wrapper
Dojo Toolkit AMD
Dojo Config (1.7)