From 26f5f710ccebdafd76ba044269aa752999250db6 Mon Sep 17 00:00:00 2001 From: Jacob Thornton Date: Tue, 27 Mar 2012 17:39:51 -0400 Subject: Initial Import Signed-off-by: Chris Aniszczyk --- LICENSE | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procfile | 1 + README.md | 43 ++++++++++++++ index.js | 78 +++++++++++++++++++++++++ lib/css.js | 145 ++++++++++++++++++++++++++++++++++++++++++++++ lib/img.js | 80 ++++++++++++++++++++++++++ lib/js.js | 115 +++++++++++++++++++++++++++++++++++++ package.json | 22 +++++++ tests/index.js | 97 +++++++++++++++++++++++++++++++ 9 files changed, 758 insertions(+) create mode 100644 LICENSE create mode 100644 Procfile create mode 100644 README.md create mode 100644 index.js create mode 100644 lib/css.js create mode 100644 lib/img.js create mode 100644 lib/js.js create mode 100644 package.json create mode 100644 tests/index.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4947287 --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..5ec9cc2 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: node index.js \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5684d42 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +[Twitter Bootstrap](http://twitter.github.com/bootstrap) - Server +================= + +The twitter bootstrap server is responsible for backing the custom builds from bootstrap's download page. It serves zip files containing custom built and minified css and js. + +This needs a lot of work. ;) #GSOC + + +Quick start +----------- + +install dependencies +``` +$ npm install +``` + +start server +``` +$ node . +``` + +run tests +``` +$ node tests +``` + + +Copyright and license +--------------------- + +Copyright 2012 Twitter, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this work except in compliance with the License. +You may obtain a copy of the License in the LICENSE file, or at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..57632d3 --- /dev/null +++ b/index.js @@ -0,0 +1,78 @@ +// ======================================================================== +// bootstrap-server v1.0.0 +// http://twitter.github.com/bootstrap +// ======================================================================== +// Copyright 2012 Twitter, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + + +"use strict" + +var express = require('express') + , zip = require('node-native-zip') + , app = express.createServer() + , types = {} + +types.img = require('./lib/img') +types.js = require('./lib/js') +types.css = require('./lib/css') + +app.use(express.bodyParser()); + +function refreshCache() { + Object.keys(types).forEach(function (type) { + types[type].cache() + }) +} + +refreshCache() +setInterval(refreshCache, 1000 * 60 * 60 * 2) + + +// API args: +// + js = array +// + css = array +// + img = array +// + vars = obj + +app.get('/', function (req, res) { + res.send('Bootstrap Server - w/cache. <3'); +}) + +app.post('/', function(req, res) { + var dist = [] + , started = 0 + , params = {} + , archive = new zip() + + params.js = req.body.js && JSON.parse(req.body.js) + params.css = req.body.css && JSON.parse(req.body.css) + params.img = req.body.img && JSON.parse(req.body.img) + params.vars = req.body.vars && JSON.parse(req.body.vars) + + Object.keys(types).forEach(function (type) { + if (!params[type] || !params[type].length) return + types[type](params, complete) + }) + + function complete (err, files) { + for (var i in files) archive.add(i, files[i]) + } + + res.attachment('bootstrap.zip') + res.send(archive.toBuffer()) +}) + +app.listen(process.env.PORT || 3000) diff --git a/lib/css.js b/lib/css.js new file mode 100644 index 0000000..4a353be --- /dev/null +++ b/lib/css.js @@ -0,0 +1,145 @@ +// ======================================================================== +// bootstrap-server v1.0.0 +// http://twitter.github.com/bootstrap +// ======================================================================== +// Copyright 2012 Twitter, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +"use strict" + +var path = require('path') + , https = require('https') + , less = require('less') + , CACHE = {} + , CW = "/*!\n * Bootstrap v2.0.2\n *\n * Copyright 2012 Twitter, Inc\n * Licensed under the Apache License v2.0\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Designed and built with all the love in the world @twitter by @mdo and @fat.\n */\n" + , ERROR = "A less error occured trying to build your bundle. Please paste the error below in an issue for us at http://github.com/twitter/bootsrap! thanks!\n\n" + , FILES = [ "variables.less" + , "mixins.less" + , "reset.less" + , "scaffolding.less" + , "grid.less" + , "layouts.less" + , "type.less" + , "code.less" + , "labels.less" + , "badges.less" + , "tables.less" + , "forms.less" + , "buttons.less" + , "sprites.less" + , "button-groups.less" + , "navs.less" + , "navbar.less" + , "breadcrumbs.less" + , "pagination.less" + , "pager.less" + , "thumbnails.less" + , "alerts.less" + , "progress-bars.less" + , "hero-unit.less" + , "tooltip.less" + , "popovers.less" + , "modals.less" + , "dropdowns.less" + , "accordion.less" + , "carousel.less" + , "wells.less" + , "close.less" + , "utilities.less" + , "component-animations.less" + , "responsive.less" ] + +function cache(callback) { + var complete = 0 + , _cache = {} + + FILES.forEach(function (filename) { + var req + , res + , content = [] + , options = { + host: 'raw.github.com' + , port: 443 + , path: path.join('/twitter/bootstrap/master/less/', filename) + , method: 'GET' + } + + req = https.request(options, function(res) { + + res.setEncoding('utf8') + + res.on('data', function (chunk) { + content.push(chunk) + }) + + res.on('end', function () { + + _cache[filename] = content.join(' ') + + if (++complete == FILES.length) { + CACHE = _cache + callback && callback(null, CACHE) + } + + }) + }) + + req.end() + }) +} + +function css(params, callback) { + + var result = '' + + result += CACHE['variables.less'] + result += generateCustomCSS(params) + result += CACHE['mixins.less'] + + params.css.forEach(function (filename) { + result += CACHE[filename] + }) + + result = result.replace(/@import[^\n]*/gi, '') //strip any imports + + try { + var parser = new less.Parser({ + paths: ['variables.less', 'mixins.less'] + , optimization: 0 + , filename: 'bootstrap.css' + }).parse(result, function (err, tree) { + if (err) callback(null, {'css/error.txt': new Buffer(ERROR + '\n' + JSON.stringify(err), 'utf8')}) + callback(null, { + 'css/bootstrap.css' : new Buffer(CW + tree.toCSS(), 'utf8') + , 'css/bootstrap.min.css': new Buffer(CW + tree.toCSS({ compress: true }), 'utf8') + }) + }) + } catch (err) { + callback(null, {'css/error.txt': new Buffer(ERROR + '\n' + JSON.stringify(err), 'utf8')}) + } + +} + +function generateCustomCSS (params) { + return params.vars ? Object.keys(params.vars) + .map(function (key) { + return params.vars[key] ? (key + ': ' + params.vars[key] + ';') : '' + }) + .join('\n') + '\n\n' : '' +} + +module.exports = css +module.exports.cache = cache +module.exports.FILES = FILES diff --git a/lib/img.js b/lib/img.js new file mode 100644 index 0000000..fe1a606 --- /dev/null +++ b/lib/img.js @@ -0,0 +1,80 @@ +// ======================================================================== +// bootstrap-server v1.0.0 +// http://twitter.github.com/bootstrap +// ======================================================================== +// Copyright 2012 Twitter, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +"use strict" + +var path = require('path') + , https = require('https') + , CACHE = {} + , FILES = [ 'glyphicons-halflings.png' + , 'glyphicons-halflings-white.png' ] + +function cache(callback) { + var _cache = {} + , complete = 0 + + FILES.forEach(function (filename) { + var req + , res + , i = 0 + , content = [] + , options = { + host: 'raw.github.com' + , port: 443 + , path: path.join('/twitter/bootstrap/master/img/', filename) + , method: 'GET' + } + + req = https.request(options, function(res) { + + var buffer = new Buffer(parseInt(res.header('Content-Length'))) + + res.setEncoding('binary') + + res.on('data', function (chunk) { + buffer.write(chunk, i, 'binary') + i += chunk.length + }) + + res.on('end', function () { + _cache[filename] = buffer + if (++complete == FILES.length) { + CACHE = _cache + callback && callback(null, CACHE) + } + }) + + }) + + req.end() + }) + +} + +function img(params, callback) { + var contents = {} + params.img.forEach(function (filename) { + contents['img/' + filename] = CACHE[filename] + }) + callback(null, contents) +} + +module.exports = img +module.exports.cache = cache +module.exports.FILES = FILES diff --git a/lib/js.js b/lib/js.js new file mode 100644 index 0000000..b9c53b7 --- /dev/null +++ b/lib/js.js @@ -0,0 +1,115 @@ +// ======================================================================== +// bootstrap-server v1.0.0 +// http://twitter.github.com/bootstrap +// ======================================================================== +// Copyright 2012 Twitter, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +"use strict" + +var uglifyJS = require('uglify-js') + , path = require('path') + , https = require('https') + , CACHE = {} + , FILES = [ "bootstrap-transition.js" + , "bootstrap-modal.js" + , "bootstrap-dropdown.js" + , "bootstrap-scrollspy.js" + , "bootstrap-tab.js" + , "bootstrap-tooltip.js" + , "bootstrap-popover.js" + , "bootstrap-alert.js" + , "bootstrap-button.js" + , "bootstrap-collapse.js" + , "bootstrap-carousel.js" + , "bootstrap-typeahead.js" ] + +function cache() { + + var done = 0 + , _cache = {} + + FILES.forEach(function (filename) { + var req + , content = [] + , options = { + host: 'raw.github.com' + , port: 443 + , path: path.join('/twitter/bootstrap/master/js/', filename) + , method: 'GET' + } + + req = https.request(options, function(res) { + + res.setEncoding('utf8') + + res.on('data', function (chunk) { + content.push(chunk) + }) + + res.on('end', function () { + _cache[filename] = content.join('') + if (++done == FILES.length) { + CACHE = _cache + } + }) + }) + + req.end() + }) + +} + +function js(params, callback) { + var min, content = params.js.map(function (filename) { + return CACHE[filename] + }).join('\n') + + try { + min = uglify(content, params.js) + } catch (e) { + min = 'Error minifying source - please open issue on http://github.com/twitter/bootstrap! thank you :)' + } + + callback(null, { + 'js/bootstrap.js' : new Buffer(content, 'utf8') + , 'js/bootstrap.min.js': new Buffer(min, 'utf8') + }) +} + +function uglify(input, names) { + var content = input.replace(/[\"\']use strict[\"\']/gi, '') + , tok = uglifyJS.parser.tokenizer(content) + , c = tok() + , result + , ast + + result = '/**\n' + + '* Bootstrap.js by @fat & @mdo\n' + + '* plugins: ' + names.join(', ') + '\n' + + '* Copyright 2012 Twitter, Inc.\n' + + '* http://www.apache.org/licenses/LICENSE-2.0.txt\n' + + '*/\n' + + ast = uglifyJS.parser.parse(content) + ast = uglifyJS.uglify.ast_mangle(ast) + ast = uglifyJS.uglify.ast_squeeze(ast) + + return result += uglifyJS.uglify.gen_code(ast) +} + +module.exports = js +module.exports.cache = cache +module.exports.FILES = FILES diff --git a/package.json b/package.json new file mode 100644 index 0000000..f62c42a --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "bootstrap-server" + , "description": "a build tool for bootstrap" + , "version": "1.0.0" + , "engines": { "node": ">= 0.4.1" } + , "main": "web.js" + , "dependencies": { + "express": "2.5.5" + , "uglify-js": "1.2.4" + , "less": "1.3.0" + , "node-native-zip": "1.0.1" + , "author": "Twitter Inc." + , "repository": { + "type": "git" + , "url": "https://github.com/twitter/bootstrap-server.git" + } + , "licenses": [ + { "type": "Apache-2.0" + , "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + } +} diff --git a/tests/index.js b/tests/index.js new file mode 100644 index 0000000..f93b6de --- /dev/null +++ b/tests/index.js @@ -0,0 +1,97 @@ +// ======================================================================== +// bootstrap-server v0.1.0 - tests +// http://twitter.github.com/bootstrap +// ======================================================================== +// Copyright 2012 @fat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +var assert = require('assert') +var express = require('express') + +// CSS +// ======================================= + +var CSS = require('../lib/css') + +// basic api +assert.ok(typeof CSS == 'function') +assert.ok(typeof CSS.cache == 'function') +assert.ok(typeof CSS.FILES == 'object') + +// css cache method +CSS.cache(function (err, cache) { + assert(!err) + + CSS.FILES.forEach(function (filename) { + assert.ok(cache[filename]) + }) + + CSS({ css: ['reset.less'] }, function (err, response) { + assert(!err) + assert(response['css/bootstrap.css']) + assert(response['css/bootstrap.min.css']) + }) +}) + + +// IMG +// ======================================= + +var IMG = require('../lib/img') + +// basic api +assert.ok(typeof IMG == 'function') +assert.ok(typeof IMG.cache == 'function') +assert.ok(typeof IMG.FILES == 'object') + +// css cache method +IMG.cache(function (err, cache) { + assert(!err) + + IMG.FILES.forEach(function (filename) { + assert.ok(cache[filename]) + }) + + IMG({ img: ['glyphicons-halflings.png'] }, function (err, response) { + assert(!err) + assert(response['img/glyphicons-halflings.png']) + }) +}) + + +// JS +// ======================================= + +var JS = require('../lib/js') + +// basic api +assert.ok(typeof JS == 'function') +assert.ok(typeof JS.cache == 'function') +assert.ok(typeof JS.FILES == 'object') + +// css cache method +JS.cache(function (err, cache) { + assert(!err) + + JS.FILES.forEach(function (filename) { + assert.ok(cache[filename]) + }) + + JS({ js: ['bootstrap-transition.js'] }, function (err, response) { + assert(!err) + assert(response['js/bootstrap.js']) + assert(response['js/bootstrap.min.js']) + }) +}) \ No newline at end of file -- cgit v1.2.3