mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'pr-667' into devel
This commit is contained in:
@@ -9,6 +9,17 @@
|
||||
// /app [user code]
|
||||
// /app.json: [data for server.js]
|
||||
// - load [list of files to load, relative to root, presumably under /app]
|
||||
// - manifest [list of resources in load order, each consists of an object]:
|
||||
// {
|
||||
// "path": relative path of file in the bundle, normalized to use forward slashes
|
||||
// "where": "client" [could also be "server" in future]
|
||||
// "type": "js", "css", or "static"
|
||||
// "cacheable": (client) boolean, is it safe to ask the browser to cache this file
|
||||
// "url": (client) relative url to download the resource, includes cache
|
||||
// busting param if used
|
||||
// "size": size in bytes
|
||||
// "hash": sha1 hash of the contents
|
||||
// }
|
||||
// /dependencies.json: files to monitor for changes in development mode
|
||||
// - extensions [list of extensions registered for user code, with dots]
|
||||
// - packages [map from package name to list of paths relative to the package]
|
||||
@@ -224,6 +235,9 @@ var Bundle = function () {
|
||||
// of file as buffer.
|
||||
self.files = {client: {}, client_cacheable: {}, server: {}};
|
||||
|
||||
// See description of manifest at the top
|
||||
self.manifest = [];
|
||||
|
||||
// list of segments of additional HTML for <head>/<body>
|
||||
self.head = [];
|
||||
self.body = [];
|
||||
@@ -329,6 +343,12 @@ _.extend(Bundle.prototype, {
|
||||
return inst;
|
||||
},
|
||||
|
||||
_hash: function (contents) {
|
||||
var hash = crypto.createHash('sha1');
|
||||
hash.update(contents);
|
||||
return hash.digest('hex');
|
||||
},
|
||||
|
||||
// Call to add a package to this bundle
|
||||
// if 'where' is given, it's an array of "client" and/or "server"
|
||||
// if 'from' is given, it's the PackageInstance that's doing the
|
||||
@@ -377,25 +397,36 @@ _.extend(Bundle.prototype, {
|
||||
minify: function () {
|
||||
var self = this;
|
||||
|
||||
var addFile = function (type, finalCode) {
|
||||
var contents = new Buffer(finalCode);
|
||||
var hash = self._hash(contents);
|
||||
var name = '/' + hash + '.' + type;
|
||||
self.files.client_cacheable[name] = contents;
|
||||
self.manifest.push({
|
||||
path: 'static_cacheable' + name,
|
||||
where: 'client',
|
||||
type: type,
|
||||
cacheable: true,
|
||||
url: name,
|
||||
size: contents.length,
|
||||
hash: hash
|
||||
});
|
||||
};
|
||||
|
||||
/// Javascript
|
||||
var codeParts = [];
|
||||
|
||||
_.each(self.js.client, function (js_path) {
|
||||
codeParts.push(self.files.client[js_path].toString('utf8'));
|
||||
|
||||
delete self.files.client[js_path];
|
||||
});
|
||||
self.js.client = [];
|
||||
|
||||
var combinedCode = codeParts.join('\n;\n');
|
||||
var finalCode = uglify.minify(
|
||||
combinedCode, {fromString: true, compress: {drop_debugger: false}}).code;
|
||||
|
||||
var hash = crypto.createHash('sha1');
|
||||
hash.update(finalCode);
|
||||
var digest = hash.digest('hex');
|
||||
var name = path.sep + digest + ".js";
|
||||
|
||||
self.files.client_cacheable[name] = new Buffer(finalCode);
|
||||
self.js.client = [name];
|
||||
addFile('js', finalCode);
|
||||
|
||||
/// CSS
|
||||
var css_concat = "";
|
||||
@@ -405,16 +436,21 @@ _.extend(Bundle.prototype, {
|
||||
|
||||
delete self.files.client[css_path];
|
||||
});
|
||||
self.css = [];
|
||||
|
||||
var final_css = cleanCSS.process(css_concat);
|
||||
|
||||
hash = crypto.createHash('sha1');
|
||||
hash.update(final_css);
|
||||
digest = hash.digest('hex');
|
||||
name = path.sep + digest + ".css";
|
||||
addFile('css', final_css);
|
||||
},
|
||||
|
||||
self.files.client_cacheable[name] = new Buffer(final_css);
|
||||
self.css = [name];
|
||||
_clientUrlsFor: function (type) {
|
||||
var self = this;
|
||||
return _.pluck(
|
||||
_.filter(self.manifest, function (resource) {
|
||||
return resource.where === 'client' && resource.type === type;
|
||||
}),
|
||||
'url'
|
||||
);
|
||||
},
|
||||
|
||||
_generate_app_html: function () {
|
||||
@@ -423,10 +459,10 @@ _.extend(Bundle.prototype, {
|
||||
var template = fs.readFileSync(path.join(__dirname, "app.html.in"));
|
||||
var f = require('handlebars').compile(template.toString());
|
||||
return f({
|
||||
scripts: self.js.client,
|
||||
scripts: self._clientUrlsFor('js'),
|
||||
head_extra: self.head.join('\n'),
|
||||
body_extra: self.body.join('\n'),
|
||||
stylesheets: self.css
|
||||
stylesheets: self._clientUrlsFor('css')
|
||||
});
|
||||
},
|
||||
|
||||
@@ -495,12 +531,64 @@ _.extend(Bundle.prototype, {
|
||||
|
||||
if (is_app) {
|
||||
if (fs.existsSync(path.join(project_dir, 'public'))) {
|
||||
files.cp_r(path.join(project_dir, 'public'),
|
||||
path.join(build_path, 'static'), {ignore: ignore_files});
|
||||
var copied =
|
||||
files.cp_r(path.join(project_dir, 'public'),
|
||||
path.join(build_path, 'static'), {ignore: ignore_files});
|
||||
|
||||
_.each(copied, function (fs_relative_path) {
|
||||
var filepath = path.join(build_path, 'static', fs_relative_path);
|
||||
var normalized = fs_relative_path.split(path.sep).join('/');
|
||||
self.manifest.push({
|
||||
// path is normalized to use forward slashes, so deliberately
|
||||
// not using path.sep here
|
||||
path: 'static/' + normalized,
|
||||
type: 'static',
|
||||
where: 'client',
|
||||
cacheable: false,
|
||||
url: '/' + normalized,
|
||||
size: fs.statSync(filepath).size,
|
||||
hash: self._hash(fs.readFileSync(filepath))
|
||||
});
|
||||
});
|
||||
}
|
||||
dependencies_json.app.push('public');
|
||||
}
|
||||
|
||||
// Add cache busting query param if needed, and
|
||||
// add to manifest.
|
||||
var processClientCode = function (type, file) {
|
||||
var contents, url;
|
||||
if (file in self.files.client_cacheable) {
|
||||
contents = self.files.client_cacheable[file];
|
||||
url = file;
|
||||
}
|
||||
else if (file in self.files.client) {
|
||||
// Client css and js becomes cacheable with the addition of the
|
||||
// cache busting query parameter.
|
||||
contents = self.files.client[file];
|
||||
delete self.files.client[file];
|
||||
self.files.client_cacheable[file] = contents;
|
||||
url = file + '?' + self._hash(contents)
|
||||
}
|
||||
else
|
||||
throw new Error('unable to find file: ' + file);
|
||||
|
||||
self.manifest.push({
|
||||
// path is normalized to use forward slashes
|
||||
path: 'static_cacheable' + file.split(path.sep).join('/'),
|
||||
where: 'client',
|
||||
type: type,
|
||||
cacheable: true,
|
||||
url: url,
|
||||
// contents is a Buffer and so correctly gives us the size in bytes
|
||||
size: contents.length,
|
||||
hash: self._hash(contents)
|
||||
});
|
||||
};
|
||||
|
||||
_.each(self.js.client, function (file) { processClientCode('js', file); });
|
||||
_.each(self.css, function (file) { processClientCode('css', file); });
|
||||
|
||||
// -- Client code --
|
||||
for (var rel_path in self.files.client) {
|
||||
var full_path = path.join(build_path, 'static', rel_path);
|
||||
@@ -515,25 +603,6 @@ _.extend(Bundle.prototype, {
|
||||
fs.writeFileSync(full_path, self.files.client_cacheable[rel_path]);
|
||||
}
|
||||
|
||||
// -- Add query params to client js and css --
|
||||
// This busts through browser caches when files change.
|
||||
var add_query_param = function (file) {
|
||||
if (file in self.files.client_cacheable)
|
||||
return file;
|
||||
else if (file in self.files.client) {
|
||||
var hash = crypto.createHash('sha1');
|
||||
hash.update(self.files.client[file]);
|
||||
var digest = hash.digest('hex');
|
||||
return file + "?" + digest;
|
||||
}
|
||||
// er? file we don't know how to serve? thats not right...
|
||||
return file;
|
||||
};
|
||||
self.js.client = _.map(self.js.client, add_query_param);
|
||||
self.css = _.map(self.css, add_query_param);
|
||||
|
||||
// --- Server code and generated files ---
|
||||
|
||||
app_json.load = [];
|
||||
files.mkdir_p(path.join(build_path, 'app'), 0755);
|
||||
for (var rel_path in self.files.server) {
|
||||
@@ -575,6 +644,8 @@ _.extend(Bundle.prototype, {
|
||||
|
||||
// --- Metadata ---
|
||||
|
||||
app_json.manifest = self.manifest;
|
||||
|
||||
dependencies_json.extensions = self._app_extensions();
|
||||
dependencies_json.exclude = _.pluck(ignore_files, 'source');
|
||||
dependencies_json.packages = {};
|
||||
@@ -585,7 +656,7 @@ _.extend(Bundle.prototype, {
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(build_path, 'app.json'),
|
||||
JSON.stringify(app_json));
|
||||
JSON.stringify(app_json, null, 2));
|
||||
fs.writeFileSync(path.join(build_path, 'dependencies.json'),
|
||||
JSON.stringify(dependencies_json));
|
||||
|
||||
|
||||
@@ -294,9 +294,14 @@ var files = module.exports = {
|
||||
// If options.ignore is present, it should be a list of regexps. Any
|
||||
// file whose basename matches one of the regexps, before
|
||||
// transformation, will be skipped.
|
||||
//
|
||||
// Returns the list of relative file paths copied to the
|
||||
// destination, as filtered by ignore and transformed by
|
||||
// transformer_filename.
|
||||
cp_r: function (from, to, options) {
|
||||
options = options || {};
|
||||
files.mkdir_p(to, 0755);
|
||||
var copied = [];
|
||||
_.each(fs.readdirSync(from), function (f) {
|
||||
if (_.any(options.ignore || [], function (pattern) {
|
||||
return f.match(pattern);
|
||||
@@ -306,8 +311,12 @@ var files = module.exports = {
|
||||
if (options.transform_filename)
|
||||
f = options.transform_filename(f);
|
||||
var full_to = path.join(to, f);
|
||||
if (fs.statSync(full_from).isDirectory())
|
||||
files.cp_r(full_from, full_to, options);
|
||||
if (fs.statSync(full_from).isDirectory()) {
|
||||
var subdir_paths = files.cp_r(full_from, full_to, options);
|
||||
copied = copied.concat(_.map(subdir_paths, function (subpath) {
|
||||
return path.join(f, subpath);
|
||||
}));
|
||||
}
|
||||
else {
|
||||
if (!options.transform_contents) {
|
||||
// XXX reads full file into memory.. lame.
|
||||
@@ -317,8 +326,10 @@ var files = module.exports = {
|
||||
contents = options.transform_contents(contents, f);
|
||||
fs.writeFileSync(full_to, contents);
|
||||
}
|
||||
copied.push(f);
|
||||
}
|
||||
});
|
||||
return copied;
|
||||
},
|
||||
|
||||
// Make a temporary directory. Returns the path to the newly created
|
||||
|
||||
@@ -88,10 +88,19 @@ var run = function () {
|
||||
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};
|
||||
|
||||
__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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user