react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / node_modules / commoner / lib / reader.js
81158 viewsvar assert = require("assert");1var path = require("path");2var fs = require("fs");3var Q = require("q");4var iconv = require("iconv-lite");5var createHash = require("crypto").createHash;6var getRequiredIDs = require("install").getRequiredIDs;7var util = require("./util");8var BuildContext = require("./context").BuildContext;9var slice = Array.prototype.slice;1011function ModuleReader(context, resolvers, processors) {12var self = this;13assert.ok(self instanceof ModuleReader);14assert.ok(context instanceof BuildContext);15assert.ok(resolvers instanceof Array);16assert.ok(processors instanceof Array);1718var hash = createHash("sha1").update(context.optionsHash + "\0");1920function hashCallbacks(salt) {21hash.update(salt + "\0");2223var cbs = util.flatten(slice.call(arguments, 1));2425cbs.forEach(function(cb) {26assert.strictEqual(typeof cb, "function");27hash.update(cb + "\0");28});2930return cbs;31}3233resolvers = hashCallbacks("resolvers", resolvers, warnMissingModule);3435var procArgs = [processors];36if (context.relativize && !context.ignoreDependencies)37procArgs.push(require("./relative").getProcessor(self));38processors = hashCallbacks("processors", procArgs);3940Object.defineProperties(self, {41context: { value: context },42idToHash: { value: {} },43resolvers: { value: resolvers },44processors: { value: processors },45salt: { value: hash.digest("hex") }46});47}4849ModuleReader.prototype = {50getSourceP: util.cachedMethod(function(id) {51var context = this.context;52var copy = this.resolvers.slice(0).reverse();53assert.ok(copy.length > 0, "no source resolvers registered");5455function tryNextResolverP() {56var resolve = copy.pop();5758try {59var promise = Q(resolve && resolve.call(context, id));60} catch (e) {61promise = Q.reject(e);62}6364return resolve ? promise.then(function(result) {65if (typeof result === "string")66return result;67return tryNextResolverP();68}, tryNextResolverP) : promise;69}7071return tryNextResolverP();72}),7374getCanonicalIdP: util.cachedMethod(function(id) {75var reader = this;76if (reader.context.useProvidesModule) {77return reader.getSourceP(id).then(function(source) {78return reader.context.getProvidedId(source) || id;79});80} else {81return Q(id);82}83}),8485readModuleP: util.cachedMethod(function(id) {86var reader = this;8788return reader.getSourceP(id).then(function(source) {89if (reader.context.useProvidesModule) {90// If the source contains a @providesModule declaration, treat91// that declaration as canonical. Note that the Module object92// returned by readModuleP might have an .id property whose93// value differs from the original id parameter.94id = reader.context.getProvidedId(source) || id;95}9697assert.strictEqual(typeof source, "string");9899var hash = createHash("sha1")100.update("module\0")101.update(id + "\0")102.update(reader.salt + "\0")103.update(source.length + "\0" + source)104.digest("hex");105106if (reader.idToHash.hasOwnProperty(id)) {107// Ensure that the same module identifier is not108// provided by distinct modules.109assert.strictEqual(110reader.idToHash[id], hash,111"more than one module named " +112JSON.stringify(id));113} else {114reader.idToHash[id] = hash;115}116117return reader.buildModuleP(id, hash, source);118});119}),120121buildModuleP: util.cachedMethod(function(id, hash, source) {122var reader = this;123return reader.processOutputP(124id, hash, source125).then(function(output) {126return new Module(reader, id, hash, output);127});128}, function(id, hash, source) {129return hash;130}),131132processOutputP: function(id, hash, source) {133var reader = this;134var cacheDir = reader.context.cacheDir;135var manifestDir = cacheDir && path.join(cacheDir, "manifest");136var charset = reader.context.options.outputCharset;137138function buildP() {139var promise = Q(source);140141reader.processors.forEach(function(build) {142promise = promise.then(function(input) {143return util.waitForValuesP(144build.call(reader.context, id, input)145);146});147});148149return promise.then(function(output) {150if (typeof output === "string") {151output = { ".js": output };152} else {153assert.strictEqual(typeof output, "object");154}155156return util.waitForValuesP(output);157158}).then(function(output) {159util.log.err(160"built Module(" + JSON.stringify(id) + ")",161"cyan"162);163164return output;165166}).catch(function(err) {167// Provide additional context for uncaught build errors.168util.log.err("Error while reading module " + id + ":");169throw err;170});171}172173if (manifestDir) {174return util.mkdirP(manifestDir).then(function(manifestDir) {175var manifestFile = path.join(manifestDir, hash + ".json");176177return util.readJsonFileP(manifestFile).then(function(manifest) {178Object.keys(manifest).forEach(function(key) {179var cacheFile = path.join(cacheDir, manifest[key]);180manifest[key] = util.readFileP(cacheFile);181});182183return util.waitForValuesP(manifest, true);184185}).catch(function(err) {186return buildP().then(function(output) {187var manifest = {};188189Object.keys(output).forEach(function(key) {190var cacheFile = manifest[key] = hash + key;191var fullPath = path.join(cacheDir, cacheFile);192193if (charset) {194fs.writeFileSync(fullPath, iconv.encode(output[key], charset))195} else {196fs.writeFileSync(fullPath, output[key], "utf8");197}198});199200fs.writeFileSync(201manifestFile,202JSON.stringify(manifest),203"utf8"204);205206return output;207});208});209});210}211212return buildP();213},214215readMultiP: function(ids) {216var reader = this;217218return Q(ids).all().then(function(ids) {219if (ids.length === 0)220return ids; // Shortcut.221222var modulePs = ids.map(reader.readModuleP, reader);223return Q(modulePs).all().then(function(modules) {224var seen = {};225var result = [];226227modules.forEach(function(module) {228if (!seen.hasOwnProperty(module.id)) {229seen[module.id] = true;230result.push(module);231}232});233234return result;235});236});237}238};239240exports.ModuleReader = ModuleReader;241242function warnMissingModule(id) {243// A missing module may be a false positive and therefore does not warrant244// a fatal error, but a warning is certainly in order.245util.log.err(246"unable to resolve module " + JSON.stringify(id) + "; false positive?",247"yellow");248249// Missing modules are installed as if they existed, but it's a run-time250// error if one is ever actually required.251var message = "nonexistent module required: " + id;252return "throw new Error(" + JSON.stringify(message) + ");";253}254255function Module(reader, id, hash, output) {256assert.ok(this instanceof Module);257assert.ok(reader instanceof ModuleReader);258assert.strictEqual(typeof output, "object");259260var source = output[".js"];261assert.strictEqual(typeof source, "string");262263Object.defineProperties(this, {264reader: { value: reader },265id: { value: id },266hash: { value: hash }, // TODO Remove?267deps: { value: getRequiredIDs(id, source) },268source: { value: source },269output: { value: output }270});271}272273Module.prototype = {274getRequiredP: function() {275return this.reader.readMultiP(this.deps);276},277278writeVersionP: function(outputDir) {279var id = this.id;280var hash = this.hash;281var output = this.output;282var cacheDir = this.reader.context.cacheDir;283var charset = this.reader.context.options.outputCharset;284285return Q.all(Object.keys(output).map(function(key) {286var outputFile = path.join(outputDir, id + key);287288function writeCopy() {289if (charset) {290fs.writeFileSync(outputFile, iconv.encode(output[key], charset));291} else {292fs.writeFileSync(outputFile, output[key], "utf8");293}294return outputFile;295}296297if (cacheDir) {298var cacheFile = path.join(cacheDir, hash + key);299return util.linkP(cacheFile, outputFile)300// If the hard linking fails, the cache directory301// might be on a different device, so fall back to302// writing a copy of the file (slightly slower).303.catch(writeCopy);304}305306return util.mkdirP(path.dirname(outputFile)).then(writeCopy);307}));308},309310toString: function() {311return "Module(" + JSON.stringify(this.id) + ")";312},313314resolveId: function(id) {315return util.absolutize(this.id, id);316}317};318319320