Coder Social home page Coder Social logo

cajon's Introduction

cajon

Cajon is a JavaScript module loader for the browser that can load CommonJS/node and AMD modules. It is built on top of RequireJS.

You can use it to code modules for your project in CommonJS/node style, then use the RequireJS Optimizer to build all the modules into an AMD-compliant bundle. This allows you to then use a small AMD API shim, like almond, to get nicely optimized code without needing a full runtime loader.

Why?

Why use this instead of RequireJS? Some possible reasons:

  1. You cannot bring yourself to use a wrapper like this around your module code:
define(function(require) {
    /*module code here */
});
  1. You have a set of code already formatted in CommonJS/node style you want to reuse.

Otherwise, you should be using RequireJS, or another AMD loader.

Note the Restrictions section below. You will likely gnash your teeth in frustration if you do not heed them.

If you do not like this particular loader, but like the idea of a dual AMD and CommonJS/node style module loader, then you may like LinkedIn's Inject loader better.

How does it work?

Cajon is constructed with:

  • RequireJS (needs 2.0.2 or later)
  • An override to requirejs.load that fetches scripts via async XHR requests then evals them, using the //@ sourceURL= to specify the script names for script debuggers.

Cajon will only use the XHR+eval approach if the request is to the same domain as the HTML document. If the script request is deemed to be on another domain, it will just delegate to the default requirejs.load() function, where it will load the script with a <script> tag, and expect it to be in AMD format, or a traditional "browser globals" script.

You can override this behavior to use XHR for some cross domain requests if you know your users will be using CORS-enabled browsers and servers. See the Configuration section below.

Scripts that are fetched are wrapped in the following AMD wrapper:

define(function (require, exports, module) {
    /* module code here */
});

and it allows the use of __dirname and __filename inside that wrapped code.

Cajon assigns the cajon variable to the be same as the requirejs variable, so you can use that if you want to specifically call out the usage of cajon. However, the requirejs optimizer only understands of require, requirejs and define, it will not understand cajon. This is particularly important if you are using the optimizer's mainConfigFile option.

It is best to just use the global require if you want the code to be portable to RequireJS, almond and other AMD loaders, and only do detection of cajon if you want to know if cajon is available.

How to use it

There is a demo directory that shows example use, but basically, put cajon.js in a <script> tag and load modules via require([]). Note the Restrictions section below though.

To optimize the demo, run:

node tools/r.js -o demo/app.build.js

This will generate the optimized project in a demo-built directory. All the modules in the build output will have been converted to AMD style, so that they can be loaded cross-domain without needing special CORS considerations.

The app.build.js build profile requires the r.js optimizer to be version 2.0.2 or later, because it uses 2.0.2's cjsTranslate build option that converts CommonJS/node modules to be define()-wrapped for the build.

Install

If using volo:

volo add cajon

If using npm:

npm install cajon

Or URL to latest release:

https://raw.github.com/requirejs/cajon/latest/cajon.js

Restrictions

Does not use node's module ID-to-path rules

So do not expect to npm install some code, then be able to require it using cajon.

Node uses multiple node_modules path lookups to find code and this is not efficient to do in a browser context. Also, many node modules depend on node's standard library or Node's environment, which are not available by default in the web browser.

If you do want to use some npm-installed code, and you know it will run in the browser, you can get it to work with cajon, but you will likely need to use the paths, map and packages requirejs config to get it to work.

Avoid computed require('') calls.

CommonJS/node module systems are synchronous, local file IO systems. So they allow these kinds of constructs:

//first example
var id = someCondition ? 'a' : 'b';
var a = require(id);

//second example
var a;
if (someCondition) {
    a = require('a');
} else {
    b = require('b');
}

The first example will fail in an AMD browser environment, since all dependencies need to be known, downloaded and executed before the code runs. If 'a' and 'b' are not already in that state, that first example will likely generate an error.

The second example can work, but know that the AMD loader will download and execute 'a' and 'b' before running that code.

If you use a runtime decision to grab a dependency, use the callback-style require() supported by AMD loaders:

var id = someCondition ? 'a' : 'b';
require([id], function (mod) {
    //do something with mod now
    //that it has been asynchronously
    //loaded and executed.
})

Or consider creating an AMD loader plugin that can do the decision logic but still be treated as a single string literal dependency:

var dep = require('has!condition?succesModuleId:failModuleId');

Both callback-style require and loader plugins are usable with cajon since it is just using requirejs behind the scenes.

Configuration

Cajon will only use the XHR+eval approach if the request is to the same domain as the HTML document. You can override this behavior if you know CORS-enabled browsers and servers will be used.

Set up a useXhr function in a cajon config passed to the loader:

require.config({
    cajon: {
        useXhr: function (url, protocol, hostname, port) {
            //Return true if XHR is usable, false if the
            //script tag approach to an AMD module/browser globals
            //script should be used.

            //url is the url being requested.
            //protocol, hostname and port area all values from the
            //current page that is using cajon. Compare them with
            //what is in `url` to make your decision.
        }
    }
});

If you need to configure each XHR object before it sends out its request, you can implement an onXhr method that gets called after xhr.open(), but before xhr.onreadystatechange() is set up and before xhr.send() is called:

require.config({
    cajon: {
        onXhr: function (xhr, url) {
            //xhr is the XHMLHttpRequest object.
            //url is the URL the xhr object is set to use.
        }
    }
});

License

MIT

Code of Conduct

jQuery Foundation Code of Conduct.

cajon's People

Contributors

jrburke avatar kirill-konshin avatar yortus avatar ysangkok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cajon's Issues

Remove Query from path in debugger...

Any way to decrease the length of the file name as displayed in the Chrome debugger by removing the query string parameters from the file path?

Convert this:

http://localhost:5000/gherkin-runner/gherkinRunner.html?featurePaths=features/stepArgumentTypes&libraryPaths=features/step_definitions/../gherkin-runner/scripts/text.js

To this:

http://localhost:5000/gherkin-runner/scripts/text.js

not working with text plugin

var template = require('text!tmpl/something.tmpl')
// Uncaught Error: Module name "text!tmpl/simple.tmpl_unnormalized2" has not been loaded yet for context: _
var template = require(['text!tmpl/something.tmpl'], function (template) {
  // no problem
})

Cajon is not available via Bower

Please make it available via Bower. Currently I have to refer to it in bower.json in a following ugly way:

"cajon": "https://raw.githubusercontent.com/requirejs/cajon/master/cajon.js",

Conditional comments around sourceUrl comment causing grey hair

Is there any chance Cajon would switch over to the //# sourceUrl= style of notation?

The conditional comments around the line which appends the magical //@ sourceURL= have a nasty habit of being stripped when passed through UglifyJS.

I attempted to solve the issue by rolling my own function for determining which comments to keep, but alas, the /*@end@*/ comment is ignored, apparently because there's no code following it in the block. If I add an executable line below the closing comment, it's passed into the preserve comment function for evaluation.

I do realise, that I should probably just change our development build to skip Cajon in the uglification process and just keep it as is, but I strive to having the build as similar as possible to minimise variables between environments.

Support require([js-data-uri]);

In this pull, base64 javascript url support was added for the paths config.
#845 . It also enables this snippet

var url = 'data:text/javascript,define([], function() { return "wut" })';
require([url], function (wut) {
    wut === 'wut'
});

See this example working with Require.js
http://jsfiddle.net/qsRT8/

I can't find cajon on any public CDN, but if you run the same thing with cajon it will fail because it is trying to do an XHR (because cajon) to the data uri, but that get's blocked by the browser because of Same-Origin Policy ('data:' is different protocol and thus different origin).

I am making a component loader that will support specifying a URL to JavaScript to run the component. The component may have a bundle that does all the right things at an http url, but it may just want to execute a bit of JavaScript without entailing the HTTP Request or having to host a file.

Could Cajon do a script tag load in the case that the URL is a data uri?

Backbone shim

Hi,

I had a config with Backbone shim working just fine with regular requirejs. Wanted to move over to use Cajon to reuse models in Node.js. It' seems however that shimming Backbone with the exact same config doesn't work:

paths: {
jquery: 'vendor/jquery',
underscore: 'vendor/underscore',
backbone: 'vendor/backbone',
...
},

shim: {
jquery: {
exports: 'jQuery'
},

underscore: {
  exports: '_'
},

backbone: {
  deps: ['underscore', 'jquery'],
  exports: 'Backbone'
},

When backbone is initialized there is not jQuery available, any idea why this would be?

Not working with handlebars via the requirejs-handlebars plugin?

Everything worked fine pre-cajon. Upon switching to cajon, handlebars (1.0.0, installed from bower, shimmed, used via the requirejs-handlebars plugin) is failing with this error:

Uncaught TypeError: Cannot call method 'compile' of undefined in /components/requirejs-handlebars/hb.js:15

This is the exact same error I'd see if I forgot to shim handlebars normally (ie. without cajon).

If I wrap handlebars.js with define([], function() { /*handlebars code*/ return Handlebars; }); or comment out the handlebars shim in requirejs.config and add module.exports = Handlebars; to the end of handlebars.js it works fine.

But with no modifications to handlebars.js, shim or not, I see that error. It's as if the handlebars shim config is being ignored.

"exports is not defined"

Hi, I'm trying to use cajon with some existing CJS modules that use this exports pattern:

function Example(name) {
    this.name = name;
}
exports.Example = Example;

However this doesn't seem to work with Cajon, console shows error:
"exports is not defined"

However, this pattern does:

function Example(name) {
    this.name = name;
}
module.exports = {
    Example: Example
}

Is this on purpose and/or expected? I have a lot of code that I'd rather not convert (which is why I'd like to use cajon here).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.