new lumbar-based solution for building fullcalendar submodules

This commit is contained in:
Adam Shaw
2013-03-16 23:51:09 -07:00
parent 5220310084
commit 95c16e274f
7 changed files with 187 additions and 483 deletions
+1
View File
@@ -1,4 +1,5 @@
build/out
build/archive
build/component
dist
+108 -113
View File
@@ -1,9 +1,7 @@
var _ = require('underscore');
module.exports = function(grunt) {
var _ = require('underscore');
// Load required NPM tasks.
// You must first run `npm install` in the project's root directory to get these dependencies.
@@ -12,197 +10,194 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('lumbar');
var fileIndex = require('./files.js'); // lists of source/dependency files
var loaderUtils = require('./build/loader.js');
// read config files, and combine into one "meta" object
// Parse config files
var lumbarConfig = grunt.file.readJSON('lumbar.json');
var packageConfig = grunt.file.readJSON('package.json');
var componentConfig = grunt.file.readJSON('component.json');
var pluginConfig = grunt.file.readJSON('fullcalendar.jquery.json');
var meta = _.extend({}, packageConfig, componentConfig, pluginConfig);
// this will eventually get passed to grunt.initConfig
// This will eventually get passed to grunt.initConfig()
// Initialize multitasks...
var config = {
meta: meta, // do this primarily for templating (<%= %>)
concat: {}, // initialize multitasks...
concat: {},
uglify: {},
copy: {},
compress: {},
clean: {}
};
// Combine configs for the "meta" template variable (<%= meta.whatever %>)
config.meta = _.extend({}, packageConfig, componentConfig, pluginConfig);
/* Important Top-Level Tasks
// The "grunt" command with no arguments
grunt.registerTask('default', 'archive');
/* FullCalendar Modules
----------------------------------------------------------------------------------------------------*/
grunt.registerTask('default', 'dist'); // what will be run with a plain old "grunt" command
grunt.registerTask('dist', 'Create a distributable ZIP file', [
'clean:build',
'concat',
'uglify',
'copy:dependencies',
'copy:demos',
'copy:misc',
'compress'
grunt.registerTask('modules', 'Build the FullCalendar modules', [
'lumbar:build',
'concat:moduleVariables',
'uglify:modules'
]);
/* Concatenate Submodules
----------------------------------------------------------------------------------------------------*/
_.each(fileIndex.fullcalendar, function(submodule, name) {
if (submodule.js) {
config.concat[name + '-js'] = {
options: {
process: true // replace template variables
},
src: submodule.js,
dest: 'build/out/fullcalendar/' + name + '.js'
};
// assemble modules
config.lumbar = {
build: {
build: 'lumbar.json',
output: 'build/out' // a directory. lumbar doesn't like trailing slash
}
};
if (submodule.css) {
config.concat[name + '-css'] = {
options: {
process: true // replace template variables
},
src: submodule.css,
dest: 'build/out/fullcalendar/' + name + '.css'
};
}
// replace template variables (<%= %>), IN PLACE
config.concat.moduleVariables = {
options: {
process: true // replace
},
expand: true,
cwd: 'build/out/',
src: [ '*.js', '*.css', '!jquery*' ],
dest: 'build/out/'
};
if (submodule.printCss) {
config.concat[name + '-print-css'] = {
options: {
process: true // replace template variables
},
src: submodule.printCss,
dest: 'build/out/fullcalendar/' + name + '.print.css'
};
}
});
/* Minify the JavaScript
----------------------------------------------------------------------------------------------------*/
config.uglify.all = {
// create minified versions (*.min.js)
config.uglify.modules = {
options: {
preserveComments: 'some' // keep comments starting with /*!
},
expand: true,
src: 'build/out/fullcalendar/*.js',
src: 'build/out/fullcalendar.js', // only do it for fullcalendar.js
ext: '.min.js'
}
config.clean.modules = 'build/out/*';
/* Copy Dependencies
/* Archive
----------------------------------------------------------------------------------------------------*/
config.copy.dependencies = {
grunt.registerTask('archive', 'Create a distributable ZIP archive', [
'clean:modules',
'clean:archive',
'modules',
'copy:archiveModules',
'copy:archiveDependencies',
'copy:archiveDemos',
'copy:archiveMisc',
'compress:archive'
]);
// copy FullCalendar modules into ./fullcalendar/ directory
config.copy.archiveModules = {
expand: true,
cwd: 'build/out/',
src: [ '*.js', '*.css', '!jquery*' ],
dest: 'build/archive/fullcalendar/'
};
// copy jQuery and jQuery UI into the ./jquery/ directory
config.copy.archiveDependencies = {
expand: true,
flatten: true,
src: [
fileIndex['jquery'].js,
fileIndex['jquery-ui'].js
// we want to retain the original filenames
lumbarConfig.modules['jquery'].scripts[0],
lumbarConfig.modules['jquery-ui'].scripts[0]
],
dest: 'build/out/jquery/'
dest: 'build/archive/jquery/'
};
/* Demos
----------------------------------------------------------------------------------------------------*/
config.copy.demos = {
// copy demo files into ./demos/ directory
config.copy.archiveDemos = {
options: {
// while copying demo files over, replace loader.js <script> with actual tags
processContentExclude: 'demos/*/**', // don't process anything more than 1 level deep (like assets)
processContent: function(content) {
content = content.replace(
/<script[^>]*loader\.js[^>]*?(?:data-modules=['"](.*?)['"])?><\/script>/i, // match loader.js tag and modules param
function(wholeMatch, moduleString) {
return loaderUtils.buildTags('..', fileIndex, moduleString, 'dist');
}
);
content = content.replace(/((?:src|href)=['"])([^'"]*)(['"])/g, function(m0, m1, m2, m3) {
return m1 + transformDemoPath(m2) + m3;
});
return content;
}
},
src: 'demos/**',
dest: 'build/out/'
dest: 'build/archive/'
};
// in demo HTML, rewrites paths to work in the archive
function transformDemoPath(path) {
path = path.replace('/build/out/jquery.js', '/' + lumbarConfig.modules['jquery'].scripts[0]);
path = path.replace('/build/out/jquery-ui.js', '/' + lumbarConfig.modules['jquery-ui'].scripts[0]);
path = path.replace('/lib/', '/jquery/');
path = path.replace('/build/out/', '/fullcalendar/');
path = path.replace('/fullcalendar.js', '/fullcalendar.min.js');
return path;
}
/* Copy Misc Files
----------------------------------------------------------------------------------------------------*/
config.copy.misc = {
src: "*.txt", // license and changelog
dest: 'build/out/'
// copy license and changelog
config.copy.archiveMisc = {
src: "*.txt",
dest: 'build/archive/'
};
/* Create ZIP file
----------------------------------------------------------------------------------------------------*/
config.compress.all = {
// create the ZIP
config.compress.archive = {
options: {
archive: 'dist/<%= meta.name %>-<%= meta.version %>.zip'
},
expand: true,
cwd: 'build/out/',
cwd: 'build/archive/',
src: '**',
dest: '<%= meta.name %>-<%= meta.version %>/' // have a top-level directory in the ZIP file
};
config.clean.archive = 'build/archive/*';
config.clean.dist = 'dist/*';
/* Bower Component
/* Bower Component (http://twitter.github.com/bower/)
----------------------------------------------------------------------------------------------------*/
// http://twitter.github.com/bower/
grunt.registerTask('component', 'Build the FullCalendar component for the Bower package manager', [
'clean:modules',
'clean:component',
'concat',
'uglify', // we want the minified JS in there
'copy:component',
'copy:component-readme',
'component.json'
'modules',
'copy:componentModules',
'copy:componentReadme',
'componentConfig'
]);
config.copy.component = {
// copy FullCalendar modules into component root
config.copy.componentModules = {
expand: true,
cwd: 'build/out/fullcalendar/',
src: '**',
cwd: 'build/out/',
src: [ '*.js', '*.css', '!jquery*' ],
dest: 'build/component/',
};
config.copy['component-readme'] = {
// copy the component-specific README
config.copy.componentReadme = {
src: 'build/component-readme.md',
dest: 'build/component/readme.md'
};
grunt.registerTask('component.json', function() {
// assemble the component's config from existing configs
grunt.registerTask('componentConfig', function() {
grunt.file.write(
'build/component/component.json',
JSON.stringify(
_.extend({}, pluginConfig, componentConfig), // combine the 2 configs
_.extend({}, pluginConfig, componentConfig), // combine 2 configs
null, // replacer
2 // indent
)
);
});
/* Clean Up Files
----------------------------------------------------------------------------------------------------*/
config.clean.build = 'build/out/*';
config.clean.component = 'build/component/*';
config.clean.dist = 'dist/*';
-312
View File
@@ -1,312 +0,0 @@
(function() {
/*
* Modules that will be implicitly included
*/
var DEFAULT_MODULES = [ 'jquery', 'jquery-ui', 'fullcalendar'/*the submodule*/ ];
/*
* Generate HTML script/link tags for loading resources.
*
* @param {String} root
* Path to the project or distributable's root directory from the HTML file running this script.
*
* @param {Object} fileIndex
* File location information for each module/submodule (everything in files.js).
*
* @param {String} moduleString
* A comma-separated string with names of additional modules to load.
*
* @param {string} [buildType]
* unspecified - use source JS/CSS files
* "concat" - use concatenated JS/CSS files
* "min" - use minified JS files, concatenated CSS files
* "dist" - use minified JS files, concatenated CSS files, and write paths for distributable
*/
function buildTags(root, fileIndex, moduleString, buildType) {
var extraModuleNames = moduleString ? moduleString.split(',') : [];
var moduleNames = DEFAULT_MODULES.concat(extraModuleNames);
var jsPaths = [];
var cssPaths = [];
var printCssPaths = [];
var buildRoot;
if (buildType == 'dist') {
buildRoot = root; // in the distributable, the build root IS the root
}
else {
buildRoot = root + '/build/out';
}
each(moduleNames, function(moduleName) {
var submodule = fileIndex.fullcalendar[moduleName];
var module = submodule || fileIndex[moduleName];
if (module) {
var js = arrayify(module.js);
var css = arrayify(module.css);
var printCss = arrayify(module.printCss);
if (buildType && submodule) {
//
// paths for a "built" fullcalendar submodule
//
if (js.length) {
jsPaths.push(buildRoot + '/fullcalendar/' + moduleName + (buildType == 'concat' ? '' : '.min') + '.js');
}
if (css.length) {
cssPaths.push(buildRoot + '/fullcalendar/' + moduleName + '.css');
}
if (printCss.length) {
printCssPaths.push(buildRoot + '/fullcalendar/' + moduleName + '.print.css');
}
}
else if (buildType == 'dist' && !submodule) {
//
// 3rd-party paths in the distributable (they all go in jquery/ for now)
//
each(js, function(path) {
jsPaths.push(buildRoot + '/jquery/' + basename(path));
});
each(css, function(path) {
cssPaths.push(buildRoot + '/jquery/' + basename(path));
});
each(printCss, function(path) {
printCssPaths.push(buildRoot + '/jquery/' + basename(path));
});
}
else {
//
// paths for development
//
each(js, function(path) {
if (!/(intro|outro)\.js$/.test(path)) { // don't write syntactically incorrect intro.js/outro.js
jsPaths.push(root + '/' + path);
}
});
each(css, function(path) {
cssPaths.push(root + '/' + path);
});
each(printCss, function(path) {
printCssPaths.push(root + '/' + path);
});
}
}
});
return [].concat(
map(cssPaths, buildCssTag),
map(printCssPaths, buildPrintCssTag),
map(jsPaths, buildJsTag)
)
.join('\n');
}
/* When run in a browser...
====================================================================================================*/
/*
* Writes script/link tags for resources.
* Attributes can be added to loader.js's script tag to change its behavior:
* data-modules="..." (a comma-separated list of additional modules to load)
* data-debug="true"
*/
function run() {
var thisScript = getLastScript();
var src = thisScript.getAttribute('src');
var cwd = dirname(src);
var projectRoot = dirname(cwd);
var moduleString = thisScript.getAttribute('data-modules');
var debugEnabled = !!thisScript.getAttribute('data-debug');
var buildType = getQueryStringVar('build');
if (debugEnabled) {
buildType = buildType || getCookieVar('build'); // fall back to cookie
initDebug(buildType);
}
loadScript(projectRoot + '/files.js', function(fileIndex) {
document.write(
buildTags(projectRoot, fileIndex, moduleString, buildType)
);
});
}
/*
* Installs a build-switching <select> box at top left of screen.
*/
function initDebug(currentBuildType) {
window.onload = function() {
var form = $(
"<form type='GET' style='position:absolute;top:5px;left:5px'>" +
"<select name='build'>" +
"<option value=''>src</option>" +
"<option value='concat'>concat</option>" +
"<option value='min'>min</option>" +
"</select>" +
"</form>"
);
var select = form.find('select')
.on('change', function() {
var val = select.val();
if (!val) {
select.remove(); // erases from resulting query string
}
document.cookie = 'build=' + val; // save cookie
form.submit();
})
.val(currentBuildType || '');
$('body').append(form);
};
}
/* HTML-building Utilities
====================================================================================================*/
function buildJsTag(path) {
return "<script src='" + path + "'></script>"
}
function buildCssTag(path) {
return "<link href='" + path + "' rel='stylesheet' />";
}
function buildPrintCssTag(path) {
return "<link href='" + path + "' rel='stylesheet' media='print' />";
}
/* DOM Utilities
====================================================================================================*/
/*
* Loads an external JS file in the global scope, and calls `callback` when done.
* If the JS file is a CommonJS module, the module's "exports" will be given to the callback.
*/
function loadScript(path, callback) {
var funcName = ('_scriptCallback' + Math.random()).replace('.', '');
document.write("<script>exports = {}; module = { exports: exports }</script>");
document.write("<script src='" + path + "'></script>");
document.write("<script>" + funcName + "()</script>");
window[funcName] = function() {
removeLastScript();
removeLastScript();
removeLastScript();
var exports = module.exports;
delete window.module;
delete window.exports;
delete window[funcName];
if (callback) {
callback(exports);
}
};
}
function removeLastScript() {
var script = getLastScript();
script.parentNode.removeChild(script);
}
function getLastScript() {
var scripts = document.getElementsByTagName('script');
return scripts[scripts.length - 1];
}
function getQueryStringVar(name) {
var qs = extractQueryString(window.location.href);
var match = new RegExp(name + '=([^&]*)').exec(qs);
if (match) {
return match[1];
}
}
function getCookieVar(name) {
var match = new RegExp(name + '=([^;]*)').exec(document.cookie);
if (match) {
return match[1];
}
}
/* File Path Utilities
====================================================================================================*/
function basename(path) {
var match = /.*\/(.*)/.exec(path);
return match ? match[1] : path;
}
function dirname(path) {
var match = /(.*)\//.exec(path);
return match ? match[1] : '.';
}
function extractQueryString(url) {
var match = /\?(.*)/.exec(url);
return match ? match[1] : '';
}
/* Language Utilities
====================================================================================================*/
function each(a, f) {
for (var i=0; i<a.length; i++) {
f(a[i], i);
}
}
function map(a, f) {
var res = [];
for (var i=0; i<a.length; i++) {
res.push(
f(a[i], i)
);
}
return res;
}
function arrayify(a) {
return [].concat(a || []);
}
/* RUN!
====================================================================================================*/
if (typeof exports != 'undefined') {
exports.buildTags = buildTags; // if we are running in Node, provide the buildTags() utility
}
else {
run(); // we are running in a web browser
}
})();
Executable
+1
View File
@@ -0,0 +1 @@
"`dirname $0`/../node_modules/lumbar/bin/lumbar" watch "$@" "`dirname $0`/out"
-57
View File
@@ -1,57 +0,0 @@
module.exports = {
jquery: {
js: 'lib/jquery-1.9.1.min.js'
},
'jquery-ui': {
js: 'lib/jquery-ui-1.10.1.custom.min.js'
},
fullcalendar: {
fullcalendar: {
js: [
'src/intro.js',
'src/defaults.js',
'src/main.js',
'src/Calendar.js',
'src/Header.js',
'src/EventManager.js',
'src/date_util.js',
'src/util.js',
'src/basic/MonthView.js',
'src/basic/BasicWeekView.js',
'src/basic/BasicDayView.js',
'src/basic/BasicView.js',
'src/basic/BasicEventRenderer.js',
'src/agenda/AgendaWeekView.js',
'src/agenda/AgendaDayView.js',
'src/agenda/AgendaView.js',
'src/agenda/AgendaEventRenderer.js',
'src/common/View.js',
'src/common/DayEventRenderer.js',
'src/common/SelectionManager.js',
'src/common/OverlayManager.js',
'src/common/CoordinateGrid.js',
'src/common/HoverListener.js',
'src/common/HorizontalPositionCache.js',
'src/outro.js'
],
css: [
'src/main.css',
'src/common/common.css',
'src/basic/basic.css',
'src/agenda/agenda.css'
],
printCss: 'src/common/print.css'
},
gcal: {
js: 'src/gcal/gcal.js'
}
}
};
+75
View File
@@ -0,0 +1,75 @@
{
"scope": "none",
"packages": {
"dependencies": {
"modules": [
"jquery",
"jquery-ui"
]
},
"fullcalendar": {
"modules": [
"fullcalendar",
"fullcalendar.print",
"gcal"
]
}
},
"modules": {
"jquery": {
"scripts": [
"lib/jquery-1.9.1.min.js"
]
},
"jquery-ui": {
"scripts": [
"lib/jquery-ui-1.10.1.custom.min.js"
]
},
"fullcalendar": {
"scripts": [
"src/intro.js",
"src/defaults.js",
"src/main.js",
"src/Calendar.js",
"src/Header.js",
"src/EventManager.js",
"src/date_util.js",
"src/util.js",
"src/basic/MonthView.js",
"src/basic/BasicWeekView.js",
"src/basic/BasicDayView.js",
"src/basic/BasicView.js",
"src/basic/BasicEventRenderer.js",
"src/agenda/AgendaWeekView.js",
"src/agenda/AgendaDayView.js",
"src/agenda/AgendaView.js",
"src/agenda/AgendaEventRenderer.js",
"src/common/View.js",
"src/common/DayEventRenderer.js",
"src/common/SelectionManager.js",
"src/common/OverlayManager.js",
"src/common/CoordinateGrid.js",
"src/common/HoverListener.js",
"src/common/HorizontalPositionCache.js",
"src/outro.js"
],
"styles": [
"src/main.css",
"src/common/common.css",
"src/basic/basic.css",
"src/agenda/agenda.css"
]
},
"fullcalendar.print": {
"styles": [
"src/common/print.css"
]
},
"gcal": {
"scripts": [
"src/gcal/gcal.js"
]
}
}
}
+2 -1
View File
@@ -6,6 +6,7 @@
"grunt-contrib-uglify": "~0.1.1",
"grunt-contrib-copy": "~0.4.0",
"grunt-contrib-compress": "~0.4.0",
"grunt-contrib-clean": "~0.4.0"
"grunt-contrib-clean": "~0.4.0",
"lumbar": "~2.0.0-beta19"
}
}