Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RequireJS: Is there a way to achieve multiple base URLs?

I want to use a separate domain as a JavaScript framework and it will create a base require config which I can augment from the app.

foo.example.com
    main.js
    lib/foo-specific.js
framework.example.com
    framework.js <-- entry point
    lib/jquery.js
    lib/etc...

Optimally, I'd like to be able to require 'lib/foo-specific' and/or 'lib/jquery' and have the paths just resolve nicely, but from what I've found, there's no way to do this, unless I use a specific path key/value for every js file in the framework. At the moment, I've got a custom plugin to load the given path with a different base url (e.g. fw!lib/jquery), though if I wanted to use the text! plugin, it won't work as plugin chaining is unsupported.

See https://github.com/jpillora/js-framework for what I've currently got, and also https://github.com/jpillora/prettyprinter for a use case.

Is there a clean way to solve this ? or to achieve multiple base URLs ?

Note: I have also looked into multiple require instances, though I don't think that would work as I'd like the the app to be able to access the framework's config.

like image 919
jpillora Avatar asked Sep 06 '12 14:09

jpillora


People also ask

What is Shim RequireJS?

As per RequireJS API documentation, shim lets you. Configure the dependencies, exports, and custom initialization for older, traditional "browser globals" scripts that do not use define() to declare the dependencies and set a module value. - Configuring dependencies.

What is the main purpose of the RequireJS framework?

RequireJS is a JavaScript file and module loader. It improves perceived page load times because it allows JavaScript to load in the background. In particular, it enables asynchronous JavaScript loading.

Should I use RequireJS?

Generally you only use RequireJS in its loading form during development. Once the site is done and ready for deployment, you minify the code. The advantage here is RequireJS knows exactly what your dependencies are, and thus can easily minify the code in the correct order.

What is RequireJS config?

Advertisements. RequireJS can be initialized by passing the main configuration in the HTML template through the data-main attribute. It is used by RequireJS to know which module to load in your application. For instance − <script data-main = "scripts/main" src = "scripts/require.js"></script>


2 Answers

Answered by James Burke on RequireJS Github Issue's page: Issue #447: Multiple Base URLs · jrburke/requirejs.

Turns out to be quite simple if data-main is the only entry point to your scripts(comments for more info), I solved my particular problem with the following:

My app's index.html:

<script src="http://framework.jpillora.com/js/lib/require.js"    data-main="http://framework.jpillora.com/js/framework" > </script> 

has the requirejs entry point set to framework.js:

var framework = ... //set using script elements src attribute  require.config({      baseUrl: 'js/',      //Framework paths     paths: {       'framework': framework,       'lib'      : framework + 'js/lib',       'ext'      : framework + 'js/ext',       'util'     : framework + 'js/util'     },      //Shortcuts     map: {       '*': {         ...       }     },      //Non-modularised libraries with deps     shim: {       ...     } });  require(['main']); 

So instead of normally doing index.html->main.js, we're adding an extra step index.html->framework.js->main.js, which gives the app code knowledge of paths to the framework code.

For example, in the app http://prettyprint.jpillora.com/, once require has loaded framework.js, it will setup paths to lib/... which to http://framework.jpillora.com/ and set the baseUrl as ./js/ so once main is required, it will have the base url set to it's own domain and lib pointing to another domain.

Which results in require(['lib/foo', 'view/bar']); resolving to:

http://framework.jpillora.com/js/lib/foo.js and http://prettyprint.jpillora.com/js/view/bar.js

As displayed here, the app is only a main.js everything else comes from the framework:

chrome devtools loaded app

So finally, whenever I load an app's main.js via with the above framework.js, I then have access to all of my commonly used libraries and utility classes. See app source.

Also note, with the r.js optimiser and a nice local file structure, one can also optimise the app into a single js file pulling only what's required from framework.

like image 68
jpillora Avatar answered Sep 21 '22 20:09

jpillora


The problem

I had a similar problem while trying to set up a testing environment. I had a file structure like this:

myApp/     src/         js/             app.js             data.js             lib/underscore.js     test/         karma.conf.js         test-main.js         matchers.js         spec/             data.js 

Here's where it gets tricky: my app scripts (app.js and data.js) assume a RequireJS configuration that resolves data to src/js/data.js, lib/underscore to src/js/lib/underscore.js etc, so I need that configuration in my test environment as well:

test/test-main.js ----------------- require.config({   // Karma serves files under /base, which is the basePath from your config file   baseUrl: '/base/src/js',   // ... }); 

Now I can write my tests:

test/spec/data.js ----------------- define(['data', '../../test/matchers'], function(dataModule) {     describe('The data module', function() {         it('should satisfy my custom matcher', function() {             expect(dataModule).toSatisfyMyCustomMatcher();         });     }); }); 

With some custom matchers:

test/matchers.js ---------------- define([], function() {     beforeEach(function() {         this.addMatchers({             toSatisfyMyCustomMatcher: function() {                 return this.actual.isGood;             },         });     }); }); 

However, that '../../test/matchers' part is horrendously ugly. The test specifications shouldn't be bothered with knowing file paths to other modules - that's RequireJS's job. Instead we want to use symbolic names.

The solution

The RequireJS paths config can also map directories.

The path that is used for a module name should not include an extension, since the path mapping could be for a directory.

So, the solution is a simple path config:

test/test-main.js ----------------- require.config({   baseUrl: '/base/src/js',   paths: {       test: '../../test',   },   // ... }); 

Now I can refer to the test directory as if it were a child of the baseUrl:

test/spec/data.js ----------------- define(['data', 'test/matchers'], function(dataModule) {     // ... }); 

Which in my case effectively comes out pretty much the same as if I could have multiple baseUrls.

like image 22
Emil Lundberg Avatar answered Sep 18 '22 20:09

Emil Lundberg