Skip to content

Commit 5d29327

Browse files
committed
Introducing run.def for defining modules, to help make the API clearer when defining modules vs just running code that has dependencies.
1 parent ec9ec55 commit 5d29327

33 files changed

Lines changed: 104 additions & 113 deletions

README.md

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
run.js loads JavaScript modules and JavaScript files. It is optimized for in-browser use, but can be easily adapted to other JavaScript environments.
44

5-
If the loaded file calls run() to define a JavaScript module, then run.js can properly trace the module's dependencies and evaluate modules in the correct order. run() allows for module modifiers and has a plugin system that supports features like i18n string bundles and text file dependencies.
5+
If the loaded file calls run.def() to define a JavaScript module, then run.js can properly trace the module's dependencies and evaluate modules in the correct order. RunJS allows for module modifiers and has a plugin system that supports features like i18n string bundles and text file dependencies.
66

77
It uses plain script tags to load modules/files, so it should allow for easy debugging.
88

@@ -48,10 +48,9 @@ See the **Configuration Options** section for information on changing the lookup
4848

4949
## Defining a Module
5050

51-
If the module does not have any dependencies, then just specify the name of the module as the first argument to run() and and the second argument is just an object literal that defines the module's properties. For example:
51+
If the module does not have any dependencies, then just specify the name of the module as the first argument to run.def() and and the second argument is just an object literal that defines the module's properties. For example:
5252

53-
run(
54-
"my/simpleshirt",
53+
run.def("my/simpleshirt",
5554
{
5655
color: "black",
5756
size: "unisize"
@@ -62,8 +61,7 @@ This example would be stored in a my/simpleshirt.js file.
6261

6362
If the module has dependencies, then specify the dependencies as the second argument (as an array) and then pass a function as the third argument. The function will be called to define the module once all dependencies have loaded. The function should return an object that defines the module:
6463

65-
run(
66-
"my/shirt",
64+
run.def("my/shirt",
6765
["my/cart", "my/inventory"],
6866
function(cart, inventory) {
6967
//return an object to define the "my/shirt" module.
@@ -94,8 +92,7 @@ The return object from the function call defines the "my/shirt" module. Be defin
9492

9593
If the modules do not have to return objects. Any valid return value from a function is allowed. Here is a module that returns a function as its module definition:
9694

97-
run(
98-
"my/title",
95+
run.def("my/title",
9996
["my/dependency1", "my/dependency2"],
10097
function(dep1, dep2) {
10198
//return a function to define "my/title". It gets or sets
@@ -114,8 +111,7 @@ Only one module should be defined per JavaScript file, given the nature of the m
114111

115112
If you define a circular dependency (A needs B and B needs A), then in this case when B's module function is called, it will get an undefined value for A. B can fetch A later after modules have been defined by using the run.get() method (be sure to specify run as a dependency so the right context is used to look up A):
116113

117-
run(
118-
"B",
114+
run.def("B",
119115
["run", "A"],
120116
function(run, a) {
121117
//"a" in this case will be null if A also asked for B,
@@ -142,8 +138,7 @@ To define a bundle, put it in a directory called "nls" -- the i18n! plugin assum
142138

143139
The contents of that file should look like so:
144140

145-
run(
146-
"i18n!my/nls/colors",
141+
run.def("i18n!my/nls/colors",
147142
[{
148143
"root": {
149144
"red": "red",
@@ -157,8 +152,7 @@ Notice that an object literal with a property of "root" as given as the only dep
157152

158153
You can then use the above module in another module, say, in a my/lamps.js file:
159154

160-
run(
161-
"my/lamps",
155+
run.def("my/lamps",
162156
["i18n!my/nls/colors"],
163157
function(colors) {
164158
return {
@@ -171,8 +165,7 @@ The my/lamps module has one property called "testMessage" that uses colors.red t
171165

172166
Later, when you want to add a specific translation to a file, say for the fr-fr locale, change my/nls/colors to look like so:
173167

174-
run(
175-
"i18n!my/nls/colors",
168+
run.def("i18n!my/nls/colors",
176169
[{
177170
"root": {
178171
"red": "red",
@@ -185,8 +178,7 @@ Later, when you want to add a specific translation to a file, say for the fr-fr
185178

186179
Then define a file at my/nls/fr-fr/colors.js that has the following contents:
187180

188-
run(
189-
"i18n!my/nls/fr-fr/colors",
181+
run.def("i18n!my/nls/fr-fr/colors",
190182
{
191183
"red": "rouge",
192184
"blue": "bleu",
@@ -200,8 +192,7 @@ run.js is also smart enough to pick the right locale bundle, the one that most c
200192

201193
run.js also combines bundles together, so for instance, if the french bundle was defined like so (omitting a value for red):
202194

203-
run(
204-
"i18n!my/nls/fr-fr/colors",
195+
run.def("i18n!my/nls/fr-fr/colors",
205196
{
206197
"blue": "bleu",
207198
"green": "vert"
@@ -223,8 +214,7 @@ run.js has a plugin, run/text.js, that can help with this issue. It will automat
223214

224215
You can specify a text file resource as a dependency like so:
225216

226-
run(
227-
["some/module", "text!some/module!html", "text!some/module!css"],
217+
run(["some/module", "text!some/module!html", "text!some/module!css"],
228218
function(module, html, css) {
229219
//the html variable will be the text of the some/module.html file
230220
//the css variable will be the text of the som/module.css file.
@@ -236,8 +226,7 @@ Notice the !html and !css suffixes to specify the extension of the file.
236226

237227
For HTML/XML/SVG files, there is another option you can pass !strip, which strips XML declarations so that external SVG and XML documents can be added to a document without worry. Also, if the string is an HTML document, only the part inside the body tag is returned. Example:
238228

239-
run(
240-
["text!some/module!html!strip"],
229+
run(["text!some/module!html!strip"],
241230
function(html) {
242231
//the html variable will be the text of the some/module.html file,
243232
//but only the part inside the body tag.
@@ -376,16 +365,15 @@ The example above in the **Multiversion Support** section shows how code can lat
376365

377366
## run.pause()/run.resume() for build layers/bundles
378367

379-
If you want to include many modules that use run() in one script, and those modules may depend on each other, then use run.pause() before the set of run() calls to prevent runjs from tracing dependencies on each run() call. When all the run() calls have finished in the file, call run.resume() to have the dependencies properly traced.
368+
If you want to include many modules that use run.def() in one script, and those modules may depend on each other, then use run.pause() before the set of run calls to prevent runjs from tracing dependencies on each run call. When all the run calls have finished in the file, call run.resume() to have the dependencies properly traced.
380369

381370
Only use run.pause() and run.resume() on a file-by-file basis. Do not use run.pause() in one file and run.resume() in another file. Multiple files can call run.pause()/resume() combinations though.
382371

383372
Example:
384373

385374
run.pause();
386375

387-
run(
388-
"alpha",
376+
run.def("alpha",
389377
["beta"],
390378
function (beta) {
391379
return {
@@ -395,16 +383,15 @@ Example:
395383
}
396384
);
397385

398-
run(
399-
"beta",
386+
run.def("beta",
400387
{
401388
name: "beta"
402389
}
403390
);
404391

405392
run.resume();
406393

407-
If run.pause() and run.resume() were not used, then the run() call to define "alpha" would have tried to load "beta" via another network/file IO call.
394+
If run.pause() and run.resume() were not used, then the run.def() call to define "alpha" would have tried to load "beta" via another network/file IO call.
408395

409396
## Module Modifiers
410397

@@ -440,7 +427,7 @@ You are not required to register modifiers with runjs. Only do it if you want to
440427

441428
### Modifier Definition
442429

443-
A modifier definition looks like a normal run() module definition, but:
430+
A modifier definition looks like a normal run.def() module definition, but:
444431

445432
* run.modify() is used.
446433
* the target module's name is listed first in the run.modify() call.
@@ -462,10 +449,10 @@ For the example given above in Modifier Registration, where "my/target1" is the
462449

463450
# History and Influences
464451

465-
I work a lot on the Dojo Loader. The normal dojo loader uses synchronous XMLHttpRequest (XHR) calls. However, the XHR loader cannot load Dojo modules from other domains because of the same-origin restrictions. So I created the xdomain loader that required a build step to inject function wrappers similar to what run() uses, but more complex, due to i18n bundle loading and dojo.requireIf behavior. Because of the more complex i18n and requireIf requirements and the existence of many dojo modules already out in the world, I did not feel like the Dojo community would consider writing modules with function wrappers manually.
452+
I work a lot on the Dojo Loader. The normal dojo loader uses synchronous XMLHttpRequest (XHR) calls. However, the XHR loader cannot load Dojo modules from other domains because of the same-origin restrictions. So I created the xdomain loader that required a build step to inject function wrappers similar to what RunJS uses, but more complex, due to i18n bundle loading and dojo.requireIf behavior. Because of the more complex i18n and requireIf requirements and the existence of many dojo modules already out in the world, I did not feel like the Dojo community would consider writing modules with function wrappers manually.
466453

467-
However, the sync XHR loader has other issues, like making debugging harder. Recently, David Mark suggested that Dojo use document.write() to load modules before the page loads to help with that issue, but that means required dependencies would not load until after the current module executes. This can cause errors if the module references a dependency as part of the module's definition. So a function wrapper is needed. The Dojo community seemed more amenable to considering a function wrapper, particularly since we are considering a Dojo 2.0 that can break some APIs. I fleshed out some of the details for run() on the dojo-contributors list, and Mike Wilson originally pushed for a more generic loader that could load plain files as well as allow for different contexts. I hope run.js can be used for Dojo 2.0 modules, but more investigation on i18n and requireIf support is needed.
454+
However, the sync XHR loader has other issues, like making debugging harder. Recently, David Mark suggested that Dojo use document.write() to load modules before the page loads to help with that issue, but that means required dependencies would not load until after the current module executes. This can cause errors if the module references a dependency as part of the module's definition. So a function wrapper is needed. The Dojo community seemed more amenable to considering a function wrapper, particularly since we are considering a Dojo 2.0 that can break some APIs. I fleshed out some of the details for RunJS on the dojo-contributors list, and Mike Wilson originally pushed for a more generic loader that could load plain files as well as allow for different contexts. I hope run.js can be used for Dojo 2.0 modules, but more investigation on i18n and requireIf support is needed.
468455

469-
YUI 3's use() function is also very similar to run(), and use()'s API (but not code) also informed run()'s structure. I believe run() is more generic, since YUI seems to use labels for their modules that do not directly correspond to file paths. It also looks like it cannot load plain JS files, and I liked explicitly passing the dependent modules as arguments to the function definition. YUI.use() might support some of these features underneath, but I just looked at the top-level API doc.
456+
YUI 3's use() function is also very similar to run, and use()'s API (but not code) also informed run's structure. I believe RunJS is more generic, since YUI seems to use labels for their modules that do not directly correspond to file paths. It also looks like it cannot load plain JS files, and I liked explicitly passing the dependent modules as arguments to the function definition. YUI.use() might support some of these features underneath, but I just looked at the top-level API doc.
470457

471458
I originally wanted something that would work with CommonJS modules, but those modules seem to be structured assuming a synchronous module loader, which is possible in server-side JavaScript environments. However, I am mostly concerned with something that works well in the browser, and that means needing a function wrapper so we can use script tags. Using synchronous XHR is not very friendly for new developers or people who want easy of debugging across browsers. It can also be slower than plain script tag loading. Some environments, like Adobe AIR do not allow eval() and most developers are taught that eval() is evil and should be avoided. run.js should be very usable in a server-side environment, and I plan on using it there too.

build/convert/convertDojo.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
* * After the build put a dependency in dijit.dijit-all for dijit.dijit to get reloads in IE to work.
2020
*
2121
* Usage:
22-
* java -classpath path/to/rhino/js.jar convertDojo.js path/to/dojo rundojo
22+
* java -jar path/to/rhino/js.jar convertDojo.js path/to/dojo rundojo
2323
*
2424
*/
2525
/*jslint plusplus: false */
@@ -88,7 +88,7 @@ fileContents = 'run.baseUrlRegExp = /dojo(\\.xd)?\\.js(\\W|$)/i;' +
8888
fileUtil.readFile(savePath + "/dojo/_base/_loader/loader.js") +
8989
fileUtil.readFile(savePath + "/dojo/_base/_loader/hostenv_browser.js");
9090

91-
fileContents += 'run("dojo", function(){return dojo;});run("dijit", function(){return dijit;});run("dojox", function(){return dojox;});';
91+
fileContents += 'run.def("dojo", function(){return dojo;});run.def("dijit", function(){return dijit;});run.def("dojox", function(){return dojox;});';
9292

9393
fileUtil.saveUtf8File(savePath + "/dojo.js", fileContents);
9494

@@ -152,7 +152,7 @@ function writeRunEnd(prefixProps, contents) {
152152
argString += ', _R' + i;
153153
}
154154

155-
return 'run("' + provideName + '", ["run", "dojo", "dijit", "dojox"' +
155+
return 'run.def("' + provideName + '", ["run", "dojo", "dijit", "dojox"' +
156156
reqString +
157157
'], function(run, dojo, dijit, dojox' + argString + ') {\n' +
158158
prefixProps.match +
@@ -292,7 +292,7 @@ function i18nConvert(fileName, convertedFileName, srcDir) {
292292

293293
if (localeRegExp.test(fileName)) {
294294
//A locale-specific bundle. Easier to handle.
295-
text = 'run("i18n!' + modName + '",\n' + contents + ');';
295+
text = 'run.def("i18n!' + modName + '",\n' + contents + ');';
296296
} else {
297297
//A root bundle. A bit more work. First, get the basic name
298298
matches = rootBundleRegExp.exec(modName);
@@ -309,7 +309,7 @@ function i18nConvert(fileName, convertedFileName, srcDir) {
309309
}
310310
}
311311

312-
text = 'run("i18n!' + prefixName + baseName + '",\n{ "root": ' + contents + locales + '\n});';
312+
text = 'run.def("i18n!' + prefixName + baseName + '",\n{ "root": ' + contents + locales + '\n});';
313313
}
314314

315315
fileUtil.saveUtf8File(convertedFileName, text);

build/example.build.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ run(
109109
//a built file that contains all of its dependencies. If that module or any
110110
//of its dependencies includes i18n bundles, they may not be included in the
111111
//built file unless the locale: section is set above.
112-
run("foo/bar/bop");
112+
run.def("foo/bar/bop");
113113

114114
//This invocation combines all the dependencies of foo/bar/bop and foo/bar/bee
115115
//and any of its dependencies into one file. If that module or any
@@ -130,6 +130,6 @@ run(
130130
exclude: [
131131
"foo/bar/bop"
132132
]
133-
}
133+
},
134134
"foo/bar/bip"
135135
);

build/jslib/runPatch.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ readFile: false, processPragmas: false */
1414

1515
(function () {
1616
var runStartRegExp = /(^|\s+|;)run\s*\(/g,
17-
runDepsRegExp = /run\s*\(\s*(['"][^'"]+['"]\s*,)?\s*(\[[^\]]+\])/,
17+
runDepsRegExp = /run(\s*\.\s*def)?\s*\(\s*(['"][^'"]+['"]\s*,)?\s*(\[[^\]]+\])/,
1818
oldExec = run.exec;
1919

2020
//These variables are not contextName-aware since the build should
@@ -111,7 +111,7 @@ readFile: false, processPragmas: false */
111111
if (context.config.execModules || moduleName === "run/text" || moduleName === "run/i18n") {
112112
eval(contents);
113113
} else {
114-
//Only find the run() parts with [] dependencies and
114+
//Only find the run parts with [] dependencies and
115115
//evaluate those. This path is useful when the code
116116
//does not follow the strict run pattern of wrapping all
117117
//code in a run callback.
@@ -128,7 +128,7 @@ readFile: false, processPragmas: false */
128128
if (deps[1].match(new RegExp('[\'"]' + moduleName + '[\'"]'))) {
129129
run.modulesWithNames[moduleName] = true;
130130
}
131-
eval('run(' + deps[1] + deps[2] + ');');
131+
eval('run.def(' + deps[1] + deps[2] + ');');
132132
}
133133
//Just call with dependencies.
134134
if (deps[2]) {

build/runbuild.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*
1515
* General use:
1616
*
17-
* Create a build.js file that has run() calls to the build layer/bundle that you
17+
* Create a build.js file that has run calls to the build layer/bundle that you
1818
* want to create. Use the config option on runjs to specify paths on where
1919
* to find things. See example.build.js for more information.
2020
*/
@@ -271,7 +271,7 @@ var run;
271271
config.runUrl = baseUrlFile.getParentFile().getAbsolutePath() + "/run.js";
272272
config.dir = baseUrlFile.getAbsolutePath() + "/build/";
273273

274-
//Set up the build file environment by creating a dummy run() function to
274+
//Set up the build file environment by creating a dummy run function to
275275
//catch the build file information.
276276
run = function (cfg, name, deps) {
277277
var layer;
@@ -361,7 +361,7 @@ var run;
361361
fileUtil.copyFile(config.runUrl, builtRunPath, true);
362362
}
363363

364-
//Figure out source file location for each layer. Do this by seeding run()
364+
//Figure out source file location for each layer. Do this by seeding run
365365
//with source area configuration. This is needed so that later the layers
366366
//can be manually copied over to the source area, since the build may be
367367
//run multiple times and the above copyDir call only copies newer files.
@@ -474,7 +474,7 @@ var run;
474474
placeHolderModName = run.buildFileToModule[path];
475475
//If we have a name, but no defined module, then add in the placeholder.
476476
if (placeHolderModName && !run.modulesWithNames[placeHolderModName]) {
477-
fileContents += 'run("' + placeHolderModName + '", function(){});\n';
477+
fileContents += 'run.def("' + placeHolderModName + '", function(){});\n';
478478
}
479479
}
480480

0 commit comments

Comments
 (0)