Skip to content

Instantly share code, notes, and snippets.

@bmeck
Created March 7, 2014 17:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bmeck/9415813 to your computer and use it in GitHub Desktop.
Save bmeck/9415813 to your computer and use it in GitHub Desktop.
//
// Code to update bootstrap
//
var fs = require('fs');
var path = require('path');
var zlib = require('zlib');
var MAX_COMMENT_SIZE = 65535;
var CENTRAL_DIRECTORY_HEADER_SIZE = 22;
function fdUtilSync(fd, start, stop) {
if (typeof start === 'number' && typeof stop !== 'number') {
throw new Error('if you provide a start, you must provide a stop');
}
start = +start;
stop = +stop;
this.start = start;
this.stop = stop;
this.fstatSync = function (cb) {
var result = fs.fstatSync(fd);
if (start === start) {
result.size = stop - start;
}
return result;
};
this.readSync = function (buff, buff_offset, amount, file_offset) {
if (start === start) {
file_offset += start;
}
return fs.readSync(fd, buff, buff_offset, amount, file_offset);
};
this.sub = function (substart, substop) {
var offset = start || 0;
return new fdUtilSync(fd, offset + substart, offset + substop);
};
this.readStreamSync = function (ondata) {
var needle = start;
while (needle < stop) {
var pref_read_len = 4096;
var read_len = stop - needle;
if (read_len > pref_read_len) {
read_len = pref_read_len;
}
if (needle + read_len > stop) {
read_len = stop - needle;
}
var buff = new Buffer(read_len);
var bytes_read = fs.readSync(fd, buff, 0, buff.length, needle);
if (ondata) ondata(buff.slice(0, bytes_read));
needle += bytes_read;
}
};
return this;
}
function Zip(file, header) {
this.file = file;
this.header = header;
return this;
}
Object.defineProperties(Zip.prototype, {
centralDirectoryEntries: {
get: function () {
return this.header.readUInt16LE(10);
}
},
centralDirectoryLength: {
get: function () {
return this.header.readUInt32LE(12);
}
},
centralDirectoryOffset: {
get: function () {
return this.header.readUInt32LE(16);
}
},
length: {
get: function () {
return this.centralDirectoryLength + this.centralDirectoryOffset + this.header.length;
}
}
});
Zip.openSync = function (opts) {
if (typeof opts === 'string') {
return fopen_ZipSync(new fdUtilSync(fs.openSync(opts, 'r')));
}
else if (opts.file instanceof fdUtilSync) {
return fopen_ZipSync(opts.file, cb);
}
throw new Error('invalid arguments');
}
Zip.prototype.mapEntriesSync = function mapEntriesSync() {
var results = Object.create(null);
this.directoryStreamSync(function (directory_entry) {
results[directory_entry.name] = directory_entry;
});
return results;
}
function fopen_ZipSync(file, cb) {
var stat = file.fstatSync();
var len = stat.size;
var pref_read_size = 1024;
var central_dir_position = 0;
var found = 0;
var buffers = [];
while (true) {
var read_len = CENTRAL_DIRECTORY_HEADER_SIZE + MAX_COMMENT_SIZE - central_dir_position;
if (read_len > pref_read_size) {
read_len = pref_read_size;
}
var read_pos = len - central_dir_position - read_len;
if (read_pos < 0) {
read_len += read_pos;
read_pos = 0;
}
if (read_len < 0) {
throw new Error('not a zip');
}
var buff = new Buffer(read_len);
var bytes_read = file.readSync(buff, 0, read_len, read_pos);
for (var i = bytes_read; i-->0;) {
if (found === 0 && buff[i] === 0x06) {found = 1;}
else if (found === 1 && buff[i] === 0x05) {found = 2;}
else if (found === 2 && buff[i] === 0x4b) {found = 3;}
else if (found === 3 && buff[i] === 0x50) {
found=4;
break;
}
else {found = 0;}
}
central_dir_position += bytes_read-i-1;
if (found === 4) {
buffers.push(buff.slice(i));
var zip = new Zip(null, Buffer.concat(buffers));
zip.file = file.sub(len - zip.length, len);
return zip;
}
buffers.push(buff);
}
}
Zip.prototype.directoryStreamSync = function (onEntry) {
var file = this.file;
var needle = this.centralDirectoryOffset;
for (var count = this.centralDirectoryEntries; count-->0;) {
var entry_header_buff = new Buffer(46);
file.readSync(entry_header_buff, 0, 46, needle);
needle += 46;
var file_name_size = entry_header_buff.readUInt16LE(28);
var extra_field_size = entry_header_buff.readUInt16LE(30);
var comment_size = entry_header_buff.readUInt16LE(32);
var variable_size = file_name_size + extra_field_size + comment_size;
var buff = new Buffer(variable_size);
file.readSync(buff, 0, variable_size, needle);
needle += variable_size;
var file_name_buff = new Buffer(file_name_size);
buff.copy(file_name_buff, 0, 0, file_name_size);
var extra_field_buff = new Buffer(extra_field_size);
buff.copy(extra_field_buff, 0, file_name_size, file_name_size + extra_field_size);
var comment_buff = new Buffer(comment_size);
buff.copy(comment_buff, 0, file_name_size + extra_field_size, variable_size);
var entry = new ZipDirectoryEntry(file, entry_header_buff, file_name_buff, extra_field_buff, comment_buff);
if (onEntry) onEntry(entry);
}
};
function ZipDirectoryEntry(file, header, entryName, extraField, comment) {
this.file = file;
this.header = header;
this.name = entryName;
this.extraField = extraField;
this.comment = comment;
}
Object.defineProperties(ZipDirectoryEntry.prototype, {
localHeaderOffset: {
get: function () {
return this.header.readUInt32LE(42);
}
},
compressedSize: {
get: function () {
return this.header.readUInt32LE(20);
}
},
compressionMethod: {
get: function () {
return this.header.readUInt16LE(10);
}
}
});
ZipDirectoryEntry.prototype.entrySync = function () {
var file = this.file;
var localHeaderOffset = this.localHeaderOffset;
var entry_header_buff = new Buffer(30);
file.readSync(entry_header_buff, 0, 30, localHeaderOffset);
var file_name_size = entry_header_buff.readUInt16LE(26);
var extra_field_size = entry_header_buff.readUInt16LE(28);
var variable_size = file_name_size + extra_field_size;
var buff = new Buffer(variable_size);
file.readSync(new Buffer(variable_size), 0, variable_size, localHeaderOffset + 30);
var file_name_buff = new Buffer(file_name_size);
buff.copy(file_name_buff, 0, 0, file_name_size);
var extra_field_buff = new Buffer(extra_field_size);
buff.copy(extra_field_buff, 0, file_name_size, file_name_size + extra_field_size);
var _entry = new ZipEntry(null, entry_header_buff, file_name_buff, extra_field_buff);
_entry.file = file.sub(localHeaderOffset, localHeaderOffset + 30 + variable_size + _entry.compressedSize);
return _entry;
};
function ZipEntry(file, header, entryName, extraField) {
this.file = file;
this.header = header;
this.name = entryName;
this.extraField = extraField;
return this;
}
Object.defineProperties(ZipEntry.prototype, {
headerSize: {
get: function () {
return this.header.length + this.name.length + this.extraField.length;
}
},
compressedSize: {
get: function () {
return this.header.readUInt32LE(18);
}
},
compressionMethod: {
get: function () {
return this.header.readUInt16LE(8);
}
},
readAllSync: {
value: function () {
var buffers = [];
var _ondata = function (buff) {
buffers.push(buff);
};
this.file.sub(this.headerSize, this.headerSize + this.compressedSize).readStreamSync(_ondata);
if (this.compressionMethod === 8) {
return zlib.inflateRawSync(Buffer.concat(buffers));
}
else if (this.compressionMethod === 0) {
return Buffer.concat(buffers);
}
throw new Error('Unknown compression');
}
}
});
function _load(module, main) {
module.require.main = main;
module.load();
module.loaded = true;
return module.exports;
}
var assert = require('assert');
function createModuleSystem (cached, resolve, load) {
var main = null;
function Module(filename, parent) {
var self = this;
this.id = filename;
this.parent = parent;
if (parent) {
parent.children.push(this);
}
this.require = this.require.bind(this);
this.require.resolve = function (path) {
return resolve(self, path);
};
this.filename = filename;
this.exports = {};
this.loaded = false;
this.children = [];
}
Module.prototype.load = function () {
assert(this.loaded === false);
load(this, this.filename);
return this.exports;
};
Module.prototype.require = function (modulePath) {
var resolvedPath = resolve(this, modulePath);
var cachedModule = cached(this, resolvedPath);
if (cachedModule) return cachedModule.exports;
var newModule = new Module(resolvedPath, module);
return _load(newModule, main);
};
Module.runMain = function (path) {
var resolvedPath = resolve(null, path);
var module = new Module(resolvedPath, null);
main = module;
return _load(module, module);
};
return Module;
}
var absolute = function (path) { return normalize(path) == path; };
var join = path.join;
var normalize = path.normalize;
var extname = path.extname;
var dirname = path.dirname;
function createNodeZippedModuleSystem(zip_path) {
zip_path = fs.realpathSync(zip_path);
var _zip = Zip.openSync(zip_path);
var map = _zip.mapEntriesSync();
function load(module, resolvedPath) {
var extension = extname(resolvedPath);
var handler = extensions[extension] || extensions['.js'];
module.require.extensions = extensions;
module.require.cache = require.cache;
resolvedPath = path.resolve(zip_path, resolvedPath);
require.cache[resolvedPath] = module;
handler(module, resolvedPath);
return module;
}
function resolve(module, modulePath) {
//console.log('RAW', modulePath, module)
var isFile = /^(?:\.\.?|\/)/.test(modulePath);
var resolvedPath;
var dir;
if (isFile) {
// if this is an absolute or relative path (aka not node_module)
modulePath = path.resolve(module.filename !== zip_path ? dirname(module.filename) : zip_path, modulePath);
if (modulePath.indexOf(zip_path) !== 0) {
throw new Error('module '+JSON.stringify(modulePath)+' not in archive');
}
dir = dirname(module.filename);
if (modulePath.slice(-1) === '/') {
modulePath = modulePath.slice(0, -1);
}
resolvedPath = resolveFile(modulePath);
if (!resolvedPath) resolvedPath = resolveFile(modulePath+'/');
if (resolvedPath) return resolvedPath;
}
else {
modulePath = normalize(modulePath);
dir = dirname(module.filename);
if (modulePath.slice(-1) === '/') {
modulePath = modulePath.slice(0, -1);
}
resolvedPath = resolveNodeModule(modulePath, dir);
if (!resolvedPath) resolvedPath = resolveNodeModule( modulePath+'/', dir);
if (resolvedPath) return resolvedPath;
}
throw new Error('module '+JSON.stringify(modulePath)+' not found');
}
function isDir(directory_entry) {
return directory_entry.name.toString().slice(-1) === '/';
}
function resolveFileExtension(possibleFile) {
var mappedPossibleFile = possibleFile.indexOf(zip_path+'/')===0?possibleFile.slice(zip_path.length+1):possibleFile;
var entry = map[mappedPossibleFile];
if (entry && !isDir(entry)) return possibleFile;
var extension;
for (extension in extensions) {
entry = map[mappedPossibleFile+extension];
if (entry && !isDir(entry)) return possibleFile+extension;
}
return null;
}
function resolveFile(possibleFile) {
// EXACT
var foundFile = resolveFileExtension(possibleFile);
if (foundFile) {
return foundFile;
}
var dir = possibleFile;
// INDEX?
possibleFile = path.join(dir, 'index');
foundFile = resolveFileExtension(possibleFile);
if (foundFile) {
return foundFile;
}
possibleFile = path.join(dir, 'package.json');
var directory_entry = map[possibleFile.indexOf(zip_path+'/')==0?possibleFile.slice(zip_path.length+1):possibleFile];
// is it a file?
if (directory_entry && directory_entry.name.toString().slice(-1) !== '/') {
var _entry = directory_entry.entrySync();
var pkg = JSON.parse(_entry.readAllSync().toString());
possibleFile = path.normalize(path.join(dir, pkg.main));
// PACKAGE.MAIN?
if (pkg.main) {
possibleFile = path.normalize(path.join(dir, pkg.main));
foundFile = resolveFileExtension(possibleFile);
if (foundFile) {
return foundFile;
}
// PACKAGE.MAIN INDEX?
possibleFile = path.join(possibleFile, 'index');
}
else {
// PACKAGE INDEX?
possibleFile = path.normalize(path.join(dir, 'index'));
}
foundFile = resolveFileExtension(possibleFile);
if (foundFile) {
return foundFile;
}
}
return null;
}
function resolveNodeModule(name, modulesFolder) {
var olddir;
do {
var possiblePackage = path.join(modulesFolder, 'node_modules');
var directory_entry = map[possiblePackage];
// is it a directory?
if (directory_entry && directory_entry.name.toString().slice(-1)==='/') {
var modulePath = resolveFile(path.join(modulesFolder, 'node_modules', name));
if (modulePath) return modulePath;
}
olddir = modulesFolder;
modulesFolder = path.dirname(modulesFolder);
} while (modulesFolder !== olddir);
return null;
}
function cached(module, modulePath) {
var cachedModule = require.cache[modulePath];
if (cachedModule) {
return cachedModule;
}
var resolvedPath = resolve(module, modulePath);
return require.cache[resolvedPath] || null;
}
var extensions = require.extensions = {
'.js': function (module, filename) {
// remove zip container and trailing slash
var mapped_filename = filename.slice(zip_path.length+1);
var _entry = map[mapped_filename].entrySync();
var buffer = _entry.readAllSync();
var fn = new Function('__filename', '__dirname', 'require', 'module', 'exports', buffer.toString());
fn(filename, dirname(filename), module.require, module, module.exports);
},
'.json': function (module, filename) {
// remove zip container and trailing slash
var mapped_filename = filename.slice(zip_path.length+1);
var _entry = map[mapped_filename].entrySync();
var buffer = _entry.readAllSync();
module.exports = JSON.parse(buffer.toString());
}
};
var ModuleSystem = createModuleSystem(cached, resolve, load);
//builtins.module = ModuleSystem;
return ModuleSystem;
}
var zip = path.resolve(process.argv[2]);
var zippedModule = new createNodeZippedModuleSystem(zip);
new zippedModule(zip, null).require(process.argv[3]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment