Created
March 7, 2014 17:23
-
-
Save bmeck/9415813 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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