Skip to content

Commit b7f2e2d

Browse files
committed
AMD support
- Build js bundles using requirejs; - Overload requirejs "node/file" to use memory cached files instead of filesystem; - Make Builder#build asynchronous Closes gh-156
1 parent adf864f commit b7f2e2d

11 files changed

+531
-204
lines changed

download.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var downloadLogger, jqueryUis,
22
_ = require( "underscore" ),
3+
Builder = require( "./lib/builder" ),
34
fs = require( "fs" ),
45
Handlebars = require( "handlebars" ),
56
JqueryUi = require( "./lib/jquery-ui" ),
@@ -83,7 +84,7 @@ Frontend.prototype = {
8384

8485
create: function( fields, response, callback ) {
8586
try {
86-
var build, components, packer, start, theme,
87+
var builder, components, jqueryUi, packer, start, theme,
8788
themeVars = null;
8889
if ( fields.theme !== "none" ) {
8990
themeVars = querystring.parse( fields.theme );
@@ -99,10 +100,11 @@ Frontend.prototype = {
99100
});
100101
components = Object.keys( _.omit( fields, "scope", "theme", "theme-folder-name", "version" ) );
101102
start = new Date();
102-
build = JqueryUi.find( fields.version ).build( components, {
103+
jqueryUi = JqueryUi.find( fields.version );
104+
builder = new Builder( jqueryUi, components, {
103105
scope: fields.scope
104106
});
105-
packer = new Packer( build, theme, {
107+
packer = new Packer( builder, theme, {
106108
scope: fields.scope
107109
});
108110
response.setHeader( "Content-Type", "application/zip" );
@@ -116,9 +118,9 @@ Frontend.prototype = {
116118
JSON.stringify({
117119
build_size: written,
118120
build_time: new Date() - start,
119-
components: build.components,
121+
components: builder.components,
120122
theme_name: theme.name,
121-
version: build.pkg.version
123+
version: jqueryUi.pkg.version
122124
})
123125
);
124126
return callback();

lib/builder.1.10.0.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ stripBanner = util.stripBanner;
1515
/**
1616
* Builder 1.10
1717
*/
18-
function Builder_1_10_0( build, jqueryUi, components, options ) {
18+
function Builder_1_10_0( build, jqueryUi, components, options, callback ) {
1919
var _bundleCss, baseCss, baseCssMin, cssComponentFileNames, existingCss, jsComponentFileNames, selectedDemoRe, selectedRe,
2020
files = jqueryUi.files(),
2121
min = function( file ) {
@@ -200,7 +200,7 @@ function Builder_1_10_0( build, jqueryUi, components, options ) {
200200
// Ad hoc
201201
build.jqueryCore = files.jqueryCore;
202202

203-
return build;
203+
callback( null, build );
204204
}
205205

206206
module.exports = Builder_1_10_0;

lib/builder.1.11.0.js

Lines changed: 148 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,42 @@
1-
var _basename, demoIndexTemplate, docsTemplate, flatten, stripBanner,
1+
var _basename, bundleJsIntro, bundleI18nIntro, bundleOutro, demoIndexTemplate, docsTemplate, flatten, stripBanner,
2+
async = require( "async" ),
23
banner = require( "./banner" ),
34
Files = require( "./files" ),
45
fs = require( "fs" ),
56
handlebars = require( "handlebars" ),
67
path = require( "path" ),
8+
rjs = require( "./rjs" ),
79
sqwish = require( "sqwish" ),
810
ThemeRoller = require( "./themeroller" ),
911
util = require( "./util" );
1012

13+
bundleJsIntro = "(function( factory ) {\n" +
14+
" if ( typeof define === \"function\" && define.amd ) {\n" +
15+
"\n" +
16+
" // AMD. Register as an anonymous module.\n" +
17+
" define([ \"jquery\" ], factory );\n" +
18+
" } else {\n" +
19+
"\n" +
20+
" // Browser globals\n" +
21+
" factory( jQuery );\n" +
22+
" }\n" +
23+
"}(function( $ ) {";
24+
25+
bundleI18nIntro = "(function( factory ) {\n" +
26+
" if ( typeof define === \"function\" && define.amd ) {\n" +
27+
"\n" +
28+
" // AMD. Register as an anonymous module.\n" +
29+
" define([ \"jquery\" ], factory );\n" +
30+
" } else {\n" +
31+
"\n" +
32+
" // Browser globals\n" +
33+
" factory( jQuery );\n" +
34+
" }\n" +
35+
"}(function( $ ) {\n" +
36+
"\n" +
37+
"var datepicker = $.datepicker;\n";
38+
39+
bundleOutro = "}));";
1140
demoIndexTemplate = handlebars.compile( fs.readFileSync( __dirname + "/../template/zip/demos_index.html", "utf8" ) );
1241
docsTemplate = handlebars.compile( fs.readFileSync( __dirname + "/../template/zip/docs.html", "utf8" ) );
1342
flatten = util.flatten;
@@ -20,11 +49,51 @@ path.basename = function() {
2049
return _basename.apply( _basename, arguments ).replace( /\/$/, "" );
2150
};
2251

52+
function camelCase( input ) {
53+
return input.toLowerCase().replace( /[-/](.)/g, function( match, group1 ) {
54+
return group1.toUpperCase();
55+
});
56+
}
57+
58+
function rjsConfig( attributes ) {
59+
return {
60+
files: attributes.files,
61+
config: {
62+
appDir: "ui",
63+
baseUrl: ".",
64+
paths: {
65+
jquery: "../" + attributes.files.jqueryCore[ 0 ].path.replace( /\.js$/, "" )
66+
},
67+
wrap: {
68+
start: attributes.intro,
69+
end: bundleOutro
70+
},
71+
modules: [{
72+
name: "output",
73+
include: attributes.include,
74+
exclude: attributes.exclude,
75+
create: true
76+
}],
77+
onBuildWrite: function ( id, path, contents ) {
78+
var name = camelCase( id.replace( /ui\//, "" ).replace( /\.js$/, "" ) );
79+
return contents
80+
81+
// Remove UMD wrapper.
82+
.replace( /\(function\( factory[\s\S]*?\(function\( [^\)]* \) \{/, "" )
83+
.replace( /\}\)\);\s*?$/, "" )
84+
85+
// Replace return exports for var =.
86+
.replace( /\nreturn/, "\nvar " + name + " =" );
87+
}
88+
}
89+
};
90+
}
91+
2392
/**
2493
* Builder 1.11
2594
*/
26-
function Builder_1_11_0( build, jqueryUi, components, options ) {
27-
var _bundleCss, baseCss, baseCssMin, cssComponentFileNames, docsCategories, existingCss, jsComponentFileNames, selectedDemoRe, selectedRe,
95+
function Builder_1_11_0( build, jqueryUi, components, options, callback ) {
96+
var _bundleCss, baseCss, baseCssMin, cssComponentFileNames, docsCategories, existingCss, selectedDemoRe, selectedRe,
2897
files = jqueryUi.files(),
2998
min = function( file ) {
3099
return files.min( file );
@@ -69,41 +138,6 @@ function Builder_1_11_0( build, jqueryUi, components, options ) {
69138
return (/images/).test( file.path );
70139
});
71140

72-
// I18n files
73-
if ( components.indexOf( "datepicker" ) >= 0 ) {
74-
build.i18nFiles = files.i18nFiles;
75-
build.i18nMinFiles = files.i18nFiles.map( min );
76-
build.bundleI18n = Files({
77-
path: "jquery-ui-i18n.js",
78-
data: files.i18nFiles.reduce(function( sum, file ) {
79-
return sum + stripBanner( file );
80-
}, banner( jqueryUi.pkg, files.i18nFiles.paths().map( path.basename ) ) )
81-
});
82-
build.bundleI18nMin = Files({
83-
path: "jquery-ui-i18n.min.js",
84-
data: banner( jqueryUi.pkg, files.i18nFiles.paths().map( path.basename ), { minify: true }) + stripBanner( files.min( build.bundleI18n[ 0 ] ) )
85-
});
86-
} else {
87-
build.i18nFiles = build.i18nMinFiles = build.bundleI18n = build.bundleI18nMin = Files();
88-
}
89-
90-
// Bundle JS (and minified)
91-
jsComponentFileNames = components.map(function( component ) {
92-
return component + ".js";
93-
});
94-
build.bundleJs = Files({
95-
path: "jquery-ui.js",
96-
data: build.components.reduce(function( sum, component ) {
97-
return sum + stripBanner( files.get( "ui/" + component + ".js" ) );
98-
}, banner( jqueryUi.pkg, jsComponentFileNames ) )
99-
});
100-
build.bundleJsMin = Files({
101-
path: "jquery-ui.min.js",
102-
data: build.components.reduce(function( sum, component ) {
103-
return sum + stripBanner( files.min( files.get( "ui/" + component + ".js" ) ) );
104-
}, banner( jqueryUi.pkg, jsComponentFileNames, { minify: true } ) )
105-
});
106-
107141
// Bundle CSS (and minified)
108142
existingCss = function( component ) {
109143
return files.get( "themes/base/" + component + ".css" ) !== undefined;
@@ -208,7 +242,82 @@ function Builder_1_11_0( build, jqueryUi, components, options ) {
208242
// Ad hoc
209243
build.jqueryCore = files.jqueryCore;
210244

211-
return build;
245+
// I18n files
246+
function i18nFiles( callback ) {
247+
if ( components.indexOf( "datepicker" ) >= 0 ) {
248+
build.i18nFiles = files.i18nFiles;
249+
build.i18nMinFiles = files.i18nFiles.map( min );
250+
async.series([
251+
function( callback ) {
252+
rjs( rjsConfig({
253+
files: files,
254+
intro: bundleI18nIntro,
255+
include: files.i18nFiles.rename( /ui\//, "" ).rename( /\.js$/, "" ).paths(),
256+
exclude: [ "jquery", "core", "datepicker" ]
257+
}), function( error, data ) {
258+
if ( error ) {
259+
return callback( error );
260+
}
261+
build.bundleI18n = Files({
262+
path: "jquery-ui-i18n.js",
263+
data: banner( jqueryUi.pkg, files.i18nFiles.paths().map( path.basename ) ) + data
264+
});
265+
callback();
266+
});
267+
},
268+
function( callback ) {
269+
build.bundleI18nMin = Files({
270+
path: "jquery-ui-i18n.min.js",
271+
data: banner( jqueryUi.pkg, files.i18nFiles.paths().map( path.basename ), { minify: true }) + stripBanner( files.min( build.bundleI18n[ 0 ] ) )
272+
});
273+
callback();
274+
}
275+
], callback );
276+
} else {
277+
build.i18nFiles = build.i18nMinFiles = build.bundleI18n = build.bundleI18nMin = Files();
278+
callback();
279+
}
280+
}
281+
282+
// Bundle JS (and minified)
283+
function bundleJs( callback ) {
284+
var jsComponentFileNames = components.map(function( component ) {
285+
return component + ".js";
286+
});
287+
async.series([
288+
function( callback ) {
289+
rjs( rjsConfig({
290+
files: files,
291+
intro: bundleJsIntro,
292+
include: components,
293+
exclude: [ "jquery" ]
294+
}), function( error, data ) {
295+
if ( error ) {
296+
return callback( error );
297+
}
298+
build.bundleJs = Files({
299+
path: "jquery-ui.js",
300+
data: banner( jqueryUi.pkg, jsComponentFileNames ) + data
301+
});
302+
callback();
303+
});
304+
},
305+
function( callback ) {
306+
build.bundleJsMin = Files({
307+
path: "jquery-ui.min.js",
308+
data: banner( jqueryUi.pkg, jsComponentFileNames, { minify: true } ) + stripBanner( files.min( build.bundleJs[ 0 ] ) )
309+
});
310+
callback();
311+
}
312+
], callback );
313+
}
314+
315+
async.series([
316+
i18nFiles,
317+
bundleJs
318+
], function( error ) {
319+
callback( error, build );
320+
});
212321
}
213322

214323
module.exports = Builder_1_11_0;

lib/builder.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
var cache,
2-
Builder_1_10_0 = require( "./builder.1.10.0.js" ),
3-
Builder_1_11_0 = require( "./builder.1.11.0.js" ),
42
Cache = require( "./cache" ),
53
semver = require( "semver" );
64

@@ -22,23 +20,47 @@ function Builder( jqueryUi, components, options ) {
2220
}
2321

2422
Builder.prototype = {
25-
build: function() {
23+
build: function( callback ) {
2624
var cacheKey = this.jqueryUi.pkg.version + JSON.stringify( this.expandComponents( this.components ) ),
2725
cached = cache.get( cacheKey );
2826

2927
if ( cached ) {
30-
return cached;
28+
29+
// if we have data, call the callback, otherwise push ours
30+
if ( cached.data ) {
31+
callback( null, cached.data );
32+
} else {
33+
cached.callbacks.push( callback );
34+
}
35+
return true;
36+
}
37+
38+
cached = {
39+
callbacks: [ callback ]
40+
};
41+
cache.set( cacheKey, cached );
42+
43+
function done( err, data ) {
44+
var callbacks = cached.callbacks;
45+
if ( !err ) {
46+
cached.data = data;
47+
delete cached.callbacks;
48+
}
49+
callbacks.forEach(function( callback ) {
50+
callback( err, data );
51+
});
52+
delete cached.callbacks;
53+
if ( err ) {
54+
cache.destroy( cacheKey );
55+
}
3156
}
3257

3358
// FIXME s/1.11.0pre/1.11.0
3459
if ( semver.gte( this.jqueryUi.pkg.version, "1.11.0pre" ) ) {
35-
cached = Builder_1_11_0( this, this.jqueryUi, this.components, this.options );
60+
require( "./builder.1.11.0" )( this, this.jqueryUi, this.components, this.options, done );
3661
} else {
37-
cached = Builder_1_10_0( this, this.jqueryUi, this.components, this.options );
62+
require( "./builder.1.10.0" )( this, this.jqueryUi, this.components, this.options, done );
3863
}
39-
40-
cache.set( cacheKey, cached );
41-
return cached;
4264
},
4365

4466
expandComponents: function( components ) {

lib/jquery-ui.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,6 @@ JqueryUi.find = function( version ) {
8787
};
8888

8989
JqueryUi.prototype = {
90-
build: function( components, options ) {
91-
var builder;
92-
if ( !Builder ) {
93-
Builder = require( "./builder" );
94-
}
95-
builder = new Builder( this, components, options );
96-
return builder.build();
97-
},
98-
9990
categories: function() {
10091
if ( !this._categories ) {
10192
var map = {};

0 commit comments

Comments
 (0)