├── .gitignore ├── Cakefile ├── LICENSE ├── README.md ├── lib └── stitch.js ├── package.json ├── src └── stitch.coffee └── test ├── fixtures ├── additional │ ├── foo │ │ └── bar.js │ └── hello.js ├── alternate │ ├── hello.alert │ └── nonsense.coffee ├── default │ ├── .dotfile.coffee │ ├── circular │ │ ├── error.js │ │ ├── index.js │ │ ├── using_exports_a.js │ │ ├── using_exports_b.js │ │ ├── using_module_exports_a.js │ │ └── using_module_exports_b.js │ ├── custom_exports.js │ ├── exported_property.coffee │ ├── foo.unknown │ ├── foo │ │ ├── bar.coffee │ │ ├── bar │ │ │ └── baz.js │ │ ├── baz.coffee │ │ └── buz │ │ │ └── index.coffee │ ├── module.coffee │ └── relative │ │ ├── a.js │ │ ├── b.js │ │ └── index.js ├── dependencies │ ├── backbone.js │ ├── underscore.js │ └── zepto.js ├── eco │ └── hello.html.eco └── link └── test_stitch.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /Cakefile: -------------------------------------------------------------------------------- 1 | {print} = require 'util' 2 | {spawn} = require 'child_process' 3 | 4 | build = (callback) -> 5 | coffee = spawn 'coffee', ['-c', '-o', 'lib', 'src'] 6 | coffee.stderr.on 'data', (data) -> 7 | process.stderr.write data.toString() 8 | coffee.stdout.on 'data', (data) -> 9 | print data.toString() 10 | coffee.on 'exit', (code) -> 11 | callback?() if code is 0 12 | 13 | task 'build', 'Build lib/ from src/', -> 14 | build() 15 | 16 | task 'test', 'Run tests', -> 17 | build -> 18 | process.chdir __dirname 19 | {reporters} = require 'nodeunit' 20 | reporters.default.run ['test'] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Joshua Peek 2 | Copyright (c) 2010 Sam Stephenson 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 3 | 4 | Develop and test your JavaScript applications as CommonJS modules in 5 | Node.js. Then __Stitch__ them together to run in the browser. 6 | 7 | npm install stitch 8 | 9 | Bundle code in lib/ and vendor/ and serve it with [Express](http://expressjs.com/): 10 | 11 | var stitch = require('stitch'); 12 | var express = require('express'); 13 | 14 | var package = stitch.createPackage({ 15 | paths: [__dirname + '/lib', __dirname + '/vendor'] 16 | }); 17 | 18 | var app = express.createServer(); 19 | app.get('/application.js', package.createServer()); 20 | app.listen(3000); 21 | 22 | Or build it to a file: 23 | 24 | var stitch = require('stitch'); 25 | var fs = require('fs'); 26 | 27 | var package = stitch.createPackage({ 28 | paths: [__dirname + '/lib', __dirname + '/vendor'] 29 | }); 30 | 31 | package.compile(function (err, source){ 32 | fs.writeFile('package.js', source, function (err) { 33 | if (err) throw err; 34 | console.log('Compiled package.js'); 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /lib/stitch.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | (function() { 3 | var CoffeeScript, Package, async, compilers, eco, extname, fs, join, normalize, _, _ref, 4 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; 5 | 6 | _ = require('underscore'); 7 | 8 | async = require('async'); 9 | 10 | fs = require('fs'); 11 | 12 | _ref = require('path'), extname = _ref.extname, join = _ref.join, normalize = _ref.normalize; 13 | 14 | exports.compilers = compilers = { 15 | js: function(module, filename) { 16 | var content; 17 | content = fs.readFileSync(filename, 'utf8'); 18 | return module._compile(content, filename); 19 | } 20 | }; 21 | 22 | try { 23 | CoffeeScript = require('coffee-script'); 24 | compilers.coffee = function(module, filename) { 25 | var content; 26 | content = CoffeeScript.compile(fs.readFileSync(filename, 'utf8')); 27 | return module._compile(content, filename); 28 | }; 29 | } catch (err) { 30 | 31 | } 32 | 33 | try { 34 | eco = require('eco'); 35 | if (eco.precompile) { 36 | compilers.eco = function(module, filename) { 37 | var content; 38 | content = eco.precompile(fs.readFileSync(filename, 'utf8')); 39 | return module._compile("module.exports = " + content, filename); 40 | }; 41 | } else { 42 | compilers.eco = function(module, filename) { 43 | var content; 44 | content = eco.compile(fs.readFileSync(filename, 'utf8')); 45 | return module._compile(content, filename); 46 | }; 47 | } 48 | } catch (err) { 49 | 50 | } 51 | 52 | exports.Package = Package = (function() { 53 | 54 | function Package(config) { 55 | this.compileSources = __bind(this.compileSources, this); 56 | 57 | this.compileDependencies = __bind(this.compileDependencies, this); 58 | 59 | var _ref1, _ref2, _ref3, _ref4; 60 | this.identifier = (_ref1 = config.identifier) != null ? _ref1 : 'require'; 61 | this.paths = (_ref2 = config.paths) != null ? _ref2 : ['lib']; 62 | this.dependencies = (_ref3 = config.dependencies) != null ? _ref3 : []; 63 | this.compilers = _.extend({}, compilers, config.compilers); 64 | this.cache = (_ref4 = config.cache) != null ? _ref4 : true; 65 | this.mtimeCache = {}; 66 | this.compileCache = {}; 67 | } 68 | 69 | Package.prototype.compile = function(callback) { 70 | return async.parallel([this.compileDependencies, this.compileSources], function(err, parts) { 71 | if (err) { 72 | return callback(err); 73 | } else { 74 | return callback(null, parts.join("\n")); 75 | } 76 | }); 77 | }; 78 | 79 | Package.prototype.compileDependencies = function(callback) { 80 | var _this = this; 81 | return async.map(this.dependencies, fs.readFile, function(err, dependencySources) { 82 | if (err) { 83 | return callback(err); 84 | } else { 85 | return callback(null, dependencySources.join("\n")); 86 | } 87 | }); 88 | }; 89 | 90 | Package.prototype.compileSources = function(callback) { 91 | var _this = this; 92 | return async.reduce(this.paths, {}, _.bind(this.gatherSourcesFromPath, this), function(err, sources) { 93 | var filename, index, name, result, source, _ref1; 94 | if (err) { 95 | return callback(err); 96 | } 97 | result = "(function(/*! Stitch !*/) {\n if (!this." + _this.identifier + ") {\n var modules = {}, cache = {}, require = function(name, root) {\n var path = expand(root, name), module = cache[path], fn;\n if (module) {\n return module.exports;\n } else if (fn = modules[path] || modules[path = expand(path, './index')]) {\n module = {id: path, exports: {}};\n try {\n cache[path] = module;\n fn(module.exports, function(name) {\n return require(name, dirname(path));\n }, module);\n return module.exports;\n } catch (err) {\n delete cache[path];\n throw err;\n }\n } else {\n throw 'module \\'' + name + '\\' not found';\n }\n }, expand = function(root, name) {\n var results = [], parts, part;\n if (/^\\.\\.?(\\/|$)/.test(name)) {\n parts = [root, name].join('/').split('/');\n } else {\n parts = name.split('/');\n }\n for (var i = 0, length = parts.length; i < length; i++) {\n part = parts[i];\n if (part == '..') {\n results.pop();\n } else if (part != '.' && part != '') {\n results.push(part);\n }\n }\n return results.join('/');\n }, dirname = function(path) {\n return path.split('/').slice(0, -1).join('/');\n };\n this." + _this.identifier + " = function(name) {\n return require(name, '');\n }\n this." + _this.identifier + ".define = function(bundle) {\n for (var key in bundle)\n modules[key] = bundle[key];\n };\n }\n return this." + _this.identifier + ".define;\n}).call(this)({"; 98 | index = 0; 99 | for (name in sources) { 100 | _ref1 = sources[name], filename = _ref1.filename, source = _ref1.source; 101 | result += index++ === 0 ? "" : ", "; 102 | result += JSON.stringify(name); 103 | result += ": function(exports, require, module) {" + source + "}"; 104 | } 105 | result += "});\n"; 106 | return callback(err, result); 107 | }); 108 | }; 109 | 110 | Package.prototype.createServer = function() { 111 | var _this = this; 112 | return function(req, res, next) { 113 | return _this.compile(function(err, source) { 114 | var message; 115 | if (err) { 116 | console.error("" + err.stack); 117 | message = "" + err.stack; 118 | res.writeHead(500, { 119 | 'Content-Type': 'text/javascript' 120 | }); 121 | return res.end("throw " + (JSON.stringify(message))); 122 | } else { 123 | res.writeHead(200, { 124 | 'Content-Type': 'text/javascript' 125 | }); 126 | return res.end(source); 127 | } 128 | }); 129 | }; 130 | }; 131 | 132 | Package.prototype.gatherSourcesFromPath = function(sources, sourcePath, callback) { 133 | var _this = this; 134 | return fs.stat(sourcePath, function(err, stat) { 135 | if (err) { 136 | return callback(err); 137 | } 138 | if (stat.isDirectory()) { 139 | return _this.getFilesInTree(sourcePath, function(err, paths) { 140 | if (err) { 141 | return callback(err); 142 | } 143 | return async.reduce(paths, sources, _.bind(_this.gatherCompilableSource, _this), callback); 144 | }); 145 | } else { 146 | return _this.gatherCompilableSource(sources, sourcePath, callback); 147 | } 148 | }); 149 | }; 150 | 151 | Package.prototype.gatherCompilableSource = function(sources, path, callback) { 152 | var _this = this; 153 | if (this.compilers[extname(path).slice(1)]) { 154 | return this.getRelativePath(path, function(err, relativePath) { 155 | if (err) { 156 | return callback(err); 157 | } 158 | return _this.compileFile(path, function(err, source) { 159 | var extension, key; 160 | if (err) { 161 | return callback(err); 162 | } else { 163 | extension = extname(relativePath); 164 | key = relativePath.slice(0, -extension.length); 165 | sources[key] = { 166 | filename: relativePath, 167 | source: source 168 | }; 169 | return callback(err, sources); 170 | } 171 | }); 172 | }); 173 | } else { 174 | return callback(null, sources); 175 | } 176 | }; 177 | 178 | Package.prototype.getRelativePath = function(path, callback) { 179 | var _this = this; 180 | return fs.realpath(path, function(err, sourcePath) { 181 | if (err) { 182 | return callback(err); 183 | } 184 | return async.map(_this.paths, fs.realpath, function(err, expandedPaths) { 185 | var base, expandedPath, _i, _len; 186 | if (err) { 187 | return callback(err); 188 | } 189 | for (_i = 0, _len = expandedPaths.length; _i < _len; _i++) { 190 | expandedPath = expandedPaths[_i]; 191 | base = expandedPath + "/"; 192 | if (sourcePath.indexOf(base) === 0) { 193 | return callback(null, sourcePath.slice(base.length)); 194 | } 195 | } 196 | return callback(new Error("" + path + " isn't in the require path")); 197 | }); 198 | }); 199 | }; 200 | 201 | Package.prototype.compileFile = function(path, callback) { 202 | var compile, extension, mod, mtime, source; 203 | extension = extname(path).slice(1); 204 | if (this.cache && this.compileCache[path] && this.mtimeCache[path] === this.compileCache[path].mtime) { 205 | return callback(null, this.compileCache[path].source); 206 | } else if (compile = this.compilers[extension]) { 207 | source = null; 208 | mod = { 209 | _compile: function(content, filename) { 210 | return source = content; 211 | } 212 | }; 213 | try { 214 | compile(mod, path); 215 | if (this.cache && (mtime = this.mtimeCache[path])) { 216 | this.compileCache[path] = { 217 | mtime: mtime, 218 | source: source 219 | }; 220 | } 221 | return callback(null, source); 222 | } catch (err) { 223 | if (err instanceof Error) { 224 | err.message = "can't compile " + path + "\n" + err.message; 225 | } else { 226 | err = new Error("can't compile " + path + "\n" + err); 227 | } 228 | return callback(err); 229 | } 230 | } else { 231 | return callback(new Error("no compiler for '." + extension + "' files")); 232 | } 233 | }; 234 | 235 | Package.prototype.walkTree = function(directory, callback) { 236 | var _this = this; 237 | return fs.readdir(directory, function(err, files) { 238 | if (err) { 239 | return callback(err); 240 | } 241 | return async.forEach(files, function(file, next) { 242 | var filename; 243 | if (file.match(/^\./)) { 244 | return next(); 245 | } 246 | filename = join(directory, file); 247 | return fs.stat(filename, function(err, stats) { 248 | var _ref1; 249 | _this.mtimeCache[filename] = stats != null ? (_ref1 = stats.mtime) != null ? _ref1.toString() : void 0 : void 0; 250 | if (!err && stats.isDirectory()) { 251 | return _this.walkTree(filename, function(err, filename) { 252 | if (filename) { 253 | return callback(err, filename); 254 | } else { 255 | return next(); 256 | } 257 | }); 258 | } else { 259 | callback(err, filename); 260 | return next(); 261 | } 262 | }); 263 | }, callback); 264 | }); 265 | }; 266 | 267 | Package.prototype.getFilesInTree = function(directory, callback) { 268 | var files; 269 | files = []; 270 | return this.walkTree(directory, function(err, filename) { 271 | if (err) { 272 | return callback(err); 273 | } else if (filename) { 274 | return files.push(filename); 275 | } else { 276 | return callback(err, files.sort()); 277 | } 278 | }); 279 | }; 280 | 281 | return Package; 282 | 283 | })(); 284 | 285 | exports.createPackage = function(config) { 286 | return new Package(config); 287 | }; 288 | 289 | }).call(this); 290 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "name": "stitch" 2 | , "description": "Stitch your CommonJS modules together for the browser" 3 | , "contributors": 4 | [ "Josh Peek" 5 | , "Sam Stephenson" 6 | ] 7 | , "version": "0.3.3" 8 | , "licenses": [ 9 | { "type": "MIT" 10 | , "url": "http://github.com/sstephenson/stitch/raw/master/LICENSE" 11 | }] 12 | , "main": "./lib/stitch" 13 | , "repository": 14 | { "type": "git" 15 | , "url": "http://github.com/sstephenson/stitch.git" 16 | } 17 | , "dependencies": 18 | { "async": ">=0.1.15" 19 | , "underscore": ">=1.1.3" 20 | } 21 | , "devDependencies" : 22 | { "coffee-script" : ">= 1.3.0" 23 | , "nodeunit" : ">= 0.6.4" 24 | , "eco": ">= 1.1.0-rc-3" 25 | } 26 | , "engine": { "node": ">=0.6" } 27 | } 28 | -------------------------------------------------------------------------------- /src/stitch.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | async = require 'async' 3 | fs = require 'fs' 4 | 5 | {extname, join, normalize} = require 'path' 6 | 7 | exports.compilers = compilers = 8 | js: (module, filename) -> 9 | content = fs.readFileSync filename, 'utf8' 10 | module._compile content, filename 11 | 12 | try 13 | CoffeeScript = require 'coffee-script' 14 | compilers.coffee = (module, filename) -> 15 | content = CoffeeScript.compile fs.readFileSync filename, 'utf8' 16 | module._compile content, filename 17 | catch err 18 | 19 | try 20 | eco = require 'eco' 21 | if eco.precompile 22 | compilers.eco = (module, filename) -> 23 | content = eco.precompile fs.readFileSync filename, 'utf8' 24 | module._compile "module.exports = #{content}", filename 25 | else 26 | compilers.eco = (module, filename) -> 27 | content = eco.compile fs.readFileSync filename, 'utf8' 28 | module._compile content, filename 29 | catch err 30 | 31 | 32 | exports.Package = class Package 33 | constructor: (config) -> 34 | @identifier = config.identifier ? 'require' 35 | @paths = config.paths ? ['lib'] 36 | @dependencies = config.dependencies ? [] 37 | @compilers = _.extend {}, compilers, config.compilers 38 | 39 | @cache = config.cache ? true 40 | @mtimeCache = {} 41 | @compileCache = {} 42 | 43 | compile: (callback) -> 44 | async.parallel [ 45 | @compileDependencies 46 | @compileSources 47 | ], (err, parts) -> 48 | if err then callback err 49 | else callback null, parts.join("\n") 50 | 51 | compileDependencies: (callback) => 52 | async.map @dependencies, fs.readFile, (err, dependencySources) => 53 | if err then callback err 54 | else callback null, dependencySources.join("\n") 55 | 56 | compileSources: (callback) => 57 | async.reduce @paths, {}, _.bind(@gatherSourcesFromPath, @), (err, sources) => 58 | return callback err if err 59 | 60 | result = """ 61 | (function(/*! Stitch !*/) { 62 | if (!this.#{@identifier}) { 63 | var modules = {}, cache = {}, require = function(name, root) { 64 | var path = expand(root, name), module = cache[path], fn; 65 | if (module) { 66 | return module.exports; 67 | } else if (fn = modules[path] || modules[path = expand(path, './index')]) { 68 | module = {id: path, exports: {}}; 69 | try { 70 | cache[path] = module; 71 | fn(module.exports, function(name) { 72 | return require(name, dirname(path)); 73 | }, module); 74 | return module.exports; 75 | } catch (err) { 76 | delete cache[path]; 77 | throw err; 78 | } 79 | } else { 80 | throw 'module \\'' + name + '\\' not found'; 81 | } 82 | }, expand = function(root, name) { 83 | var results = [], parts, part; 84 | if (/^\\.\\.?(\\/|$)/.test(name)) { 85 | parts = [root, name].join('/').split('/'); 86 | } else { 87 | parts = name.split('/'); 88 | } 89 | for (var i = 0, length = parts.length; i < length; i++) { 90 | part = parts[i]; 91 | if (part == '..') { 92 | results.pop(); 93 | } else if (part != '.' && part != '') { 94 | results.push(part); 95 | } 96 | } 97 | return results.join('/'); 98 | }, dirname = function(path) { 99 | return path.split('/').slice(0, -1).join('/'); 100 | }; 101 | this.#{@identifier} = function(name) { 102 | return require(name, ''); 103 | } 104 | this.#{@identifier}.define = function(bundle) { 105 | for (var key in bundle) 106 | modules[key] = bundle[key]; 107 | }; 108 | } 109 | return this.#{@identifier}.define; 110 | }).call(this)({ 111 | """ 112 | 113 | index = 0 114 | for name, {filename, source} of sources 115 | result += if index++ is 0 then "" else ", " 116 | result += JSON.stringify name 117 | result += ": function(exports, require, module) {#{source}}" 118 | 119 | result += """ 120 | });\n 121 | """ 122 | 123 | callback err, result 124 | 125 | createServer: -> 126 | (req, res, next) => 127 | @compile (err, source) -> 128 | if err 129 | console.error "#{err.stack}" 130 | message = "" + err.stack 131 | res.writeHead 500, 'Content-Type': 'text/javascript' 132 | res.end "throw #{JSON.stringify(message)}" 133 | else 134 | res.writeHead 200, 'Content-Type': 'text/javascript' 135 | res.end source 136 | 137 | 138 | gatherSourcesFromPath: (sources, sourcePath, callback) -> 139 | fs.stat sourcePath, (err, stat) => 140 | return callback err if err 141 | 142 | if stat.isDirectory() 143 | @getFilesInTree sourcePath, (err, paths) => 144 | return callback err if err 145 | async.reduce paths, sources, _.bind(@gatherCompilableSource, @), callback 146 | else 147 | @gatherCompilableSource sources, sourcePath, callback 148 | 149 | gatherCompilableSource: (sources, path, callback) -> 150 | if @compilers[extname(path).slice(1)] 151 | @getRelativePath path, (err, relativePath) => 152 | return callback err if err 153 | 154 | @compileFile path, (err, source) -> 155 | if err then callback err 156 | else 157 | extension = extname relativePath 158 | key = relativePath.slice(0, -extension.length) 159 | sources[key] = 160 | filename: relativePath 161 | source: source 162 | callback err, sources 163 | else 164 | callback null, sources 165 | 166 | getRelativePath: (path, callback) -> 167 | fs.realpath path, (err, sourcePath) => 168 | return callback err if err 169 | 170 | async.map @paths, fs.realpath, (err, expandedPaths) -> 171 | return callback err if err 172 | 173 | for expandedPath in expandedPaths 174 | base = expandedPath + "/" 175 | if sourcePath.indexOf(base) is 0 176 | return callback null, sourcePath.slice base.length 177 | callback new Error "#{path} isn't in the require path" 178 | 179 | compileFile: (path, callback) -> 180 | extension = extname(path).slice(1) 181 | 182 | if @cache and @compileCache[path] and @mtimeCache[path] is @compileCache[path].mtime 183 | callback null, @compileCache[path].source 184 | else if compile = @compilers[extension] 185 | source = null 186 | mod = 187 | _compile: (content, filename) -> 188 | source = content 189 | 190 | try 191 | compile mod, path 192 | 193 | if @cache and mtime = @mtimeCache[path] 194 | @compileCache[path] = {mtime, source} 195 | 196 | callback null, source 197 | catch err 198 | if err instanceof Error 199 | err.message = "can't compile #{path}\n#{err.message}" 200 | else 201 | err = new Error "can't compile #{path}\n#{err}" 202 | callback err 203 | else 204 | callback new Error "no compiler for '.#{extension}' files" 205 | 206 | walkTree: (directory, callback) -> 207 | fs.readdir directory, (err, files) => 208 | return callback err if err 209 | 210 | async.forEach files, (file, next) => 211 | return next() if file.match /^\./ 212 | filename = join directory, file 213 | 214 | fs.stat filename, (err, stats) => 215 | @mtimeCache[filename] = stats?.mtime?.toString() 216 | 217 | if !err and stats.isDirectory() 218 | @walkTree filename, (err, filename) -> 219 | if filename 220 | callback err, filename 221 | else 222 | next() 223 | else 224 | callback err, filename 225 | next() 226 | , callback 227 | 228 | getFilesInTree: (directory, callback) -> 229 | files = [] 230 | @walkTree directory, (err, filename) -> 231 | if err 232 | callback err 233 | else if filename 234 | files.push filename 235 | else 236 | callback err, files.sort() 237 | 238 | 239 | exports.createPackage = (config) -> 240 | new Package config 241 | -------------------------------------------------------------------------------- /test/fixtures/additional/foo/bar.js: -------------------------------------------------------------------------------- 1 | exports.filename = "additional/foo/bar.js"; 2 | -------------------------------------------------------------------------------- /test/fixtures/additional/hello.js: -------------------------------------------------------------------------------- 1 | exports.hello = "hello"; 2 | -------------------------------------------------------------------------------- /test/fixtures/alternate/hello.alert: -------------------------------------------------------------------------------- 1 | hello world 2 | -------------------------------------------------------------------------------- /test/fixtures/alternate/nonsense.coffee: -------------------------------------------------------------------------------- 1 | # This is some nonsense that should throw a parse error 2 | 3 | class for when if void 4 | -------------------------------------------------------------------------------- /test/fixtures/default/.dotfile.coffee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstephenson/stitch/a734eceb99f431b37d2693e1fe64195f76e159ec/test/fixtures/default/.dotfile.coffee -------------------------------------------------------------------------------- /test/fixtures/default/circular/error.js: -------------------------------------------------------------------------------- 1 | exports.a = require('./using_exports_a'); 2 | throw 'hello'; 3 | -------------------------------------------------------------------------------- /test/fixtures/default/circular/index.js: -------------------------------------------------------------------------------- 1 | exports.using_exports_a = require("./using_exports_a"); 2 | exports.using_exports_b = require("./using_exports_b"); 3 | exports.using_module_exports_a = require("./using_module_exports_a"); 4 | exports.using_module_exports_b = require("./using_module_exports_b"); 5 | -------------------------------------------------------------------------------- /test/fixtures/default/circular/using_exports_a.js: -------------------------------------------------------------------------------- 1 | exports.a = function() { 2 | return "a"; 3 | }; 4 | exports.b = require("./using_exports_b").b; 5 | -------------------------------------------------------------------------------- /test/fixtures/default/circular/using_exports_b.js: -------------------------------------------------------------------------------- 1 | var a = require("./using_exports_a"); 2 | exports.b = function() { 3 | return a.a(); 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/default/circular/using_module_exports_a.js: -------------------------------------------------------------------------------- 1 | var B; 2 | 3 | module.exports = { 4 | a: function() { 5 | return 'a'; 6 | }, 7 | b: function() { 8 | return B.b() 9 | } 10 | }; 11 | 12 | B = require('./using_module_exports_b'); 13 | -------------------------------------------------------------------------------- /test/fixtures/default/circular/using_module_exports_b.js: -------------------------------------------------------------------------------- 1 | A = require('./using_module_exports_a'); 2 | 3 | module.exports = { 4 | b: function() { 5 | return A.a() 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/default/custom_exports.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | return "foo"; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/default/exported_property.coffee: -------------------------------------------------------------------------------- 1 | exports.foo = "bar" 2 | -------------------------------------------------------------------------------- /test/fixtures/default/foo.unknown: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstephenson/stitch/a734eceb99f431b37d2693e1fe64195f76e159ec/test/fixtures/default/foo.unknown -------------------------------------------------------------------------------- /test/fixtures/default/foo/bar.coffee: -------------------------------------------------------------------------------- 1 | bar = "baz" 2 | -------------------------------------------------------------------------------- /test/fixtures/default/foo/bar/baz.js: -------------------------------------------------------------------------------- 1 | exports.baz = require("foo/baz").baz; 2 | 3 | -------------------------------------------------------------------------------- /test/fixtures/default/foo/baz.coffee: -------------------------------------------------------------------------------- 1 | exports.baz = "biz" 2 | -------------------------------------------------------------------------------- /test/fixtures/default/foo/buz/index.coffee: -------------------------------------------------------------------------------- 1 | 2 | exports.buz= "BUZ" -------------------------------------------------------------------------------- /test/fixtures/default/module.coffee: -------------------------------------------------------------------------------- 1 | exports.foo = require("exported_property").foo 2 | exports.bar = require("custom_exports") 3 | exports.baz = require("foo/bar/baz").baz 4 | -------------------------------------------------------------------------------- /test/fixtures/default/relative/a.js: -------------------------------------------------------------------------------- 1 | exports.a = "a"; 2 | exports.b = require("./b"); 3 | -------------------------------------------------------------------------------- /test/fixtures/default/relative/b.js: -------------------------------------------------------------------------------- 1 | module.exports = "b"; 2 | -------------------------------------------------------------------------------- /test/fixtures/default/relative/index.js: -------------------------------------------------------------------------------- 1 | exports.a = require("./a"); 2 | exports.custom = require("../custom_exports"); 3 | exports.baz = require("../foo/bar/baz").baz; 4 | exports.buz = require("../foo/bar/../buz").buz; 5 | -------------------------------------------------------------------------------- /test/fixtures/dependencies/backbone.js: -------------------------------------------------------------------------------- 1 | // Backbone 2 | -------------------------------------------------------------------------------- /test/fixtures/dependencies/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore 2 | -------------------------------------------------------------------------------- /test/fixtures/dependencies/zepto.js: -------------------------------------------------------------------------------- 1 | // Zepto 2 | -------------------------------------------------------------------------------- /test/fixtures/eco/hello.html.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | hello <%= @name %>! 5 | -------------------------------------------------------------------------------- /test/fixtures/link: -------------------------------------------------------------------------------- 1 | default -------------------------------------------------------------------------------- /test/test_stitch.coffee: -------------------------------------------------------------------------------- 1 | sys = require "util" 2 | fs = require "fs" 3 | stitch = require "../." 4 | 5 | fixtureRoot = __dirname + "/fixtures" 6 | fixtures = fixtureRoot + "/default" 7 | altFixtures = fixtureRoot + "/alternate" 8 | addlFixtures = fixtureRoot + "/additional" 9 | ecoFixtures = fixtureRoot + "/eco" 10 | linkFixtures = fixtureRoot + "/link" 11 | fixtureCount = 17 12 | 13 | defaultOptions = 14 | identifier: "testRequire" 15 | paths: [fixtures] 16 | defaultPackage = stitch.createPackage defaultOptions 17 | 18 | additionalOptions = 19 | identifier: "testRequire" 20 | paths: [addlFixtures] 21 | additionalPackage = stitch.createPackage additionalOptions 22 | 23 | alternateOptions = 24 | paths: [altFixtures] 25 | alternatePackage = stitch.createPackage alternateOptions 26 | 27 | ecoOptions = 28 | identifier: "testRequire" 29 | paths: [ecoFixtures] 30 | ecoPackage = stitch.createPackage ecoOptions 31 | 32 | dependencyOptions = 33 | identifier: "testRequire" 34 | paths: [fixtures] 35 | dependencies: [ 36 | fixtureRoot + "/dependencies/zepto.js" 37 | fixtureRoot + "/dependencies/underscore.js" 38 | fixtureRoot + "/dependencies/backbone.js" 39 | ] 40 | dependencyPackage = stitch.createPackage dependencyOptions 41 | 42 | linkOptions = 43 | identifier: "testRequire" 44 | paths: [linkFixtures] 45 | linkPackage = stitch.createPackage linkOptions 46 | 47 | 48 | load = (source, callback) -> 49 | (-> eval source).call module = {} 50 | callback? (source) -> (-> eval source).call module 51 | module.testRequire 52 | 53 | rescue = (callback) -> 54 | rescued = false 55 | try 56 | callback() 57 | catch err 58 | rescued = true 59 | rescued 60 | 61 | 62 | module.exports = 63 | "walk tree": (test) -> 64 | test.expect fixtureCount 65 | 66 | defaultPackage.walkTree fixtures, (err, file) -> 67 | if file 68 | test.ok file 69 | else 70 | test.done() 71 | 72 | "get files in tree": (test) -> 73 | test.expect 2 74 | 75 | defaultPackage.getFilesInTree fixtures, (err, files) -> 76 | test.ok !err 77 | test.same fixtureCount, files.length 78 | test.done() 79 | 80 | "get files in tree that does not exist": (test) -> 81 | test.expect 1 82 | 83 | defaultPackage.getFilesInTree fixtures + "/missing", (err, files) -> 84 | test.ok err 85 | test.done() 86 | 87 | "get files in empty directory": (test) -> 88 | test.expect 1 89 | 90 | dirname = fixtures + "/empty" 91 | fs.mkdirSync dirname, 0o0755 92 | defaultPackage.getFilesInTree dirname, (err, files) -> 93 | test.ok !err 94 | fs.rmdirSync dirname 95 | test.done() 96 | 97 | "compile file": (test) -> 98 | test.expect 2 99 | 100 | defaultPackage.compileFile __filename, (err, source) -> 101 | test.ok !err 102 | test.ok source.match(/\(function\(\) \{/) 103 | test.done() 104 | 105 | "compile file does not exist": (test) -> 106 | test.expect 1 107 | 108 | defaultPackage.compileFile "nosuchthing.coffee", (err, source) -> 109 | test.ok err 110 | test.done() 111 | 112 | "compile file with syntax error": (test) -> 113 | test.expect 1 114 | 115 | alternatePackage.compileFile altFixtures + "/nonsense.coffee", (err, source) -> 116 | test.ok err.toString().match(/SyntaxError/) 117 | test.done() 118 | 119 | "compile file with custom compiler": (test) -> 120 | test.expect 1 121 | 122 | options = Object.create alternateOptions 123 | options.compilers = 124 | alert: (module, filename) -> 125 | source = require('fs').readFileSync filename, 'utf8' 126 | source = "alert(#{sys.inspect source});" 127 | module._compile source, filename 128 | pkg = stitch.createPackage options 129 | 130 | pkg.compileFile altFixtures + "/hello.alert", (err, source) -> 131 | test.same "alert('hello world\\n');", source 132 | test.done() 133 | 134 | "compile file with unknown extension": (test) -> 135 | test.expect 1 136 | 137 | alternatePackage.compileFile altFixtures + "/hello.alert", (err, source) -> 138 | test.ok err.toString().match(/no compiler/) 139 | test.done() 140 | 141 | "get relative path": (test) -> 142 | test.expect 2 143 | 144 | defaultPackage.getRelativePath fixtures + "/foo/bar.coffee", (err, path) -> 145 | test.ok !err 146 | test.same 'foo/bar.coffee', path 147 | test.done() 148 | 149 | "compile generates valid javascript": (test) -> 150 | test.expect 2 151 | 152 | defaultPackage.compile (err, sources) -> 153 | test.ok !err 154 | testRequire = load sources 155 | test.ok typeof testRequire is "function" 156 | test.done() 157 | 158 | "compile module with custom exports": (test) -> 159 | test.expect 3 160 | 161 | defaultPackage.compile (err, sources) -> 162 | test.ok !err 163 | testRequire = load sources 164 | result = testRequire("custom_exports") 165 | test.ok typeof result is "function" 166 | test.same "foo", result() 167 | test.done() 168 | 169 | "compile module with exported property": (test) -> 170 | test.expect 2 171 | 172 | defaultPackage.compile (err, sources) -> 173 | test.ok !err 174 | testRequire = load sources 175 | test.same "bar", testRequire("exported_property").foo 176 | test.done() 177 | 178 | "compile module with requires": (test) -> 179 | test.expect 4 180 | 181 | defaultPackage.compile (err, sources) -> 182 | test.ok !err 183 | testRequire = load sources 184 | module = testRequire("module") 185 | test.same "bar", module.foo 186 | test.same "foo", module.bar() 187 | test.same "biz", module.baz 188 | test.done() 189 | 190 | "runtime require only loads files once": (test) -> 191 | test.expect 3 192 | 193 | defaultPackage.compile (err, sources) -> 194 | test.ok !err 195 | testRequire = load sources 196 | module = testRequire("module") 197 | test.ok !module.x 198 | module.x = "foo" 199 | test.same "foo", testRequire("module").x 200 | test.done() 201 | 202 | "look for module index if necessary": (test) -> 203 | test.expect 2 204 | 205 | defaultPackage.compile (err, sources) -> 206 | test.ok !err 207 | testRequire = load sources 208 | buz = testRequire("foo/buz").buz 209 | test.same buz, "BUZ" 210 | test.done() 211 | 212 | "modules can be defined at runtime": (test) -> 213 | test.expect 3 214 | 215 | defaultPackage.compile (err, sources) -> 216 | test.ok !err 217 | testRequire = load sources 218 | 219 | test.ok rescue -> testRequire("frob") 220 | 221 | testRequire.define 222 | "frob": (exports, require, module) -> 223 | exports.frob = require("foo/buz").buz 224 | 225 | test.same "BUZ", testRequire("frob").frob 226 | test.done() 227 | 228 | "multiple packages may share the same require namespace": (test) -> 229 | test.expect 5 230 | 231 | defaultPackage.compile (err, sources) -> 232 | test.ok !err 233 | testRequire = load sources, (load) -> 234 | additionalPackage.compile (err, sources) -> 235 | test.ok !err 236 | load sources 237 | 238 | test.same "hello", testRequire("hello").hello 239 | test.same "additional/foo/bar.js", testRequire("foo/bar").filename 240 | test.same "biz", testRequire("foo/bar/baz").baz; 241 | test.done() 242 | 243 | "relative require": (test) -> 244 | test.expect 6 245 | 246 | defaultPackage.compile (err, sources) -> 247 | test.ok !err 248 | testRequire = load sources 249 | 250 | relative = testRequire("relative") 251 | test.same "a", relative.a.a 252 | test.same "b", relative.a.b 253 | test.same "foo", relative.custom() 254 | test.same "biz", relative.baz 255 | test.same "BUZ", relative.buz 256 | test.done() 257 | 258 | "circular require": (test) -> 259 | test.expect 7 260 | 261 | defaultPackage.compile (err, sources) -> 262 | test.ok !err 263 | testRequire = load sources 264 | 265 | circular = testRequire("circular") 266 | test.same "a", circular.using_exports_a.a() 267 | test.same "a", circular.using_exports_b.b() 268 | test.same "a", circular.using_exports_a.b() 269 | 270 | test.same "a", circular.using_module_exports_a.a() 271 | test.same "a", circular.using_module_exports_b.b() 272 | test.same "a", circular.using_module_exports_a.b() 273 | 274 | test.done() 275 | 276 | "errors at require time don't leave behind a partially loaded cache": (test) -> 277 | test.expect 3 278 | 279 | defaultPackage.compile (err, sources) -> 280 | test.ok !err 281 | testRequire = load sources 282 | 283 | test.ok rescue -> testRequire("circular/error") 284 | test.ok rescue -> testRequire("circular/error") 285 | test.done() 286 | 287 | "dependencies option concatenates files in order": (test) -> 288 | test.expect 5 289 | dependencyPackage.compile (err, sources) -> 290 | test.ok !err 291 | lines = sources.split("\n").slice(0, 5) 292 | 293 | test.same "// Zepto", lines[0] 294 | test.same "// Underscore", lines[2] 295 | test.same "// Backbone", lines[4] 296 | 297 | testRequire = load sources 298 | test.ok testRequire("foo/bar/baz") 299 | test.done() 300 | 301 | "paths may be symlinks": (test) -> 302 | test.expect 2 303 | linkPackage.compile (err, sources) -> 304 | test.ok !err 305 | testRequire = load sources 306 | test.ok testRequire("foo/bar/baz") 307 | test.done() 308 | 309 | if stitch.compilers.eco 310 | module.exports["eco compiler"] = (test) -> 311 | test.expect 2 312 | ecoPackage.compile (err, sources) -> 313 | test.ok !err 314 | testRequire = load sources 315 | 316 | html = testRequire("hello.html")(name: "Sam").trim() 317 | test.same "hello Sam!", html.split("\n").pop() 318 | test.done() 319 | 320 | --------------------------------------------------------------------------------