mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
`__meteor_bootstrap__.bundler` => `__meteor_bootstrap__.bundle` `bundle.bundle_dir` => `bundle.root` Rather than placing app_info/appInfo containing the manifest in the bundle object, instead I'm now placing the manifest directly in the bundle object. A) no code currently uses bundle.appInfo.load; B) if someday we expand the manifest to also include server side resources than we'd be getting rid of `load` anyway; C) I think it reads better: `bundle.appInfo.manifest` => `bundle.manifest` cp_r now returns a list of os-specific relative file system paths as strings, instead of paths as arrays. I changed the normalized "path" field in the manifest to be a relative path instead of an absolute path (`"/static/cat.jpg"` => `"static/cat.jpg"`). This felt better when looking at the manifest; I think because the path is relative to the bundle root.
174 lines
5.3 KiB
JavaScript
174 lines
5.3 KiB
JavaScript
////////// Requires //////////
|
|
|
|
var Fiber = require("fibers");
|
|
|
|
var fs = require("fs");
|
|
var path = require("path");
|
|
|
|
var connect = require('connect');
|
|
var gzippo = require('gzippo');
|
|
var argv = require('optimist').argv;
|
|
var mime = require('mime');
|
|
var handlebars = require('handlebars');
|
|
var useragent = require('useragent');
|
|
|
|
var _ = require('underscore');
|
|
|
|
// This code is duplicated in app/server/server.js.
|
|
var MIN_NODE_VERSION = 'v0.8.18';
|
|
if (require('semver').lt(process.version, MIN_NODE_VERSION)) {
|
|
process.stderr.write(
|
|
'Meteor requires Node ' + MIN_NODE_VERSION + ' or later.\n');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Keepalives so that when the outer server dies unceremoniously and
|
|
// doesn't kill us, we quit ourselves. A little gross, but better than
|
|
// pidfiles.
|
|
var init_keepalive = function () {
|
|
var keepalive_count = 0;
|
|
|
|
process.stdin.on('data', function (data) {
|
|
keepalive_count = 0;
|
|
});
|
|
|
|
process.stdin.resume();
|
|
|
|
setInterval(function () {
|
|
keepalive_count ++;
|
|
if (keepalive_count >= 3) {
|
|
console.log("Failed to receive keepalive! Exiting.");
|
|
process.exit(1);
|
|
}
|
|
}, 3000);
|
|
};
|
|
|
|
var supported_browser = function (user_agent) {
|
|
return true;
|
|
|
|
// For now, we don't actually deny anyone. The unsupported browser
|
|
// page isn't very good.
|
|
//
|
|
// var agent = useragent.lookup(user_agent);
|
|
// return !(agent.family === 'IE' && +agent.major <= 5);
|
|
};
|
|
|
|
// add any runtime configuration options needed to app_html
|
|
var runtime_config = function (app_html) {
|
|
var insert = '';
|
|
if (typeof __meteor_runtime_config__ === 'undefined')
|
|
return app_html;
|
|
|
|
app_html = app_html.replace(
|
|
"// ##RUNTIME_CONFIG##",
|
|
"__meteor_runtime_config__ = " +
|
|
JSON.stringify(__meteor_runtime_config__) + ";");
|
|
|
|
return app_html;
|
|
};
|
|
|
|
var run = function () {
|
|
var bundle_dir = path.join(__dirname, '..');
|
|
|
|
// check environment
|
|
var port = process.env.PORT ? parseInt(process.env.PORT) : 80;
|
|
|
|
// check for a valid MongoDB URL right away
|
|
if (!process.env.MONGO_URL)
|
|
throw new Error("MONGO_URL must be set in environment");
|
|
|
|
// webserver
|
|
var app = connect.createServer();
|
|
var static_cacheable_path = path.join(bundle_dir, 'static_cacheable');
|
|
if (fs.existsSync(static_cacheable_path))
|
|
app.use(gzippo.staticGzip(static_cacheable_path, {clientMaxAge: 1000 * 60 * 60 * 24 * 365}));
|
|
app.use(gzippo.staticGzip(path.join(bundle_dir, 'static'), {clientMaxAge: 0}));
|
|
|
|
// read bundle config file
|
|
var info_raw =
|
|
fs.readFileSync(path.join(bundle_dir, 'app.json'), 'utf8');
|
|
var info = JSON.parse(info_raw);
|
|
var bundle = {manifest: info.manifest, root: bundle_dir};
|
|
|
|
// start up app
|
|
|
|
__meteor_bootstrap__ = {
|
|
require: require,
|
|
startup_hooks: [],
|
|
app: app,
|
|
bundle: bundle
|
|
};
|
|
|
|
__meteor_runtime_config__ = {};
|
|
|
|
Fiber(function () {
|
|
// (put in a fiber to let Meteor.db operations happen during loading)
|
|
|
|
// load app code
|
|
_.each(info.load, function (filename) {
|
|
var code = fs.readFileSync(path.join(bundle_dir, filename));
|
|
// it's tempting to run the code in a new context so we can
|
|
// precisely control the enviroment the user code sees. but,
|
|
// this is harder than it looks. you get a situation where []
|
|
// created in one runInContext invocation fails 'instanceof
|
|
// Array' if tested in another (reusing the same context each
|
|
// time fixes it for {} and Object, but not [] and Array.) and
|
|
// we have no pressing need to do this, so punt.
|
|
//
|
|
// the final 'true' is an undocumented argument to
|
|
// runIn[Foo]Context that causes it to print out a descriptive
|
|
// error message on parse error. it's what require() uses to
|
|
// generate its errors.
|
|
require('vm').runInThisContext(code, filename, true);
|
|
});
|
|
|
|
|
|
// Actually serve HTML. This happens after user code, so that
|
|
// packages can insert connect middlewares and update
|
|
// __meteor_runtime_config__
|
|
var app_html = fs.readFileSync(path.join(bundle_dir, 'app.html'), 'utf8');
|
|
var unsupported_html = fs.readFileSync(path.join(bundle_dir, 'unsupported.html'));
|
|
|
|
app_html = runtime_config(app_html);
|
|
|
|
app.use(function (req, res) {
|
|
// prevent these URLs from returning app_html
|
|
//
|
|
// NOTE: app.manifest is not a web standard like favicon.ico and
|
|
// robots.txt. It is a file name we have chosen to use for HTML5
|
|
// appcache URLs. It is included here to prevent using an appcache
|
|
// then removing it from poisoning an app permanently. Eventually,
|
|
// once we have server side routing, this won't be needed as
|
|
// unknown URLs with return a 404 automatically.
|
|
if (_.indexOf(['/app.manifest', '/favicon.ico', '/robots.txt'], req.url)
|
|
!== -1) {
|
|
res.writeHead(404);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
|
|
if (supported_browser(req.headers['user-agent']))
|
|
res.write(app_html);
|
|
else
|
|
res.write(unsupported_html);
|
|
res.end();
|
|
});
|
|
|
|
// run the user startup hooks.
|
|
_.each(__meteor_bootstrap__.startup_hooks, function (x) { x(); });
|
|
|
|
// only start listening after all the startup code has run.
|
|
app.listen(port, function() {
|
|
if (argv.keepalive)
|
|
console.log("LISTENING"); // must match run.js
|
|
});
|
|
|
|
}).run();
|
|
|
|
if (argv.keepalive)
|
|
init_keepalive();
|
|
};
|
|
|
|
run();
|