├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── assets_manager.coffee ├── bundle.coffee ├── bundle_up.coffee ├── compiler.coffee ├── css.coffee ├── default_compilers.coffee ├── helpers.coffee ├── js.coffee └── otf_compiler.coffee ├── package.json └── test ├── bundle_spec.coffee ├── file_adding_spec.coffee ├── files ├── assets.coffee ├── assets_namespaced.coffee ├── coffee │ ├── 1.coffee │ ├── 2.coffee │ ├── 3.coffee │ └── syntax_error.coffee ├── css │ └── screen.css ├── js │ ├── 1.js │ ├── 2.js │ └── 3.js ├── nested │ ├── css │ │ ├── 1.css │ │ ├── 1.styl │ │ └── sub │ │ │ ├── 2.css │ │ │ └── 2.styl │ └── js │ │ ├── 1.coffee │ │ ├── 1.js │ │ └── sub │ │ ├── 2.coffee │ │ ├── 2.js │ │ └── sub2 │ │ ├── 3.js │ │ └── 4.js ├── public │ ├── jquery.js │ ├── main.coffee │ ├── print.styl │ └── styles │ │ └── bootstrap.css └── stylus │ ├── main.styl │ ├── syntax_error.styl │ └── typography.styl ├── helper.coffee ├── namespaces_spec.coffee ├── otf_compiler_spec.coffee ├── path_normalizing_spec.coffee ├── view_helper_spec.coffee └── views ├── customJs.jade ├── globalAndCustomJs.jade ├── globalAndPrintCss.jade ├── globalCss.jade ├── globalJs.jade └── printCss.jade /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | test/files/public/generated 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.6 4 | - 0.8 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2011 Fredrik Lindin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Bundle up! [![Build Status](https://secure.travis-ci.org/Cowboy-coder/bundle-up.png)](https://travis-ci.org/Cowboy-coder/bundle-up) 2 | ========== 3 | 4 | Bundle up is a middleware for connect to manage all client-side assets in an organized way. 5 | 6 | Installation 7 | ------------ 8 | 9 | $ npm install bundle-up 10 | 11 | Usage 12 | ----- 13 | 14 | ``` js 15 | var BundleUp = require('bundle-up'); 16 | 17 | BundleUp(app, __dirname + '/assets', { 18 | staticRoot: __dirname + '/public/', 19 | staticUrlRoot:'/', 20 | bundle:true, 21 | minifyCss: true, 22 | minifyJs: true 23 | }); 24 | 25 | // To actually serve the files a static file 26 | // server needs to be added after Bundle Up 27 | app.use(express.static(__dirname + '/public/')) 28 | ``` 29 | 30 | The first parameter to the BundleUp middleware is the app object and the second is the path to the assets file. Through the assets file all client-side assets needs to get added. 31 | 32 | ``` js 33 | // assets.js 34 | module.exports = function(assets) { 35 | assets.root = __dirname; 36 | assets.addJs('/public/js/jquery-1.6.4.min.js'); 37 | assets.addJs('/public/js/jquery.placeholder.min.js'); 38 | assets.addJs('/app/client/main.coffee'); 39 | 40 | assets.addCss('/public/bootstrap/bootstrap.min.css'); 41 | assets.addCss('/app/styles/screen.styl'); 42 | } 43 | ``` 44 | 45 | Just point to a file (.js, .css, .coffee or .styl are currently supported) anywhere in your app directory. In your view you can then just render all the css or javascript files by calling `renderStyles` and `renderJs` like this: 46 | 47 | ``` jade 48 | !!! 49 | html 50 | head 51 | != renderStyles() 52 | body!= body 53 | != renderJs() 54 | ``` 55 | 56 | By default this will render 57 | 58 | ``` html 59 | 60 | 61 | 62 | 63 | 64 | 65 | ``` 66 | 67 | All assets will be compiled on-the-fly when `bundle:false` is set. Therefore the server never 68 | needs to be restarted when editing the different assets. 69 | 70 | To render bundles `bundle:true` needs to be passed as a parameter to the middleware. This will concatenate all javascript and css files into bundles and render this: 71 | 72 | ``` html 73 | 74 | 75 | ``` 76 | 77 | All bundles are generated during startup. The filename will change with the content so you should configure your web server with far future expiry headers. 78 | 79 | ### generated/ 80 | 81 | All files that needs to be compiled, copied (if you are bundling up a file that doesn't reside in your `public/` directory) or bundled will end up in `public/generated/` directory. This is to have an organized way to separate whats actually *your code* and whats *generated code*. 82 | 83 | ### Filtered paths 84 | 85 | All files can be added in a directory by using a "filtered path" like this 86 | 87 | ``` js 88 | // assets.js 89 | module.exports = function(assets) { 90 | assets.addJs(__dirname + '/public/js/**'); //adds all files in /public/js (subdirectories included) 91 | assets.addJs(__dirname + '/public/*.js'); //adds all js files in /public 92 | assets.addJs(__dirname + '/cs/**.coffee'); //adds all coffee files in /cs (subdirectories included) 93 | }); 94 | ``` 95 | ### Namespaces 96 | 97 | Sometimes all javascript or css files cannot be bundled into the same bundle. In that case 98 | namespaces can be used 99 | 100 | ``` js 101 | // assets.js 102 | module.exports = function(assets) { 103 | assets.addJs(__dirname + '/public/js/1.js'); 104 | assets.addJs(__dirname + '/public/js/2.js'); 105 | assets.addJs(__dirname + '/public/locales/en_US.js', 'en_US'); 106 | 107 | assets.addJs(__dirname + '/public/css/1.css'); 108 | assets.addJs(__dirname + '/public/css/2.css'); 109 | assets.addJs(__dirname + '/public/css/ie.css', 'ie'); 110 | }); 111 | ``` 112 | 113 | ``` jade 114 | !!! 115 | html 116 | head 117 | != renderStyles() 118 | != renderStyles('ie') 119 | body!= body 120 | != renderJs() 121 | != renderJs('en_US') 122 | ``` 123 | 124 | which will render this with `bundle:false`: 125 | 126 | ``` html 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | ``` 135 | 136 | and this with `bundle:true`: 137 | 138 | ``` html 139 | 140 | 141 | 142 | 143 | ``` 144 | 145 | ### Using CDN 146 | 147 | Using bundle-up with a CDN is pretty straight forward. In this example we´ll use a Cloud Front URL as the `staticUrlRoot`. 148 | 149 | ``` js 150 | BundleUp(app, __dirname + '/assets', { 151 | staticRoot: __dirname + '/public/', 152 | staticUrlRoot:'///drhu3hxlexxxx.cloudfront.net/', 153 | bundle:true, 154 | minifyCss: true, 155 | minifyJs: true 156 | }); 157 | ``` 158 | 159 | When using Express, you can easily have the development environment skip the CDN. 160 | 161 | ``` js 162 | var root = app.get('env') == "production" ? '///drhu3hxlexxxx.cloudfront.net/' : "/"; 163 | ... 164 | staticUrlRoot: root 165 | ``` 166 | 167 | License 168 | ------- 169 | 170 | MIT licensed 171 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('coffee-script'); 2 | module.exports = require(__dirname + '/lib/bundle_up'); 3 | -------------------------------------------------------------------------------- /lib/assets_manager.coffee: -------------------------------------------------------------------------------- 1 | class AssetsManager 2 | constructor: (@css, @js)-> 3 | @root = '' 4 | 5 | addCss: (file, namespace=undefined) => 6 | @css.addFile("#{@root}/#{file}", namespace) 7 | 8 | addJs: (file, namespace=undefined) => 9 | @js.addFile("#{@root}/#{file}", namespace) 10 | 11 | module.exports = AssetsManager 12 | -------------------------------------------------------------------------------- /lib/bundle.coffee: -------------------------------------------------------------------------------- 1 | crypto = require 'crypto' 2 | path = require 'path' 3 | compiler = require './compiler' 4 | fs = require 'fs' 5 | {writeToFile, normalizeUrl} = require './helpers' 6 | 7 | class Bundle 8 | constructor: (@options) -> 9 | @options.staticRoot = path.normalize(@options.staticRoot) 10 | @files = [] 11 | @defaultNamespace = 'global' 12 | 13 | # Gets relative path from staticRoot. If the 14 | # file passed in resides in another folder 15 | # the path returned is relative to the 16 | # root of the application 17 | _getRelativePath: (file) => 18 | relativePath = '' 19 | for char, i in file 20 | if @options.staticRoot[i] == file[i] 21 | continue 22 | else 23 | relativePath = file.substring(i) 24 | break 25 | return relativePath 26 | 27 | # 28 | # Determines based on file extension 29 | # if the file needs to get compiled 30 | # 31 | _needsCompiling: (file) -> 32 | fileExt = file.split('.') 33 | fileExt = fileExt[fileExt.length - 1] 34 | return fileExt != 'js' and fileExt != 'css' 35 | 36 | addFilesBasedOnFilter: (filterPath, namespace) -> 37 | directoryPath = filterPath.substring(0, filterPath.indexOf('*')) 38 | 39 | searchFiles = filterPath.substring(filterPath.indexOf('*.') + 1) 40 | searchFiles = undefined if searchFiles == filterPath 41 | searchNested = filterPath.indexOf('**') > -1 42 | 43 | 44 | foundFiles = [] 45 | directoryFind = (dir, retrying=false) -> 46 | try 47 | files = fs.readdirSync(dir) 48 | 49 | for file in files 50 | file = dir + '/' + file 51 | unless fs.lstatSync(file).isDirectory() 52 | if searchFiles 53 | if file.indexOf(searchFiles) > -1 54 | foundFiles.push file 55 | else 56 | foundFiles.push file 57 | 58 | else if searchNested 59 | directoryFind(file) 60 | catch err 61 | if err.code == 'ENOENT' 62 | unless retrying 63 | # We need to retry to see if it matches a directory 64 | # based on a earlier directory in the path. As an 65 | # example "/path/to/dir* should match /path/to/directory/ 66 | closestDir = dir.split('/') 67 | dir = closestDir.splice(0, closestDir.length-1).join('/') 68 | searchFiles = dir + '/' + closestDir.splice(closestDir.length-1).join('') 69 | searchNested = true 70 | directoryFind(dir, true) 71 | else 72 | # Found no files when retrying either... 73 | return 74 | else 75 | console.log err 76 | directoryFind(directoryPath) 77 | 78 | foundFiles = foundFiles.sort() 79 | for file in foundFiles 80 | @addFile(file, namespace) 81 | 82 | 83 | addFile:(file, namespace=@defaultNamespace) => 84 | file = path.normalize(file) 85 | 86 | for f in @files 87 | # File already exists in this namespace! 88 | return if file == f.origFile and namespace == f.namespace 89 | 90 | # Check if the file is a "filter path" 91 | if file.indexOf('*') > -1 92 | return @addFilesBasedOnFilter(file, namespace) 93 | 94 | relativeFile = @_getRelativePath(file) 95 | origFile = file 96 | needsCompiling = false 97 | 98 | # Determine if we need to copy/compile 99 | # the file into the staticRoot folder 100 | if (file.indexOf(@options.staticRoot) == -1 or @_needsCompiling(file)) 101 | writeTo = path.normalize(@_convertFilename(@options.staticRoot + '/generated/' + relativeFile)) 102 | needsCompiling = true 103 | file = writeTo 104 | relativeFile = @_getRelativePath(file) 105 | 106 | url = @options.staticUrlRoot + relativeFile 107 | 108 | url = normalizeUrl(url) 109 | @files.push 110 | url: url 111 | file: file 112 | origFile: origFile 113 | needsCompiling: needsCompiling 114 | namespace: namespace 115 | 116 | toBundles: => 117 | toBundle = (namespace, files) => 118 | str = '' 119 | for file in files 120 | if file.namespace == namespace 121 | @_compile(file.origFile, file.file) 122 | str += fs.readFileSync(file.file, 'utf-8').trim('\n') + '\n' 123 | 124 | str = @minify(str) 125 | hash = crypto.createHash('md5').update(str).digest('hex') 126 | filepath = "#{@options.staticRoot}/generated/bundle/#{hash.substring(0, 7)}_#{namespace}#{@fileExtension}" 127 | 128 | writeToFile(filepath, str) 129 | 130 | return filepath 131 | 132 | files = @files 133 | @files = [] 134 | 135 | bundles = [] 136 | for file in files 137 | bundles.push file.namespace unless file.namespace in bundles 138 | 139 | @addFile(toBundle(bundle, files), bundle) for bundle in bundles 140 | 141 | _compile: (file, writeTo) => 142 | compiler.compileFile(@options.compilers, file, (err, content) -> 143 | throw err if err? 144 | writeToFile(writeTo, content) 145 | ) 146 | return writeTo 147 | 148 | module.exports = Bundle 149 | -------------------------------------------------------------------------------- /lib/bundle_up.coffee: -------------------------------------------------------------------------------- 1 | AssetsManager = require './assets_manager' 2 | Js = require './js' 3 | Css = require './css' 4 | OnTheFlyCompiler = require './otf_compiler' 5 | compilers = require './default_compilers' 6 | 7 | class BundleUp 8 | constructor: (app, assetPath, options = {bundle:false}) -> 9 | unless options.compilers? 10 | options.compilers = compilers 11 | else 12 | options.compilers.stylus = options.compilers.stylus || compilers.stylus 13 | options.compilers.coffee = options.compilers.coffee || compilers.coffee 14 | options.compilers.js = options.compilers.js || compilers.js 15 | options.compilers.css = options.compilers.css || compilers.css 16 | 17 | options.minifyCss = options.minifyCss || false 18 | options.minifyJs = options.minifyJs || false 19 | 20 | @app = app 21 | @js = new Js(options) 22 | @css = new Css(options) 23 | 24 | require(assetPath)(new AssetsManager(@css, @js)) 25 | 26 | if options.bundle 27 | @js.toBundles() 28 | @css.toBundles() 29 | else 30 | # Compile files on-the-fly when not bundled 31 | @app.use (new OnTheFlyCompiler(@js, @css, options.compilers)).middleware 32 | 33 | @app.locals( 34 | renderStyles: (namespace=@css.defaultNamespace) => 35 | return @css.render(namespace) 36 | renderJs: (namespace=@js.defaultNamespace) => 37 | return @js.render(namespace) 38 | ) 39 | 40 | module.exports = (app, assetPath, options)-> 41 | new BundleUp(app, assetPath, options) 42 | -------------------------------------------------------------------------------- /lib/compiler.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | 3 | exports.compile = compile = (compilers, content, file, cb) -> 4 | fileExt = file.split('.') 5 | fileExt = fileExt[fileExt.length - 1] 6 | 7 | switch fileExt 8 | when 'coffee' 9 | try 10 | return cb(null, compilers.coffee(content, file)) 11 | catch err 12 | console.log file + ':\n ' + err.message + "\n" 13 | return cb(err, err.message) 14 | when 'styl' 15 | compilers.stylus(content, file).render (err, css) -> 16 | if err? 17 | console.log err.message 18 | css = err.message 19 | return cb(err, css) 20 | when 'css' 21 | return cb(null, compilers.css(content)) 22 | when 'js' 23 | return cb(null, compilers.js(content)) 24 | 25 | exports.compileFile = (compilers, file, cb) -> 26 | content = fs.readFileSync(file, 'utf-8') 27 | return compile(compilers, content, file, cb) 28 | -------------------------------------------------------------------------------- /lib/css.coffee: -------------------------------------------------------------------------------- 1 | csso = require 'csso' 2 | Bundle = require './bundle' 3 | class Css extends Bundle 4 | constructor: (@options) -> 5 | @fileExtension = '.css' 6 | super 7 | 8 | minify: (code) -> 9 | return code unless @options.minifyCss 10 | 11 | csso.justDoIt(code) 12 | 13 | render: (namespace) -> 14 | style = '' 15 | for file in @files 16 | if file.namespace == namespace 17 | style += "" 18 | return style 19 | 20 | _convertFilename: (filename) -> 21 | splitted = filename.split('.') 22 | splitted.splice(0, splitted.length - 1).join('.') + '.css' 23 | 24 | module.exports = Css 25 | -------------------------------------------------------------------------------- /lib/default_compilers.coffee: -------------------------------------------------------------------------------- 1 | coffee = require 'coffee-script' 2 | stylus = require 'stylus' 3 | 4 | module.exports = 5 | stylus: (content, file) -> 6 | return stylus(content) 7 | .set('filename', file) 8 | coffee: (content, file) -> 9 | return coffee.compile(content) 10 | js: (content) -> 11 | return content 12 | css: (content) -> 13 | return content 14 | -------------------------------------------------------------------------------- /lib/helpers.coffee: -------------------------------------------------------------------------------- 1 | mkdirp = require 'mkdirp' 2 | fs = require 'fs' 3 | 4 | # 5 | # Convenient method for writing a file on 6 | # a path that might not exist. This function 7 | # will create all folders provided in the 8 | # path to the file. 9 | # 10 | # 11 | # writeToFile('/tmp/hi/folder/file.js', "console.log('hi')") 12 | # 13 | # will create a file at /tmp/hi/folder/file.js with provided content 14 | # 15 | writeToFile = (file, content) -> 16 | try 17 | fs.writeFileSync(file, content) 18 | catch e 19 | if e.code == 'ENOENT' or e.code == 'EBADF' 20 | splitted = file.split('/') 21 | mkdirp.sync(splitted.splice(0, splitted.length-1).join('/'), 0o0777) 22 | 23 | # Retry! 24 | writeToFile(file, content) 25 | else 26 | console.log e 27 | 28 | normalizeUrl = (url) -> 29 | protocol = url.match(/^(http|https):\/\//)?[0] 30 | protocol = '' unless protocol? 31 | url = url.replace(protocol, '') 32 | url = url.replace('\\', '/') 33 | url = url.replace('//', '/') 34 | return protocol + url 35 | 36 | exports.writeToFile = writeToFile 37 | exports.normalizeUrl = normalizeUrl 38 | -------------------------------------------------------------------------------- /lib/js.coffee: -------------------------------------------------------------------------------- 1 | Bundle = require './bundle' 2 | UglifyJS = require 'uglify-js' 3 | class Js extends Bundle 4 | constructor: (@options) -> 5 | @fileExtension = '.js' 6 | super 7 | 8 | minify: (code) -> 9 | return code unless @options.minifyJs 10 | UglifyJS.minify(code, { fromString: true }).code # Minify with UglifyJS (v2) 11 | 12 | render: (namespace) -> 13 | js = '' 14 | for file in @files 15 | if file.namespace == namespace 16 | js += "" 17 | js 18 | 19 | _convertFilename: (filename) -> 20 | splitted = filename.split('.') 21 | splitted.splice(0, splitted.length - 1).join('.') + '.js' 22 | 23 | module.exports = Js 24 | -------------------------------------------------------------------------------- /lib/otf_compiler.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | mkdirp = require 'mkdirp' 3 | async = require 'async' 4 | stylus = require 'stylus' 5 | {compile} = require './compiler' 6 | 7 | class OnTheFlyCompiler 8 | constructor: (js, css, compilers) -> 9 | @js = js 10 | @css = css 11 | @compilers = compilers 12 | 13 | setImportFlag = (files) -> 14 | for file in files 15 | file._importChecked = false 16 | 17 | setImportFlag(@js.files) 18 | setImportFlag(@css.files) 19 | 20 | middleware: (req, res, next) => 21 | if req.url.indexOf('.js') > -1 22 | for file, i in @js.files 23 | if req.url == file.url 24 | return @handleFile(file, (err) -> next(err)) 25 | else 26 | return next() if i == (@js.files.length - 1) 27 | 28 | else if req.url.indexOf('.css') > -1 29 | for file, i in @css.files 30 | if req.url == file.url 31 | return @handleFile(file, (err) -> next(err)) if req.url == file.url 32 | else 33 | return next() if i == (@css.files.length - 1) 34 | return next() 35 | 36 | handleFile: (file, fn) => 37 | if not file._importChecked 38 | @mapImports file, (err) => 39 | return fn(err) if err? 40 | 41 | file._importChecked = true 42 | return @handleFile(file, fn) 43 | return 44 | 45 | # Check modified timestamp on file 46 | fs.stat file.origFile, (err, destStats) => 47 | return fn(err) if err? 48 | 49 | if not file._mtime 50 | return @compileFile(file, fn) 51 | if file._mtime < destStats.mtime 52 | return @compileFile(file, fn) 53 | 54 | # Check modified timestamps on imports 55 | else if file._imports? 56 | changed = [] 57 | compileFile = @compileFile 58 | async.forEach(file._imports, (importedFile, cb) => 59 | fs.stat importedFile.file, (err, stats) => 60 | return cb(err) if err? 61 | # One of the imported files changed 62 | if importedFile.mtime < stats.mtime 63 | importedFile.mtime = stats.mtime 64 | changed.push importedFile 65 | return cb() 66 | , (err) -> 67 | return fn(err) if err? 68 | 69 | if changed.length > 0 70 | return compileFile(file, fn) 71 | else 72 | return fn() 73 | ) 74 | else 75 | fn() 76 | 77 | compileFile: (file, fn) => 78 | return fn() unless file.needsCompiling 79 | 80 | fs.readFile file.origFile, 'utf8', (err, content) => 81 | return fn(err) if err? 82 | compile @compilers, content, file.origFile, (err, newContent) => 83 | return fn(err) if err? 84 | @writeToFile file.file, newContent, (err) -> 85 | return fn(err) if err? 86 | 87 | fs.stat file.origFile, (err, stats) -> 88 | return fn(err) if err? 89 | 90 | file._mtime = stats.mtime 91 | return fn() 92 | 93 | writeToFile: (file, content, fn) => 94 | fs.writeFile file, content, 'utf8', (err) => 95 | if err 96 | if err.code == 'ENOENT' 97 | splitted = file.split('/') 98 | mkdirp splitted.splice(0, splitted.length-1).join('/'), 0o0777, (err) => 99 | return fn(err) if err? 100 | return @writeToFile(file, content, fn) 101 | else 102 | return fn(err) 103 | else 104 | return fn() 105 | 106 | mapImports: (file, fn) -> 107 | # Need to map imports for stylus files 108 | if file.origFile.indexOf('.styl') > -1 109 | fs.readFile file.origFile, 'utf8', (err, content) => 110 | return fn(err) if err? 111 | style = @compilers.stylus(content, file.origFile) 112 | file._imports = [] 113 | paths = style.options._imports = [] 114 | style.render (err, css) -> 115 | async.forEach(paths, (path, cb) -> 116 | if path.path 117 | fs.stat path.path, (err, stats) -> 118 | return cb(err) if err? 119 | file._imports.push 120 | file: path.path 121 | mtime: stats.mtime 122 | cb() 123 | else 124 | cb() 125 | , (err) -> 126 | return fn(err) 127 | ) 128 | else 129 | return fn() 130 | 131 | module.exports = OnTheFlyCompiler 132 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bundle-up", 3 | "author": "Fredrik Lindin ", 4 | "description": "A simple asset manager middleware for connect", 5 | "version": "0.3.5", 6 | "repository": { 7 | "url": "" 8 | }, 9 | "main": "index.js", 10 | "engines": { 11 | "node": ">= 0.6.x < 0.9.0" 12 | }, 13 | "scripts": { 14 | "test": "node_modules/.bin/mocha --compilers coffee:coffee-script" 15 | }, 16 | "dependencies": { 17 | "coffee-script": ">= 1.1.3" 18 | , "csso": ">= 1.2.8" 19 | , "async": ">= 0.1.15" 20 | , "stylus": ">= 0.19.0" 21 | , "mkdirp": ">= 0.0.7" 22 | , "uglify-js" : ">= 2.2.0" 23 | }, 24 | "devDependencies": { 25 | "mocha": ">= 0.11.0" 26 | , "expect.js": ">= 0.1.1" 27 | , "rimraf": ">= 1.0.8" 28 | , "express": "2.x" 29 | , "request": ">= 2.2.9" 30 | , "jade": ">= 0.19.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/bundle_spec.coffee: -------------------------------------------------------------------------------- 1 | expect = require 'expect.js' 2 | BundleUp = require './../index' 3 | Js = require './../lib/js' 4 | Css = require './../lib/css' 5 | helper = require './helper' 6 | fs = require 'fs' 7 | express = require 'express' 8 | 9 | describe 'bundle:true', -> 10 | beforeEach -> 11 | helper.beforeEach() 12 | @app = express.createServer() 13 | 14 | # Bundle once without minifying and then once _with_ minifying: 15 | 16 | @bundle = BundleUp @app, __dirname + "/files/assets.coffee", 17 | staticRoot: __dirname + "/files/public/", 18 | staticUrlRoot:"/", 19 | bundle:true, 20 | minifyJs:false, 21 | minifyCss:false 22 | 23 | @bundle = BundleUp @app, __dirname + "/files/assets.coffee", 24 | staticRoot: __dirname + "/files/public/", 25 | staticUrlRoot:"/", 26 | bundle:true, 27 | minifyJs:true, 28 | minifyCss:true 29 | 30 | describe 'individual files', -> 31 | it 'should create coffee/1.js', (done) -> 32 | fs.stat __dirname + "/files/public/generated/coffee/1.js", (err, stats) -> 33 | expect(err).to.equal(null) 34 | done() 35 | 36 | it 'should create coffee/2.js', (done) -> 37 | fs.stat __dirname + "/files/public/generated/coffee/2.js", (err, stats) -> 38 | expect(err).to.equal(null) 39 | done() 40 | 41 | it 'should create js/1.js', (done) -> 42 | fs.stat __dirname + "/files/public/generated/js/1.js", (err, stats) -> 43 | expect(err).to.equal(null) 44 | done() 45 | 46 | it 'should create stylus/main.css', (done) -> 47 | fs.stat __dirname + "/files/public/generated/stylus/main.css", (err, stats) -> 48 | expect(err).to.equal(null) 49 | done() 50 | 51 | it 'should create public/print.css', (done) -> 52 | fs.stat __dirname + "/files/public/generated/print.css", (err, stats) -> 53 | expect(err).to.equal(null) 54 | done() 55 | 56 | describe 'bundled files', -> 57 | it 'should create a css bundle', (done) -> 58 | expect(@bundle.css.files.length).to.equal(1) 59 | fs.stat @bundle.css.files[0].file, (err, stats) -> 60 | expect(err).to.equal(null) 61 | done() 62 | 63 | it 'should create a js bundle', (done) -> 64 | expect(@bundle.js.files.length).to.equal(1) 65 | fs.stat @bundle.js.files[0].file, (err, stats) -> 66 | expect(err).to.equal(null) 67 | done() 68 | -------------------------------------------------------------------------------- /test/file_adding_spec.coffee: -------------------------------------------------------------------------------- 1 | expect = require 'expect.js' 2 | Js = require './../lib/js' 3 | Css = require './../lib/css' 4 | helper = require './helper' 5 | fs = require 'fs' 6 | 7 | describe 'When adding files', -> 8 | beforeEach -> 9 | helper.beforeEach() 10 | @js = new Js(staticRoot:"#{__dirname}/files/public", staticUrlRoot:'/') 11 | @css = new Css(staticRoot:"#{__dirname}/files/public", staticUrlRoot:'/') 12 | 13 | it 'should add the correct paths when adding a js file', -> 14 | @js.addFile(__dirname + '/files/js/1.js') 15 | expect(@js.files.length).to.equal(1) 16 | file = @js.files[0] 17 | 18 | expect(file.file).to.equal("#{__dirname}/files/public/generated/js/1.js") 19 | expect(file.origFile).to.equal(__dirname + '/files/js/1.js') 20 | expect(file.url).to.equal('/generated/js/1.js') 21 | 22 | it 'should add the correct paths when adding a coffee file', -> 23 | @js.addFile(__dirname + '/files/coffee/1.coffee') 24 | expect(@js.files.length).to.equal(1) 25 | file = @js.files[0] 26 | 27 | expect(file.file).to.equal("#{__dirname}/files/public/generated/coffee/1.js") 28 | expect(file.origFile).to.equal(__dirname + '/files/coffee/1.coffee') 29 | expect(file.url).to.equal('/generated/coffee/1.js') 30 | 31 | it 'should add the correct paths when adding a css file', -> 32 | @css.addFile(__dirname + '/files/css/screen.css') 33 | expect(@css.files.length).to.equal(1) 34 | file = @css.files[0] 35 | 36 | expect(file.file).to.equal("#{__dirname}/files/public/generated/css/screen.css") 37 | expect(file.origFile).to.equal(__dirname + '/files/css/screen.css') 38 | expect(file.url).to.equal('/generated/css/screen.css') 39 | 40 | it 'should add the correct paths when adding a styl file', -> 41 | @css.addFile(__dirname + '/files/stylus/main.styl') 42 | expect(@css.files.length).to.equal(1) 43 | file = @css.files[0] 44 | 45 | expect(file.file).to.equal("#{__dirname}/files/public/generated/stylus/main.css") 46 | expect(file.origFile).to.equal(__dirname + '/files/stylus/main.styl') 47 | expect(file.url).to.equal('/generated/stylus/main.css') 48 | 49 | it 'should not point to generated/ when adding a js file already in staticRoot', -> 50 | @js.addFile(__dirname + '/files/public/jquery.js') 51 | expect(@js.files.length).to.equal(1) 52 | file = @js.files[0] 53 | 54 | expect(file.file).to.equal("#{__dirname}/files/public/jquery.js") 55 | expect(file.origFile).to.equal("#{__dirname}/files/public/jquery.js") 56 | expect(file.url).to.equal('/jquery.js') 57 | 58 | it 'should not point to generated/ when adding a css file already in staticRoot', -> 59 | @css.addFile(__dirname + '/files/public/styles/bootstrap.css') 60 | expect(@css.files.length).to.equal(1) 61 | file = @css.files[0] 62 | 63 | expect(file.file).to.equal("#{__dirname}/files/public/styles/bootstrap.css") 64 | expect(file.origFile).to.equal("#{__dirname}/files/public/styles/bootstrap.css") 65 | expect(file.url).to.equal('/styles/bootstrap.css') 66 | 67 | it 'should add the correct paths when adding a coffee file inside staticRoot', -> 68 | @js.addFile(__dirname + '/files/public/main.coffee') 69 | expect(@js.files.length).to.equal(1) 70 | file = @js.files[0] 71 | 72 | expect(file.file).to.equal("#{__dirname}/files/public/generated/main.js") 73 | expect(file.origFile).to.equal(__dirname + '/files/public/main.coffee') 74 | expect(file.url).to.equal('/generated/main.js') 75 | 76 | it 'should add the correct paths when adding a styl file inside staticRoot', -> 77 | @css.addFile(__dirname + '/files/public/print.styl') 78 | expect(@css.files.length).to.equal(1) 79 | file = @css.files[0] 80 | 81 | expect(file.file).to.equal("#{__dirname}/files/public/generated/print.css") 82 | expect(file.origFile).to.equal(__dirname + '/files/public/print.styl') 83 | expect(file.url).to.equal('/generated/print.css') 84 | 85 | it 'should not change the css file when adding a file already in staticRoot', -> 86 | file = __dirname + '/files/public/styles/bootstrap.css' 87 | beforeStat = fs.statSync(file) 88 | @css.addFile(file) 89 | afterStat = fs.statSync(file) 90 | works = false 91 | for key of beforeStat 92 | works = true 93 | expect(beforeStat[key]).to.eql(afterStat[key]) 94 | expect(works).to.equal(true) 95 | 96 | it 'should not change the js file when adding a file already in staticRoot', -> 97 | file = __dirname + '/files/public/jquery.js' 98 | beforeStat = fs.statSync(file) 99 | @css.addFile(file) 100 | afterStat = fs.statSync(file) 101 | works = false 102 | for key of beforeStat 103 | works = true 104 | expect(beforeStat[key]).to.eql(afterStat[key]) 105 | expect(works).to.equal(true) 106 | 107 | it 'should only add the file once when adding the same file twice', -> 108 | @js.addFile(__dirname + '/files/coffee/1.coffee') 109 | @js.addFile(__dirname + '/files/coffee/1.coffee') 110 | 111 | expect(@js.files.length).to.equal(1) 112 | file = @js.files[0] 113 | expect(file.file).to.equal("#{__dirname}/files/public/generated/coffee/1.js") 114 | expect(file.origFile).to.equal(__dirname + '/files/coffee/1.coffee') 115 | expect(file.url).to.equal('/generated/coffee/1.js') 116 | 117 | describe 'filtered paths', -> 118 | it 'should be able to add 1.coffee using files/nested/js/*.coffee', -> 119 | @js.addFile(__dirname + '/files/nested/js/*.coffee') 120 | expect(@js.files.length).to.equal(1) 121 | expect(@js.files[0].origFile).to.equal(__dirname + '/files/nested/js/1.coffee') 122 | 123 | it 'should be able to add all 2 coffee files using files/nested/js/**.coffee', -> 124 | @js.addFile(__dirname + '/files/nested/js/**.coffee') 125 | expect(@js.files.length).to.equal(2) 126 | expect(@js.files[0].origFile).to.equal(__dirname + '/files/nested/js/1.coffee') 127 | expect(@js.files[1].origFile).to.equal(__dirname + '/files/nested/js/sub/2.coffee') 128 | 129 | it 'should be able to add all 6 files using files/coffee/js/**', -> 130 | @js.addFile(__dirname + '/files/nested/js/**') 131 | expect(@js.files.length).to.equal(6) 132 | expect(@js.files[0].origFile).to.equal(__dirname + '/files/nested/js/1.coffee') 133 | expect(@js.files[1].origFile).to.equal(__dirname + '/files/nested/js/1.js') 134 | expect(@js.files[2].origFile).to.equal(__dirname + '/files/nested/js/sub/2.coffee') 135 | expect(@js.files[3].origFile).to.equal(__dirname + '/files/nested/js/sub/2.js') 136 | expect(@js.files[4].origFile).to.equal(__dirname + '/files/nested/js/sub/sub2/3.js') 137 | expect(@js.files[5].origFile).to.equal(__dirname + '/files/nested/js/sub/sub2/4.js') 138 | 139 | it 'should add 0 files when trying /files/invalid*', -> 140 | @js.addFile(__dirname + '/files/invalid*') 141 | expect(@js.files.length).to.equal(0) 142 | 143 | it 'should add 6 files when trying /files/nested/j*', -> 144 | @js.addFile(__dirname + '/files/nested/j*') 145 | expect(@js.files.length).to.equal(6) 146 | -------------------------------------------------------------------------------- /test/files/assets.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (assets) -> 2 | assets.root = __dirname 3 | assets.addJs('/coffee/1.coffee') 4 | assets.addJs('/public/jquery.js') 5 | assets.addJs('/coffee/2.coffee') 6 | assets.addJs('/js/1.js') 7 | assets.addCss('/stylus/main.styl') 8 | assets.addCss('/public/print.styl') 9 | -------------------------------------------------------------------------------- /test/files/assets_namespaced.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (assets) -> 2 | assets.root = __dirname 3 | assets.addJs('/coffee/1.coffee') 4 | assets.addJs('/coffee/2.coffee', 'custom_namespace') 5 | assets.addJs('/coffee/3.coffee', 'custom_namespace') 6 | assets.addCss('/stylus/main.styl') 7 | assets.addCss('/public/print.styl', 'print_namespace') 8 | -------------------------------------------------------------------------------- /test/files/coffee/1.coffee: -------------------------------------------------------------------------------- 1 | console.log '1' 2 | -------------------------------------------------------------------------------- /test/files/coffee/2.coffee: -------------------------------------------------------------------------------- 1 | console.log '2' 2 | -------------------------------------------------------------------------------- /test/files/coffee/3.coffee: -------------------------------------------------------------------------------- 1 | console.log '3' 2 | -------------------------------------------------------------------------------- /test/files/coffee/syntax_error.coffee: -------------------------------------------------------------------------------- 1 | syntax_error->? 2 | -------------------------------------------------------------------------------- /test/files/css/screen.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/files/js/1.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/js/1.js -------------------------------------------------------------------------------- /test/files/js/2.js: -------------------------------------------------------------------------------- 1 | console.log('2'); 2 | -------------------------------------------------------------------------------- /test/files/js/3.js: -------------------------------------------------------------------------------- 1 | console.log('3'); 2 | -------------------------------------------------------------------------------- /test/files/nested/css/1.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/css/1.css -------------------------------------------------------------------------------- /test/files/nested/css/1.styl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/css/1.styl -------------------------------------------------------------------------------- /test/files/nested/css/sub/2.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/css/sub/2.css -------------------------------------------------------------------------------- /test/files/nested/css/sub/2.styl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/css/sub/2.styl -------------------------------------------------------------------------------- /test/files/nested/js/1.coffee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/js/1.coffee -------------------------------------------------------------------------------- /test/files/nested/js/1.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/js/1.js -------------------------------------------------------------------------------- /test/files/nested/js/sub/2.coffee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/js/sub/2.coffee -------------------------------------------------------------------------------- /test/files/nested/js/sub/2.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/js/sub/2.js -------------------------------------------------------------------------------- /test/files/nested/js/sub/sub2/3.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/js/sub/sub2/3.js -------------------------------------------------------------------------------- /test/files/nested/js/sub/sub2/4.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cowboy-coder/bundle-up/7b00063aca7692ac0366321651ffbc1fab478f7d/test/files/nested/js/sub/sub2/4.js -------------------------------------------------------------------------------- /test/files/public/jquery.js: -------------------------------------------------------------------------------- 1 | console.log('jquery!'); 2 | -------------------------------------------------------------------------------- /test/files/public/main.coffee: -------------------------------------------------------------------------------- 1 | console.log '1' 2 | -------------------------------------------------------------------------------- /test/files/public/print.styl: -------------------------------------------------------------------------------- 1 | body 2 | background #a2a2a2 3 | -------------------------------------------------------------------------------- /test/files/public/styles/bootstrap.css: -------------------------------------------------------------------------------- 1 | span { 2 | font-weight: bold; 3 | } 4 | -------------------------------------------------------------------------------- /test/files/stylus/main.styl: -------------------------------------------------------------------------------- 1 | @import typography 2 | body 3 | background-color: yellow 4 | -------------------------------------------------------------------------------- /test/files/stylus/syntax_error.styl: -------------------------------------------------------------------------------- 1 | syntax 2 | error 3 | -------------------------------------------------------------------------------- /test/files/stylus/typography.styl: -------------------------------------------------------------------------------- 1 | h1 2 | color: blue 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/helper.coffee: -------------------------------------------------------------------------------- 1 | rimraf = require 'rimraf' 2 | exports.beforeEach = -> 3 | try 4 | rimraf.sync(__dirname + '/files/public/generated') 5 | catch e 6 | #ignoring... 7 | -------------------------------------------------------------------------------- /test/namespaces_spec.coffee: -------------------------------------------------------------------------------- 1 | expect = require 'expect.js' 2 | BundleUp = require './../index' 3 | Js = require './../lib/js' 4 | Css = require './../lib/css' 5 | helper = require './helper' 6 | fs = require 'fs' 7 | express = require 'express' 8 | 9 | describe 'Namespaces', -> 10 | beforeEach -> 11 | helper.beforeEach() 12 | @js = new Js(staticRoot:"#{__dirname}/files/public", staticUrlRoot:'/') 13 | 14 | it 'should have default namespace set to "global"', -> 15 | @js.addFile(__dirname + '/files/coffee/1.coffee') 16 | expect(@js.files[0].namespace).to.equal('global') 17 | 18 | it 'should add the correct namespaces when 3 coffee files in separate namespaces', -> 19 | @js.addFile(__dirname + '/files/coffee/1.coffee', 'namespace1') 20 | @js.addFile(__dirname + '/files/coffee/2.coffee', 'namespace2') 21 | @js.addFile(__dirname + '/files/coffee/3.coffee', 'namespace3') 22 | 23 | expect(@js.files.length).to.equal(3) 24 | expect(@js.files[0].namespace).to.equal('namespace1') 25 | expect(@js.files[1].namespace).to.equal('namespace2') 26 | expect(@js.files[2].namespace).to.equal('namespace3') 27 | 28 | it 'should add the correct namespaces when using filtered paths', -> 29 | @js.addFile(__dirname + '/files/coffee/*.coffee', 'namespace1') 30 | 31 | expect(@js.files.length).to.equal(4) 32 | expect(@js.files[0].namespace).to.equal('namespace1') 33 | expect(@js.files[1].namespace).to.equal('namespace1') 34 | expect(@js.files[2].namespace).to.equal('namespace1') 35 | expect(@js.files[3].namespace).to.equal('namespace1') 36 | 37 | it 'should add a file two times if in different namespaces', -> 38 | @js.addFile(__dirname + '/files/coffee/*.coffee') 39 | @js.addFile(__dirname + '/files/coffee/1.coffee', 'namespace1') 40 | 41 | expect(@js.files.length).to.equal(5) 42 | expect(@js.files[0].namespace).to.equal('global') 43 | expect(@js.files[4].namespace).to.equal('namespace1') 44 | 45 | describe 'bundle:true', -> 46 | beforeEach -> 47 | @app = express.createServer() 48 | @bundle = BundleUp @app, __dirname + '/files/assets_namespaced.coffee', 49 | staticRoot: __dirname + '/files/public/', 50 | staticUrlRoot:'/', 51 | bundle:true 52 | 53 | it 'should create 2 js bundles and 2 css bundles', -> 54 | expect(@bundle.js.files.length).to.equal(2) 55 | expect(@bundle.css.files.length).to.equal(2) 56 | 57 | it 'should create global.js bundle and custom_namespace.bundle.js', -> 58 | expect(@bundle.js.files.length).to.equal(2) 59 | expect(@bundle.js.files[0].origFile).to.contain('global.js') 60 | expect(@bundle.js.files[1].origFile).to.contain('custom_namespace.js') 61 | 62 | expect(@bundle.js.files[0].namespace).to.equal('global') 63 | expect(@bundle.js.files[1].namespace).to.equal('custom_namespace') 64 | -------------------------------------------------------------------------------- /test/otf_compiler_spec.coffee: -------------------------------------------------------------------------------- 1 | expect = require 'expect.js' 2 | BundleUp = require './../index' 3 | Js = require './../lib/js' 4 | Css = require './../lib/css' 5 | helper = require './helper' 6 | fs = require 'fs' 7 | express = require 'express' 8 | request = require 'request' 9 | async = require 'async' 10 | 11 | describe 'OnTheFlyCompiler', -> 12 | bundle = {} 13 | beforeEach -> 14 | helper.beforeEach() 15 | @app = express.createServer() 16 | bundle = BundleUp @app, __dirname + '/files/assets.coffee', 17 | staticRoot: __dirname + '/files/public/', 18 | staticUrlRoot:"/", 19 | bundle:false 20 | @app.use(express.static(__dirname + '/files/public/')) 21 | 22 | @app.listen(1338) 23 | 24 | afterEach -> 25 | @app.close() 26 | 27 | it 'should compile stylus files correctly', (done) -> 28 | request.get 'http://localhost:1338/generated/stylus/main.css', (err, res) -> 29 | expected = """ 30 | h1 { 31 | color: #00f; 32 | } 33 | body { 34 | background-color: #ff0; 35 | } 36 | 37 | """ 38 | expect(res.body).to.equal(expected) 39 | done() 40 | 41 | it 'should compile coffee files correctly', (done) -> 42 | request.get 'http://localhost:1338/generated/coffee/1.js', (err, res) -> 43 | expect(res.body).to.contain("console.log('1');") 44 | done() 45 | 46 | it 'should map imported files for main.styl first time it is requested', (done) -> 47 | file = bundle.css.files[0] 48 | expect(file.origFile).to.equal(__dirname + '/files/stylus/main.styl') 49 | expect(file._imports).to.equal(undefined) 50 | request.get 'http://localhost:1338/generated/stylus/main.css', (err, res) -> 51 | expect(file._imports).to.not.equal(undefined) 52 | found = false 53 | for imp in file._imports 54 | if imp.file == __dirname + '/files/stylus/typography.styl' 55 | found = true 56 | expect(found).to.equal(true) 57 | 58 | done() 59 | 60 | it 'should re-compile main.styl when imported file change', (done) -> 61 | file = bundle.css.files[0] 62 | expect(file.origFile).to.equal(__dirname + '/files/stylus/main.styl') 63 | request.get 'http://localhost:1338/generated/stylus/main.css', (err, res) -> 64 | beforeTime = (fs.statSync __dirname + '/files/stylus/typography.styl').mtime 65 | 66 | oldContent = fs.readFileSync __dirname + '/files/stylus/typography.styl', 'utf8' 67 | fs.writeFileSync __dirname + '/files/stylus/typography.styl', oldContent + '\n', 'utf8' 68 | 69 | afterTime = (fs.statSync __dirname + '/files/stylus/typography.styl').mtime 70 | 71 | request.get 'http://localhost:1338/generated/stylus/main.css', (err, res) -> 72 | # Apparently the main.css file mtime doesn't change 73 | # most likely because the content in the file is the same... 74 | # therefore we make the expectations on the typography file 75 | # instead 76 | expect(beforeTime).to.not.equal(afterTime) 77 | importedFile = file._imports[1] 78 | expect(importedFile.file).to.equal(__dirname + '/files/stylus/typography.styl') 79 | expect(importedFile.mtime).to.eql(afterTime) 80 | 81 | # Clean up 82 | fs.writeFileSync __dirname + '/files/stylus/typography.styl', oldContent, 'utf8' 83 | done() 84 | 85 | it 'should not cause any errors when parallel requests comes in at the same time', (done) -> 86 | async.forEach [1..4], (i, cb) -> 87 | request.get 'http://localhost:1338/generated/stylus/main.css', (err, res) -> 88 | expect(res.statusCode).to.equal(200) 89 | cb(err) 90 | , (err) -> 91 | done(err) 92 | 93 | 94 | describe 'Error handling', -> 95 | it 'should respond with 500 when requesting a coffee file with syntax errors', (done) -> 96 | bundle.js.addFile(__dirname + '/files/coffee/syntax_error.coffee') 97 | request.get 'http://localhost:1338/generated/coffee/syntax_error.js', (err, res) -> 98 | expect(res.statusCode).to.equal(500) 99 | 100 | done() 101 | 102 | it 'should respond with 500 when requesting a stylus file with syntax errors', (done) -> 103 | bundle.css.addFile(__dirname + '/files/stylus/syntax_error.styl') 104 | request.get 'http://localhost:1338/generated/stylus/syntax_error.css', (err, res) -> 105 | expect(res.statusCode).to.equal(500) 106 | 107 | done() 108 | 109 | it 'should respond with 500 when requesting a file not found', (done) -> 110 | bundle.js.addFile(__dirname + '/files/coffee/not_found.coffee') 111 | request.get 'http://localhost:1338/generated/coffee/not_found.js', (err, res) -> 112 | expect(res.statusCode).to.equal(500) 113 | 114 | done() 115 | -------------------------------------------------------------------------------- /test/path_normalizing_spec.coffee: -------------------------------------------------------------------------------- 1 | expect = require 'expect.js' 2 | Js = require './../lib/js' 3 | Css = require './../lib/css' 4 | helper = require './helper' 5 | fs = require 'fs' 6 | 7 | describe 'Path normalizing', -> 8 | beforeEach -> 9 | helper.beforeEach() 10 | 11 | describe 'staticRoot', -> 12 | it "shouldn't matter if staticRoot ends with / or not", -> 13 | @js = new Js(staticRoot:"#{__dirname}/files/public", staticUrlRoot:'/') 14 | @js.addFile(__dirname + '/files/js/1.js') 15 | firstFile = @js.files[0] 16 | @js = new Js(staticRoot:"#{__dirname}/files/public/", staticUrlRoot:'/') 17 | @js.addFile(__dirname + '/files/js/1.js') 18 | secondFile = @js.files[0] 19 | expect(firstFile.file).to.equal(secondFile.file) 20 | expect(firstFile.origFile).to.equal(secondFile.origFile) 21 | expect(firstFile.file).to.equal(__dirname + '/files/public/generated/js/1.js') 22 | 23 | it "shouldn't matter if staticRoot path isn't 'normalized'" , -> 24 | @js = new Js(staticRoot:"#{__dirname}/files/public", staticUrlRoot:'/') 25 | expect(@js.options.staticRoot).to.equal("#{__dirname}/files/public") 26 | 27 | describe 'staticUrlRoot', -> 28 | it "shouldn't matter if staticUrlRoot ends with / or not", -> 29 | @js = new Js(staticRoot:"#{__dirname}/files/public", staticUrlRoot:'/url') 30 | @js.addFile(__dirname + '/files/js/1.js') 31 | firstFile = @js.files[0] 32 | @js = new Js(staticRoot:"#{__dirname}/files/public", staticUrlRoot:'/url/') 33 | @js.addFile(__dirname + '/files/js/1.js') 34 | secondFile = @js.files[0] 35 | expect(firstFile.url).to.equal(secondFile.url) 36 | expect(firstFile.url).to.equal('/url/generated/js/1.js') 37 | 38 | it 'should add the correct url using staticUrlRoot http://www.example.com', -> 39 | @js = new Js(staticRoot:"#{__dirname}/files/public", staticUrlRoot:'http://www.example.com') 40 | @js.addFile(__dirname + '/files/js/1.js') 41 | expect(@js.files[0].url).to.equal('http://www.example.com/generated/js/1.js') 42 | 43 | it 'should add the correct url using staticUrlRoot https://www.example.com/', -> 44 | @js = new Js(staticRoot:"#{__dirname}/files/public", staticUrlRoot:'https://www.example.com/') 45 | @js.addFile(__dirname + '/files/js/1.js') 46 | expect(@js.files[0].url).to.equal('https://www.example.com/generated/js/1.js') 47 | -------------------------------------------------------------------------------- /test/view_helper_spec.coffee: -------------------------------------------------------------------------------- 1 | expect = require 'expect.js' 2 | BundleUp = require './../index' 3 | Js = require './../lib/js' 4 | Css = require './../lib/css' 5 | helper = require './helper' 6 | fs = require 'fs' 7 | express = require 'express' 8 | request = require 'request' 9 | 10 | describe 'View Helper', -> 11 | beforeEach -> 12 | @app = express.createServer() 13 | @app.set('views', __dirname + '/views') 14 | @bundle = BundleUp @app, __dirname + '/files/assets_namespaced.coffee', 15 | staticRoot: __dirname + '/files/public/', 16 | staticUrlRoot:'/', 17 | bundle:true 18 | 19 | @app.get '/globalJs', (req, res) -> 20 | res.render 'globalJs.jade', layout:false 21 | 22 | @app.get '/globalJs/custom_namespaceJs', (req, res) -> 23 | res.render 'globalAndCustomJs.jade', layout:false 24 | 25 | @app.get '/custom_namespaceJs', (req, res) -> 26 | res.render 'customJs.jade', layout:false 27 | 28 | @app.get '/globalCss', (req, res) -> 29 | res.render 'globalCss.jade', layout:false 30 | 31 | @app.get '/globalJs/printNamespaceCss', (req, res) -> 32 | res.render 'globalAndPrintCss.jade', layout:false 33 | 34 | @app.get '/print_namespaceCss', (req, res) -> 35 | res.render 'printCss.jade', layout:false 36 | 37 | @app.listen(1338) 38 | 39 | afterEach -> 40 | @app.close() 41 | 42 | describe 'renderJs', -> 43 | it 'should render the global.js bundle', (done) -> 44 | request.get 'http://localhost:1338/globalJs', (err, res) => 45 | expect(@bundle.js.files[0].namespace).to.equal('global') 46 | expect(res.body).to.contain(@bundle.js.files[0].url) 47 | 48 | expect(res.body).to.not.contain(@bundle.js.files[1].url) 49 | done() 50 | 51 | it 'should render the global.js and the custom_namespace.js bundle', (done) -> 52 | request.get 'http://localhost:1338/globalJs/custom_namespaceJs', (err, res) => 53 | expect(@bundle.js.files[0].namespace).to.equal('global') 54 | expect(@bundle.js.files[1].namespace).to.equal('custom_namespace') 55 | expect(res.body).to.contain(@bundle.js.files[0].url) 56 | expect(res.body).to.contain(@bundle.js.files[1].url) 57 | done() 58 | 59 | it 'should only render the custom_namespace.js bundle', (done) -> 60 | request.get 'http://localhost:1338/custom_namespaceJs', (err, res) => 61 | expect(@bundle.js.files[1].namespace).to.equal('custom_namespace') 62 | expect(res.body).to.contain(@bundle.js.files[1].url) 63 | 64 | expect(res.body).to.not.contain(@bundle.js.files[0].url) 65 | done() 66 | 67 | describe 'renderStyles', -> 68 | it 'should render the global.css bundle', (done) -> 69 | request.get 'http://localhost:1338/globalCss', (err, res) => 70 | expect(@bundle.js.files[0].namespace).to.equal('global') 71 | expect(res.body).to.contain(@bundle.js.files[0].url) 72 | 73 | expect(res.body).to.not.contain(@bundle.js.files[1].url) 74 | done() 75 | 76 | it 'should render the global.css and the print_namespace.css bundle', (done) -> 77 | request.get 'http://localhost:1338/globalJs/printNamespaceCss', (err, res) => 78 | expect(@bundle.css.files[0].namespace).to.equal('global') 79 | expect(@bundle.css.files[1].namespace).to.equal('print_namespace') 80 | expect(res.body).to.contain(@bundle.css.files[0].url) 81 | expect(res.body).to.contain(@bundle.css.files[1].url) 82 | done() 83 | 84 | it 'should only render the print_namespace.css bundle', (done) -> 85 | request.get 'http://localhost:1338/print_namespaceCss', (err, res) => 86 | expect(@bundle.css.files[1].namespace).to.equal('print_namespace') 87 | expect(res.body).to.contain(@bundle.css.files[1].url) 88 | 89 | expect(res.body).to.not.contain(@bundle.css.files[0].url) 90 | done() 91 | -------------------------------------------------------------------------------- /test/views/customJs.jade: -------------------------------------------------------------------------------- 1 | !=renderJs('custom_namespace') 2 | -------------------------------------------------------------------------------- /test/views/globalAndCustomJs.jade: -------------------------------------------------------------------------------- 1 | !=renderJs() 2 | !=renderJs('custom_namespace') 3 | -------------------------------------------------------------------------------- /test/views/globalAndPrintCss.jade: -------------------------------------------------------------------------------- 1 | !=renderStyles() 2 | !=renderStyles('print_namespace') 3 | -------------------------------------------------------------------------------- /test/views/globalCss.jade: -------------------------------------------------------------------------------- 1 | !=renderJs() 2 | -------------------------------------------------------------------------------- /test/views/globalJs.jade: -------------------------------------------------------------------------------- 1 | !=renderJs() 2 | -------------------------------------------------------------------------------- /test/views/printCss.jade: -------------------------------------------------------------------------------- 1 | !=renderStyles('print_namespace') 2 | --------------------------------------------------------------------------------