[2.0] build system updates, esp for language file generation

This commit is contained in:
Adam Shaw
2014-01-24 22:57:24 -08:00
parent 564ee6a9a9
commit 05ab0a7cf8
2 changed files with 271 additions and 10 deletions
+62 -10
View File
@@ -33,7 +33,10 @@ module.exports = function(grunt) {
grunt.registerTask('default', 'archive');
// Bare minimum for debugging
grunt.registerTask('dev', 'lumbar:build');
grunt.registerTask('dev', [
'lumbar:build',
'generateLanguages'
]);
@@ -41,6 +44,7 @@ module.exports = function(grunt) {
----------------------------------------------------------------------------------------------------*/
grunt.registerTask('modules', 'Build the FullCalendar modules', [
'clean:modules',
'lumbar:build',
'concat:moduleVariables',
'uglify:modules'
@@ -75,7 +79,37 @@ module.exports = function(grunt) {
ext: '.min.js'
}
config.clean.modules = 'build/out/*';
config.clean.modules = 'build/out/**';
/* Languages
----------------------------------------------------------------------------------------------------*/
grunt.registerTask('languages', [
'clean:languages',
'generateLanguages',
'uglify:languages'
]);
config.generateLanguages = {
moment: 'lib/moment/lang/',
datepicker: 'lib/jquery-ui/ui/i18n/',
fullCalendar: 'lang/',
dest: 'build/out/lang/'
};
config.uglify.languages = {
expand: true,
cwd: 'build/out/lang/',
src: '*.js',
dest: 'build/out/lang-min/'
};
config.clean.languages = [
'build/out/lang/*',
'build/out/lang-min/*'
];
@@ -83,10 +117,12 @@ module.exports = function(grunt) {
----------------------------------------------------------------------------------------------------*/
grunt.registerTask('archive', 'Create a distributable ZIP archive', [
'clean:modules',
'clean:archive',
'modules',
'languages',
'copy:archiveModules',
'copy:archiveLanguages',
'copy:archiveMoment',
'copy:archiveJQuery',
'concat:archiveJQueryUI',
'copy:archiveDemos',
@@ -103,6 +139,18 @@ module.exports = function(grunt) {
dest: 'build/archive/fullcalendar/'
};
config.copy.archiveLanguages = {
expand: true,
cwd: 'build/out/lang-min/',
src: '*.js',
dest: 'build/archive/fullcalendar/lang/'
};
config.copy.archiveMoment = {
src: 'lib/moment/min/moment.min.js',
dest: 'build/archive/lib/moment.min.js'
};
config.copy.archiveJQuery = {
src: 'lib/jquery/jquery.min.js',
dest: 'build/archive/lib/jquery.min.js'
@@ -129,7 +177,7 @@ module.exports = function(grunt) {
return content;
}
},
src: 'demos/*',
src: 'demos/**',
dest: 'build/archive/'
};
@@ -138,14 +186,15 @@ module.exports = function(grunt) {
expand: true,
cwd: 'lib/jquery-ui/themes/cupertino/',
src: [ 'jquery-ui.min.css', 'images/*' ],
dest: 'build/archive/demos/cupertino/'
dest: 'build/archive/lib/cupertino/'
};
// in demo HTML, rewrites paths to work in the archive
function transformDemoPath(path) {
path = path.replace('../lib/moment/moment.js', '../lib/moment.min.js');
path = path.replace('../lib/jquery/jquery.js', '../lib/jquery.min.js');
path = path.replace('../lib/jquery-ui/ui/jquery-ui.js', '../lib/jquery-ui.custom.min.js');
path = path.replace('../lib/jquery-ui/themes/', '');
path = path.replace('../lib/jquery-ui/themes/cupertino/', '../lib/cupertino/');
path = path.replace('../build/out/', '../fullcalendar/');
path = path.replace('/fullcalendar.js', '/fullcalendar.min.js');
return path;
@@ -153,8 +202,10 @@ module.exports = function(grunt) {
// copy license and changelog
config.copy.archiveMisc = {
src: [ 'license.txt', 'changelog.txt' ],
dest: 'build/archive/'
files: {
'build/archive/license.txt': 'license.txt',
'build/archive/changelog.txt': 'changelog.md'
}
};
// create the ZIP
@@ -177,7 +228,6 @@ module.exports = function(grunt) {
----------------------------------------------------------------------------------------------------*/
grunt.registerTask('bower', 'Build the FullCalendar Bower component', [
'clean:modules',
'clean:bower',
'modules',
'copy:bowerModules',
@@ -220,7 +270,6 @@ module.exports = function(grunt) {
----------------------------------------------------------------------------------------------------*/
grunt.registerTask('cdnjs', 'Build files for CDNJS\'s hosted version of FullCalendar', [
'clean:modules',
'clean:cdnjs',
'modules',
'copy:cdnjsModules',
@@ -253,4 +302,7 @@ module.exports = function(grunt) {
// finally, give grunt the config object...
grunt.initConfig(config);
grunt.loadTasks('tasks');
};
+209
View File
@@ -0,0 +1,209 @@
module.exports = function(grunt) {
var config = grunt.config('generateLanguages');
grunt.registerTask('generateLanguages', function() {
var combinedJS = '';
var languageCnt = 0;
var skippedLangCodes = [];
grunt.file.mkdir(config.dest, 0755);
grunt.file.expand(config.moment + '/*.js').forEach(function(momentPath) {
var langCode = momentPath.match(/([^\/]*)\.js$/)[1];
var js = getLangJS(langCode, momentPath);
if (js) {
grunt.file.write(
config.dest + '/' + langCode + '.js',
wrapWithUMD(js)
);
combinedJS += wrapWithClosure(js) + '\n';
languageCnt++;
}
else {
skippedLangCodes.push(langCode);
}
});
// code for resetting the language back to English
combinedJS += '\nmoment.lang("en");';
combinedJS += '\n$.fullCalendar.lang("en");';
combinedJS += '\nif ($.datepicker) $.datepicker.setDefaults($.datepicker.regional[""]);';
grunt.file.write(
config.dest + '/all.js',
wrapWithUMD(combinedJS)
);
grunt.log.writeln(skippedLangCodes.length + ' skipped languages: ' + skippedLangCodes.join(', '));
grunt.log.writeln(languageCnt + ' generated languages.');
});
function getLangJS(langCode, momentPath) {
var shortLangCode;
var momentLangJS;
var datepickerLangJS;
var fullCalendarLangJS;
// given "fr-ca", get just "fr"
if (langCode.indexOf('-') != -1) {
shortLangCode = langCode.replace(/-.*/, '');
}
momentLangJS = getMomentLangJS(momentPath);
datepickerLangJS = getDatepickerLangJS(langCode);
if (!datepickerLangJS && shortLangCode) {
datepickerLangJS = getDatepickerLangJS(shortLangCode, langCode);
}
fullCalendarLangJS = getFullCalendarLangJS(langCode);
if (!fullCalendarLangJS && shortLangCode) {
fullCalendarLangJS = getFullCalendarLangJS(shortLangCode, langCode);
}
// If this is an "en" language, only the Moment config is needed.
// For all other languages, all 3 configs are needed.
if (momentLangJS && (shortLangCode == 'en' || (datepickerLangJS && fullCalendarLangJS))) {
// if there is no definition, we still need to tell FC to set the default
if (!fullCalendarLangJS) {
fullCalendarLangJS = '$.fullCalendar.lang("' + langCode + '");';
}
datepickerLangJS = datepickerLangJS || '';
return momentLangJS + '\n' +
datepickerLangJS + '\n' +
fullCalendarLangJS;
}
}
function wrapWithUMD(body) {
return [
'(function(factory) {',
' if (typeof define === "function" && define.amd) {',
' define([ "jquery", "moment" ], factory);',
' }',
' else {',
' factory(jQuery, moment);',
' }',
'})(function($, moment) {',
'',
body,
'',
'});'
].join('\n');
}
function wrapWithClosure(body) {
return [
'(function() {',
'',
body,
'',
'})();'
].join('\n');
}
function getMomentLangJS(path) { // file assumed to exist
var js = grunt.file.read(path);
js = js.replace( // remove the UMD wrap
/\(\s*function[\S\s]*?function\s*\(\s*moment\s*\)\s*\{([\S\s]*)\}\)\);?/,
function(m0, body) {
body = body.replace(/^ /mg, ''); // remove 1 level of indentation
return body;
}
);
js = js.replace( // replace the `return` statement so execution continues
/^(\s*)return moment\.lang\(/m,
'$1moment.lang('
);
return js;
}
function getDatepickerLangJS(langCode, targetLangCode) {
// convert "en-ca" to "en-CA"
var datepickerLangCode = langCode.replace(/\-(\w+)/, function(m0, m1) {
return '-' + m1.toUpperCase();
});
var path = config.datepicker + '/jquery.ui.datepicker-' + datepickerLangCode + '.js';
var js;
try {
js = grunt.file.read(path);
}
catch (ex) {
return false;
}
js = js.replace(
/^jQuery\([\S\s]*?\{([\S\s]*)\}\);?/m, // inside the jQuery(function) wrap,
function(m0, body) { // use only the function body, modified.
var match = body.match(/\$\.datepicker\.regional[\S\s]*?(\{[\S\s]*?\});?/);
var props = match[1];
// remove 1 level of tab indentation
props = props.replace(/^\t/mg, '');
return "$.fullCalendar.datepickerLang(" +
"'" + (targetLangCode || langCode) + "', " + // for FullCalendar
"'" + datepickerLangCode + "', " + // for datepicker
props +
");";
}
);
return js;
}
function getFullCalendarLangJS(langCode, targetLangCode) {
var path = config.fullCalendar + '/' + langCode + '.js';
var js;
try {
js = grunt.file.read(path);
}
catch (ex) {
return false;
}
// if we originally wanted "ar-ma", but only "ar" is available, we have to adjust
// the declaration
if (targetLangCode && targetLangCode != langCode) {
js = js.replace(
/\$\.fullCalendar\.lang\(['"]([^'"]*)['"]/,
'$.fullCalendar.lang("' + targetLangCode + '"'
);
}
return js;
}
};