How to work with shimming, third-party scripts, and interdependencies - javascript

I'm new to working with RequireJS, and am trying to figure out shimming 3rd-party, interdependent scripts. Specifically, I'm trying to get the Stanford Crypto scripts imported.
Basically, the suite is comprised of the core (jsbn.js, jsbn2.js, base64.js, rng.js, and prng4.js), a basic RSA script (rsa.js), and an extended RSA script (rsa2.js).
rsa.js defines the global variable-object RSAKey, and rsa2.js references it.
function RSAKey() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null;
}
I've set up my shim in a way that I thought was correct, but I get the error "RSAKey is not defined" in rsa2.js. The following is my shim:
require.config({
paths: {
'jsbn': "../StanfordRSA/jsbn.js",
'jsbn2': "../StanfordRSA/jsbn2.js",
'base64': "../StanfordRSA/base64.js",
'rng': "../StanfordRSA/rng.js",
'prng4': "../StanfordRSA/prng4.js",
'rsa': "../StanfordRSA/rsa.js",
'rsa2': "../StanfordRSA/rsa2.js"
},
shim: {
'rsa': {
deps: ['jsbn', 'jsbn2', 'base64', 'rng', 'prng4'],
exports: "RSAKey"
},
'rsa2': {
deps: ['rsa']
}
}
});
My understanding, then, is that if I set 'rsa2' as a requirement in one of my RequireJS modules, it would look at the shim and see that rsa2 is dependent on rsa, which is dependent on the core and exports RSAKey...But that's not what's happening, and it seems like either rsa isn't loading, or it isn't loading correctly. (Please note that all of this works using raw script tags. I'm trying to convert an already existing, already functioning webapp to RequireJS)
Thoughts?

Your basic setup is correct, except for 2 things:
(really important!) You have to omit the .js extensions!!!
You probably have missed the exact dependencies between the scripts.
After some experimentation and reading the comments at the top of the scripts, the working configuration is:
require.config({
paths: {
'jsbn': "../StanfordRSA/jsbn",
'jsbn2': "../StanfordRSA/jsbn2",
'base64': "../StanfordRSA/base64",
'rng': "../StanfordRSA/rng",
'prng4': "../StanfordRSA/prng4",
'rsa': "../StanfordRSA/rsa",
'rsa2': "../StanfordRSA/rsa2"
},
shim: {
'rng': {
deps: ['prng4']
},
'jsbn2': {
deps: ['jsbn']
},
'rsa': {
deps: ['jsbn', 'rng'],
exports: 'RSAKey'
},
'rsa2': {
deps: ['rsa', 'jsbn2'],
exports: 'RSAKey'
}
}
});
Check out a plunk here.

Related

How to Repath CommonJS modules in StealJS

I'm probably doing something wrong, so feel free to question all things. I'm using an npm package xrm-mock for a MS CRM mocking framework. I've setup my config as such
steal.config({
meta: {
"dependencyModule": {
deps: [
/***********************************
* List of Spec Files goes here! *
***********************************/
"spec/po_/commonSpec"
,"spec/xrmMockGeneratorSpec"
]
},
"jasmine": {
"exports": "jasmineRequire"
},
"jasmine-html": {
deps: ["jasmine"]
},
"jasmine-boot": {
deps: ["jasmine", "jasmine-html"]
},
"xrm-mock-generator": {
deps: ["xrm-mock"]
}
},
bundlesPath: "../WebResources",
loadBundles: true,
paths: {
"jasmine": "../node_modules/jasmine-core/lib/jasmine-core/jasmine.js",
"jasmine-html": "../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js",
"jasmine-boot": "../node_modules/jasmine-core/lib/jasmine-core/boot.js",
"sourcemapped-stacktrace": "../node_modules/sourcemapped-stacktrace/dist/sourcemapped-stacktrace.js",
"xrm-mock": "../node_modules/xrm-mock/index.js",
"xrm-mock-generator": "../node_modules/xrm-mock-generator/dist/xrm-mock-generator.js"
},
map: {},
main: "./testRunner"
});
but xrm-mock/index.js looks like this:
"use strict";
exports.__esModule = true;
var formselector_mock_1 = require("./dist/page/formselector/formselector.mock");
exports.FormSelectorMock = formselector_mock_1.FormSelectorMock;
var formitem_mock_1 = require("./dist/page/formitem/formitem.mock");
exports.FormItemMock = formitem_mock_1.FormItemMock;
... 80 more lines...
and I get 404s for each require: "http://localhost:62576/test/dist/page/formselector/formselector.mock.js" which should be "http://localhost:62576/node_modules/xrm-mock/dist/page/formselector/formselector.mock.js"
I'm guessing I could add each and every module file as a module with a path, but that's 40 some modules I'd have to define. Is there an easier way?
Most devs use the npm plugin nowadays, do you know about it? Removes the need for this manual (and difficult) configuration
To answer your question though, I think what you want to do is remove the xrm-mock path and instead have something like
"xrm-mock/*": "../node_modules/xrm-mock/*.js"
and then a map for the main module:
"map": {
"xrm-mock": "xrm-mock/index"
}

Create a globally available function with RequireJS to handle Google Analytics Event Tracking

I am looking for a way to create and include a single script which will house all my Google Analytics event tracking code. There are various points in my application where I want to be able to track clicks and interaction and I would like to be able to have all these functions in a single file.
My problem is I can't find a suitable way of doing that with RequireJS, which my site uses.
This is an example of what I would have. I'd like to target an anchor with a class of resend and trigger a GA event.
Resend
Sure that is simple enough, but I don't want to have to require a module everywhere I want to do event tracking. Is this necessary or is there a clearer/cleaner way to do it? I should point out I don't want to include Google Analytics as there are many tutorials on how to do that and I am already doing that through the traditional way of having it in the footer - it's only GA event tracking code I want to include.
This is my requireJS config.js:
require = {
baseUrl: '/assets/js',
paths: {
// Amcharts.
'amcharts': '/assets/vendor/amcharts/dist/amcharts/amcharts',
'amcharts.funnel': '/assets/vendor/amcharts/dist/amcharts/funnel',
'amcharts.gauge': '/assets/vendor/amcharts/dist/amcharts/gauge',
bootstrap: '/assets/vendor/bootstrap/dist/js/bootstrap.min',
jquery: '/assets/vendor/jquery/dist/jquery.min',
jstz: '//cdnjs.cloudflare.com/ajax/libs/jstimezonedetect/1.0.4/jstz.min',
pwstrength: '/assets/vendor/pwstrength-bootstrap/dist/pwstrength-bootstrap-1.2.7.min',
},
shim: {
'amcharts.funnel': {
deps: [ 'amcharts' ],
exports: 'AmCharts',
init: function() {
AmCharts.isReady = true;
}
},
'amcharts.gauge': {
deps: [ 'amcharts' ],
exports: 'AmCharts',
init: function() {
AmCharts.isReady = true;
}
},
pwstrength: {
deps: [
'jquery'
]
},
bootstrap: {
deps: [
'jquery'
]
}
}
};
// Apply the urlArgs here for cache busting.
require.urlArgs = requireBase.urlArgs;
This is how I ended up configuring my RequireJS config.js.
require = {
baseUrl: '/assets/js',
paths: {
'gaEventTracking': '/assets/src/js/crmpicco/gaEventTracking',
},
shim: {
'gaEventTracking': {
deps: [
'jquery'
]
},
}
};
// Apply the urlArgs here for cache busting.
require.urlArgs = requireBase.urlArgs;
There then exists a gaEventTracking.js in the /assets/src/js/crmpicco directory.

RequireJS module shim not working when testing via Jasmine

I have a JavaEE project that uses RequireJS to load a few third party frameworks. One of those frameworks is OpenLayers3. Openlayers3 natively creates a global "ol" variable. However, OpenLayers3 is written to be AMD compatible and works as a module through RequireJS. I also am using an OpenLayers3 plugin called "olLayerSwitcher" which is not optimized for AMD. Instead, it depends on the "ol" variable being global.
My require config looks like the following:
paths: {
"sinon": ['/webjars/sinonjs/1.7.3/sinon'],
"jquery": ["/webjars/jquery/2.1.4/jquery"],
"backbone": ['/webjars/backbonejs/1.2.1/backbone'],
"underscore": ['/webjars/underscorejs/1.8.3/underscore'],
"text": ['/webjars/requirejs-text/2.0.14/text'],
"log4js": ['/webjars/log4javascript/1.4.13/log4javascript'],
"ol": ['/webjars/openlayers/3.5.0/ol'],
"olLayerSwitcher": ['/js/vendor/ol3-layerswitcher/1.0.1/ol3-layerswitcher']
},
shim: {
"olLayerSwitcher": {
deps: ["ol"],
exports: "olLayerSwitcher"
},
'sinon' : {
'exports' : 'sinon'
}
}
The project is uses Backbone and includes a Router module (/src/main/webapp/js/controller/AppRouter.js):
/*jslint browser : true*/
/*global Backbone*/
define([
'backbone',
'utils/logger',
'views/MapView'
], function (Backbone, logger, MapView) {
"use strict";
var applicationRouter = Backbone.Router.extend({
routes: {
'': 'mapView'
},
initialize: function () {
this.LOG = logger.init();
this.on("route:mapView", function () {
this.LOG.trace("Routing to map view");
new MapView({
mapDivId: 'map-container'
});
});
}
});
return applicationRouter;
});
The Router module depends on a View module (/src/main/webapp/js/views/MapView.js):
/*jslint browser: true */
define([
'backbone',
'utils/logger',
'ol',
'utils/mapUtils',
'olLayerSwitcher'
], function (Backbone, logger, ol, mapUtils, olLayerSwitcher) {
"use strict";
[...]
initialize: function (options) {
this.LOG = logger.init();
this.mapDivId = options.mapDivId;
this.map = new ol.Map({
[...]
controls: ol.control.defaults().extend([
new ol.control.ScaleLine(),
new ol.control.LayerSwitcher({
tipLabel: 'Switch base layers'
})
])
});
Backbone.View.prototype.initialize.apply(this, arguments);
this.render();
this.LOG.debug("Map View rendered");
}
});
return view;
});
The View module attempts to pull in both OpenLayers3 as well as the third-party OpenLayers plugin.
When the project is built and deployed, it works fine in-browser. When the View module is loaded, OpenLayers and the third-party plugin are pulled in just fine and everything renders properly.
However, when I attempt to test this in Jasmine is where all of this falls apart.
For Jasmine, I am using the Jasmine-Maven plugin. It pulls in JasmineJS, PhantomJS and RequireJS along with my libraries and runs my specs. The issue is that when run via Jasmine, the MapView module attempts to load both the OpenLayers3 library as well as the third party plugin (olLayerSwitcher) but fails because the third party plugin can't find "ol".
The test:
define([
"backbone",
"sinon",
'controller/AppRouter'
], function (Backbone, sinon, Router) {
describe("Router", function () {
beforeEach(function () {
this.router = new Router();
this.routeSpy = sinon.spy();
this.router.bind("route:mapView", this.routeSpy);
try {
Backbone.history.start({silent: true});
} catch (e) {
}
this.router.navigate("elsewhere");
});
it("does not fire for unknown paths", function () {
this.router.navigate("unknown", true);
expect(this.routeSpy.notCalled).toBeTruthy();
});
it("fires the default root with a blank hash", function () {
this.router.navigate("", true);
expect(this.routeSpy.calledOnce).toBeTruthy();
expect(this.routeSpy.calledWith(null)).toBeTruthy();
});
});
});
The error from Jasmine:
[ERROR - 2015-08-08T21:27:30.693Z] Session [4610ead0-3e14-11e5-bb2b-dd2c4b5c2c7b] - page.onError - msg: ReferenceError: Can't find variable: ol
:262 in error
[ERROR - 2015-08-08T21:27:30.694Z] Session [4610ead0-3e14-11e5-bb2b-dd2c4b5c2c7b] - page.onError - stack:
global code (http://localhost:58309/js/vendor/ol3- layerswitcher/1.0.1/ol3-layerswitcher.js:9)
:262 in error
JavaScript Console Errors:
* ReferenceError: Can't find variable: ol
The relevant section from the ol3-layerswitcher plugin on line 9 is:
[...]
ol.control.LayerSwitcher = function(opt_options) {
[...]
So it does depend on "ol" being a thing at this point.
The Jasmine-Maven plugin creates its own spec runner HTML and the relevant portion looks like this:
<script type="text/javascript">
if(window.location.href.indexOf("ManualSpecRunner.html") !== -1) {
document.body.appendChild(document.createTextNode("Warning: Opening this HTML file directly from the file system is deprecated. You should instead try running `mvn jasmine:bdd` from the command line, and then visit `http://localhost:8234` in your browser. "))
}
var specs = ['spec/controller/AppRouterSpec.js'];
var configuration = {
paths: {
"sinon": ['/webjars/sinonjs/1.7.3/sinon'],
"jquery": ["/webjars/jquery/2.1.4/jquery"],
"backbone": ['/webjars/backbonejs/1.2.1/backbone'],
"underscore": ['/webjars/underscorejs/1.8.3/underscore'],
"text": ['/webjars/requirejs-text/2.0.14/text'],
"log4js": ['/webjars/log4javascript/1.4.13/log4javascript'],
"ol": ['/webjars/openlayers/3.5.0/ol'],
"olLayerSwitcher": ['/js/vendor/ol3-layerswitcher/1.0.1/ol3-layerswitcher']
},
shim: {
"olLayerSwitcher": {
deps: ["ol"],
exports: "olLayerSwitcher"
},
'sinon' : {
'exports' : 'sinon'
}
}
};
if (!configuration.baseUrl) {
configuration.baseUrl = 'js';
}
if (!configuration.paths) {
configuration.paths = {};
}
if (!configuration.paths.specs) {
var specDir = 'spec';
if (!specDir.match(/^file/)) {
specDir = '/'+specDir;
}
configuration.paths.specs = specDir;
}
require.config(configuration);
require(specs, function() {
jasmine.boot();
});
I am able to create a customer HTML runner but am not sure what the problem is so I wouldn't know what needs changing.
This doesn't seem to be a PhantomJS issue as I can load the tests in-browser and am experiencing the same issue.
I'd appreciate if anyone has any thoughts on what could be happening here. I really do not want to hack up the third-party module to transform it into a RequireJS module as the Jasmine testing is the last-leg of implementing this completely and I'm completely stuck here.
I am using Jasmine 2.3.0 and RequireJS 2.1.18
I apologize for not linking out more but this is a new account and I don't have enough rep for it.
It will be tough to figure out the problem without a running version of your setup.
However, if you're able to customize the SpecRunner.html for jasmine generated by the maven plugin, simply include the jasmine(/ any other library causing an issue) in the SpecRunner html - <script src="/<path_to_lib>">.
In my experience, its usually not worth the effort , to make libraries used in source amd compliant and play nicely with every other library for testing setup.

RequireJS load only concatenated file

I'm using RequireJS (version 2.1.14) and would like to concatenate my JavaScript files into one single app-built.js.
I've created a little node module which reads my app.js, extracts the project paths and gets executed once I run node build in the js directory of my application.
The node module (build.js):
var fs = require('fs'),
path = require('path'),
directory = __dirname + path.sep,
requirejs = require(directory + 'vendor/r.js');
fs.readFile(directory + 'app.js', 'utf8', function(err, data) {
if (err) {
console.log('Error: ' + err);
return
} else {
data = data.replace(/'/g, '"').replace(/\s+/g, '');
var paths = data.substr(data.indexOf('{'), data.indexOf('}')),
paths = paths.substr(0, paths.indexOf('}') + 1),
paths = JSON.parse(paths);
createAppBuilt(paths);
}
});
function createAppBuilt(paths) {
var config = {
baseUrl: __dirname,
paths: paths,
name: 'app',
out: 'app-built.js',
preserveLicenseComments: false,
findNestedDependencies: true,
removeCombined: true
};
requirejs.optimize(config, function(buildResponse) {
var contents = fs.readFileSync(config.out, 'utf8');
console.log('Created app-built.js');
}, function(err) {
console.log('Error: ' + err);
return;
});
}
app.js:
var paths = {
'jquery': 'vendor/jquery-1.11.0.min',
// other paths
};
// Set language, necessary for validtaion plugin -> validation.js
if (Modernizr.localstorage) {
localStorage.getItem('language') || localStorage.setItem('language', navigator.language || navigator.userLanguage);
}
requirejs.config({
paths: paths,
shim: {
touchswipe: {
deps: ['jquery']
},
icheck: {
deps: ['jquery']
},
validate: {
deps: ['jquery']
},
mask: {
deps: ['jquery']
},
chosenImage: {
deps: ['jquery', 'chosen']
},
cookie: {
deps: ['jquery']
}
}
});
require(['globals', 'jquery', 'underscore'], function() {
var initial = ['main'];
if (!Modernizr.localstorage) {
initial.push('cookie');
}
require(initial, function(Main) {
$(function() {
if (!Modernizr.localstorage) {
$.cookie.json = true;
}
Main.init();
});
});
});
The app-built.js gets generated but when I include it in my index.php all the other modules get loaded as well. How can I prevent the loading of all modules and only load the app-built.js?
I recommend you look into http://webpack.github.io/
or http://browserify.org/ as these solve this problem for you.
They allow you to use require much as before, yet the code is compiled/concatenated into a single file.
Webpack allows for a bit more flexibility in loading different chunks of code for different parts of your site, but Browserify is the most well-known so far.
There may be a cost in switching over to these, as I don't think that they're 100% compatible requirejs, however they bring great advantages.
Here's someone's journey from RequireJS to Browserify with some Pros and Cons.
Separate modules into different files, e.g. app-built.js, user-built.js. Then load script when it's needed.
Here's a demo: http://plnkr.co/edit/s6hUOEHjRbDhtGxaagdR?p=preview .
When page loaded, requirejs only loads global.js. After clicking the Change Color button, requirejs starts to load colorfy.js and random-color.js, which required by colorfy.js.
I am not sure about the exact details, but, yet if you don't have an exports option, r.js doesn't define a named module for you, that causes to actually load the script.
I assume you have jquery plugins there so add this extra exports option:
shim: {
touchswipe: {
deps: ['jquery'],
exports: 'jQuery.fn.touchswipe'
},
This should force r.js to build a named module for touchswipe:
define("touchswipe", (function (global) {
return function () {
var ret, fn;
return ret || global.jQuery.fn.touchswipe;
};
}(this)));
Note that, exports option might not build this named module, in that case your best bet is to include this manually.
Again I am not sure about why and how this happens, It must be a bug in requirejs, it's unlikely there is a tweak for this.
Changing the r.js optimizer (to uglify2) solved the problem for me:
var config = {
baseUrl: __dirname,
paths: paths,
name: 'app',
out: 'app-built.js',
findNestedDependencies: true,
preserveLicenseComments: false,
removeCombined: true,
optimize: 'uglify2'
};

SignalR & require.js configuration

I'm incorporating SignalR into a project where I'm already using require.js to handle my scripts dependencies.
I'm having a little trouble making sure "/signalr/hubs" is called after "jquery.signalR-1.1.2" loads.
I got it to work, but I'm wondering if there is a better alternative out there.
This is what I have:
require(["signalr"], function () {
require(["noext!/signalr/hubs"], function () {
//initialize and work with the hub here
}
}
Is there a way I can create a shim here to establish the dependency between signalr/hubs and the signalr script?
Thanks!
This works for me with SignalR 1.1.2:
require.config({
baseUrl: "/<your scripts dir>",
paths: {
"jquery": "jquery-<your jquery version>.min",
"signalr.core": "jquery.signalR-<your signalr version>.min",
"signalr.hubs": "/signalr/hubs?"
},
shim: {
"jquery": {
exports: "$"
},
"signalr.core": {
deps: ["jquery"],
exports: "$.connection"
},
"signalr.hubs": {
deps: ["signalr.core"],
}
}
});
require(["jquery", "signalr.hubs"],
function($)
{
var hubProxy = $.connection.myHub;
// ... go to town ...
});

Categories

Resources