lib/util/fsTools.js

lib/ AssetGraph.js errors.js index.js query.js
assets/ Asset.js Atom.js CacheManifest.js CoffeeScript.js Css.js Flash.js Gif.js Htc.js Html.js I18n.js Ico.js Image.js JavaScript.js Jpeg.js Json.js KnockoutJsTemplate.js Less.js Png.js Rss.js StaticUrlMap.js Stylus.js Text.js Xml.js index.js
relations/ CacheManifestEntry.js CssAlphaImageLoader.js CssBehavior.js CssFontFaceSrc.js CssImage.js CssImport.js CssUrlTokenRelation.js HtmlAlternateLink.js HtmlAnchor.js HtmlAppleTouchStartupImage.js HtmlApplet.js HtmlAudio.js HtmlCacheManifest.js HtmlConditionalComment.js HtmlDataBindAttribute.js HtmlEdgeSideInclude.js HtmlEmbed.js HtmlFrame.js HtmlIFrame.js HtmlIFrameSrcDoc.js HtmlImage.js HtmlInlineScriptTemplate.js HtmlKnockoutContainerless.js HtmlObject.js HtmlRelation.js HtmlRequireJsMain.js HtmlScript.js HtmlShortcutIcon.js HtmlStyle.js HtmlStyleAttribute.js HtmlVideo.js HtmlVideoPoster.js JavaScriptAmdDefine.js JavaScriptAmdRequire.js JavaScriptCommonJsRequire.js JavaScriptExtJsRequire.js JavaScriptGetStaticUrl.js JavaScriptGetText.js JavaScriptInclude.js JavaScriptShimRequire.js JavaScriptTrHtml.js Relation.js StaticUrlMapEntry.js index.js
resolvers/ data.js extJs4Dir.js file.js fixedDirectory.js http.js index.js javascript.js
transforms/ addCacheManifest.js bundleRelations.js bundleRequireJs.js compileCoffeeScriptToJavaScript.js compileLessToCss.js compileStylusToCss.js compressJavaScript.js convertCssImportsToHtmlStyles.js convertHtmlStylesToInlineCssImports.js convertStylesheetsToInlineStyles.js drawGraph.js executeJavaScriptInOrder.js externalizeRelations.js flattenStaticIncludes.js inlineCssImagesWithLegacyFallback.js inlineRelations.js loadAssets.js mergeIdenticalAssets.js minifyAssets.js moveAssets.js moveAssetsInOrder.js populate.js prettyPrintAssets.js pullGlobalsIntoVariables.js registerRequireJsConfig.js removeAssets.js removeRelations.js setAssetContentType.js setAssetEncoding.js setAssetExtension.js setHtmlImageDimensions.js startOverIfAssetSourceFilesChange.js writeAssetsToDisc.js writeAssetsToStdout.js writeStatsToStderr.js
util/ deepCopy.js extendWithGettersAndSetters.js fsTools.js getImageInfoFromBuffers.js memoizeAsyncAccessor.js uniqueId.js urlTools.js
var fs = require('fs'),
    Path = require('path'),
    constants = process.ENOENT ? process : require('constants'),
    _ = require('underscore'),
    seq = require('seq'),
    passError = require('passerror'),
    fsTools = {};

fsTools.mkpath = function mkpath(dir, permissions, cb) {
    if (typeof permissions === 'function') {
        cb = permissions;
        permissions = parseInt('0777', 8); // Stupid JSLint
    }
    fs.mkdir(dir, permissions, function (err) {
        if (err && (err.code === 'EEXIST' || err.errno === constants.EEXIST)) {
            // Success!
            return cb();
        }
        if (err && (err.code === 'ENOENT' || err.errno === constants.ENOENT)) {
            var parentDir = Path.normalize(dir + "/..");
            if (parentDir !== '/' && parentDir !== '') {
                fsTools.mkpath(parentDir, permissions, passError(cb, function () {
                    fs.mkdir(dir, permissions, function (err) {
                        if (!err || err.code === 'EEXIST' || err.errno === constants.EEXIST) {
                            // Success!
                            return cb();
                        }
                        cb(err);
                    });
                }));
                return;
            }
        }
        cb(err);
    });
};

fsTools.mkpathAndWriteFile = function mkpathAndWriteFile(fileName, contents, encoding, cb) {
    fs.writeFile(fileName, contents, encoding, function (err) {
        if (err && (err.code === 'ENOENT' || err.errno === constants.ENOENT)) {
            fsTools.mkpath(Path.dirname(fileName), passError(cb, function () {
                fs.writeFile(fileName, contents, encoding, cb);
            }));
        } else {
            cb(err);
        }
    });
};

// FIXME: Make flushable
var dirExistsCache = {},
    dirExistsWaitingQueue = {};

fsTools.dirExistsCached = function dirExistsCached(fsPath, cb) {
    if (fsPath in dirExistsCache) {
        process.nextTick(function () {
            cb(null, dirExistsCache[fsPath]);
        });
    } else if (fsPath in dirExistsWaitingQueue) {
        dirExistsWaitingQueue[fsPath].push(cb);
    } else {
        dirExistsWaitingQueue[fsPath] = [cb];
        fs.stat(fsPath, function (err, stats) {
            var isDirectory = !err && stats.isDirectory();
            dirExistsCache[fsPath] = isDirectory;
            dirExistsWaitingQueue[fsPath].forEach(function (waitingCallback) {
                waitingCallback(null, isDirectory);
            });
            delete dirExistsWaitingQueue[fsPath];
        });
    }
};

fsTools.findParentDirCached = function findParentDirCached(fromPath, parentDirName, cb) {
    var candidatePaths = [],
        fromPathFragments = fromPath.replace(/\/$/, "").split("/");

    seq(fromPathFragments)
        .parMap(function (fromPathFragment, i) {
            // FIXME: Stop at caller's definition of root?
            var candidatePath = fromPathFragments.slice(0, i+1).concat(parentDirName).join("/");
            candidatePaths.push(candidatePath);
            fsTools.dirExistsCached(candidatePath, this);
        })
        .unflatten()
        .seq(function (dirExistsResults) {
            var bestCandidateIndex = dirExistsResults.lastIndexOf(true);
            if (bestCandidateIndex === -1) {
                return cb(new Error("fsTools.findParentDirCached: Couldn't find a parent dir named " + parentDirName + " from " + fromPath));
            }
            cb(null, candidatePaths[bestCandidateIndex]);
        })
        ['catch'](cb);
};

_.extend(exports, fsTools);