示例#1
0
    /**
     * Load extensions.
     *
     * @param {?Array.<string>} A list containing references to extension source
     *      location. A source location may be either (a) a folder name inside
     *      src/extensions or (b) an absolute path.
     * @return {!$.Promise} A promise object that is resolved when all extensions complete loading.
     */
    function init(paths) {
        var params = new UrlParams();

        if (_init) {
            // Only init once. Return a resolved promise.
            return new $.Deferred().resolve().promise();
        }

        if (!paths) {
            params.parse();

            if (params.get("reloadWithoutUserExts") === "true") {
                paths = ["default"];
            } else {
                paths = [
                    getDefaultExtensionPath(),
                    "dev",
                    getUserExtensionPath()
                ];
            }
        }

        // Load extensions before restoring the project

        // Get a Directory for the user extension directory and create it if it doesn't exist.
        // Note that this is an async call and there are no success or failure functions passed
        // in. If the directory *doesn't* exist, it will be created. Extension loading may happen
        // before the directory is finished being created, but that is okay, since the extension
        // loading will work correctly without this directory.
        // If the directory *does* exist, nothing else needs to be done. It will be scanned normally
        // during extension loading.
        var extensionPath = getUserExtensionPath();
        FileSystem.getDirectoryForPath(extensionPath).create();

        // Create the extensions/disabled directory, too.
        var disabledExtensionPath = extensionPath.replace(/\/user$/, "/disabled");
        FileSystem.getDirectoryForPath(disabledExtensionPath).create();

        var promise = Async.doSequentially(paths, function (item) {
            var extensionPath = item;

            // If the item has "/" in it, assume it is a full path. Otherwise, load
            // from our source path + "/extensions/".
            if (item.indexOf("/") === -1) {
                extensionPath = FileUtils.getNativeBracketsDirectoryPath() + "/extensions/" + item;
            }

            return loadAllExtensionsInNativeDirectory(extensionPath);
        }, false);

        promise.always(function () {
            _init = true;
        });

        return promise;
    }
示例#2
0
        FileSystem.resolve(path, function (err) {
            if (!err) {
                // Item already exists, fail with error
                d.reject(FileSystemError.ALREADY_EXISTS);
            } else {
                if (isFolder) {
                    var directory = FileSystem.getDirectoryForPath(path);

                    directory.create(function (err) {
                        if (err) {
                            d.reject(err);
                        } else {
                            d.resolve(directory);
                        }
                    });
                } else {
                    // Create an empty file
                    var file = FileSystem.getFileForPath(path);

                    FileUtils.writeText(file, "").then(function () {
                        d.resolve(file);
                    }, d.reject);
                }
            }
        });
示例#3
0
 /**
  * @private
  *
  * Gets the FileSystem object (either a File or Directory) based on the path provided.
  *
  * @param {string} path Path to retrieve
  */
 function _getFSObject(path) {
     if (!path) {
         return path;
     } else if (_pathIsFile(path)) {
         return FileSystem.getFileForPath(path);
     }
     return FileSystem.getDirectoryForPath(path);
 }
示例#4
0
 runs(function () {
     var dir = FileSystem.getDirectoryForPath(getTempDirectory()).create(function (err) {
         if (err && err !== FileSystemError.ALREADY_EXISTS) {
             deferred.reject(err);
         } else {
             deferred.resolve();
         }
     });
 });
示例#5
0
 ProjectModel.prototype._getDirectoryContents = function (path) {
     var d = new $.Deferred();
     FileSystem.getDirectoryForPath(path).getContents(function (err, contents) {
         if (err) {
             d.reject(err);
         } else {
             d.resolve(contents);
         }
     });
     return d.promise();
 };
示例#6
0
    /**
     * Rename a file/folder. This will update the project tree data structures
     * and send notifications about the rename.
     *
     * @param {string} oldName Old item name
     * @param {string} newName New item name
     * @param {boolean} isFolder True if item is a folder; False if it is a file.
     * @return {$.Promise} A promise object that will be resolved or rejected when
     *   the rename is finished.
     */
    function _renameItem(oldName, newName, isFolder) {
        var result = new $.Deferred();

        if (oldName === newName) {
            result.resolve();
        } else if (!isValidFilename(FileUtils.getBaseName(newName), _invalidChars)) {
            result.reject(ERROR_INVALID_FILENAME);
        } else {
            var entry = isFolder ? FileSystem.getDirectoryForPath(oldName) : FileSystem.getFileForPath(oldName);
            entry.rename(newName, function (err) {
                if (err) {
                    result.reject(err);
                } else {
                    result.resolve();
                }
            });
        }

        return result.promise();
    }
示例#7
0
    /**
     * @private
     * Loads a file entryPoint from each extension folder within the baseUrl into its own Require.js context
     *
     * @param {!string} directory, an absolute native path that contains a directory of extensions.
     *                  each subdirectory is interpreted as an independent extension
     * @param {!{baseUrl: string}} config object with baseUrl property containing absolute path of extension folder
     * @param {!string} entryPoint Module name to load (without .js suffix)
     * @param {function} processExtension 
     * @return {!$.Promise} A promise object that is resolved when all extensions complete loading.
     */
    function _loadAll(directory, config, entryPoint, processExtension) {
        var result = new $.Deferred();
        
        FileSystem.getDirectoryForPath(directory).getContents(function (err, contents) {
            if (!err) {
                var i,
                    extensions = [];
                
                for (i = 0; i < contents.length; i++) {
                    if (contents[i].isDirectory) {
                        // FUTURE (JRB): read package.json instead of just using the entrypoint "main".
                        // Also, load sub-extensions defined in package.json.
                        extensions.push(contents[i].name);
                    }
                }

                if (extensions.length === 0) {
                    result.resolve();
                    return;
                }
                
                Async.doInParallel(extensions, function (item) {
                    var extConfig = {
                        baseUrl: config.baseUrl + "/" + item,
                        paths: config.paths
                    };
                    return processExtension(item, extConfig, entryPoint);
                }).always(function () {
                    // Always resolve the promise even if some extensions had errors
                    result.resolve();
                });
            } else {
                console.error("[Extension] Error -- could not read native directory: " + directory);
                result.reject();
            }
        });
               
        return result.promise();
    }
示例#8
0
 NativeFileSystem.DirectoryEntry = function (fullPath) {
     _warn("new NativeFileSystem.DirectoryEntry()", "FileSystem.getDirectoryForPath()");
     return FileSystem.getDirectoryForPath(fullPath);
 };
示例#9
0
    /**
     * @private
     * Find valid extensions in specified path
     * @param {string} dirPath Directory with extensions
     * @param {Object} autoExtensions Object that maps names of previously auto-installed
     *      extensions {string} to installed version {string}.
     * @return {$.Promise} Promise that resolves with arrays for extensions to update and install
     */
    function _getAutoInstallFiles(dirPath, autoExtensions) {
        var zipFiles    = [],
            installZips = [],
            updateZips  = [],
            deferred    = new $.Deferred();

        FileSystem.getDirectoryForPath(dirPath).getContents(function (err, contents) {
            if (!err) {
                zipFiles = contents.filter(function (dirItem) {
                    return (dirItem.isFile && FileUtils.getFileExtension(dirItem.fullPath) === "zip");
                });
            }

            // Parse zip files and separate new installs vs. updates
            Async.doInParallel_aggregateErrors(zipFiles, function (file) {
                var zipFilePromise = new $.Deferred();

                // Call validate() so that we open the local zip file and parse the
                // package.json. We need the name to detect if this zip will be a
                // new install or an update.
                Package.validate(file.fullPath, { requirePackageJSON: true }).done(function (info) {
                    if (info.errors.length) {
                        zipFilePromise.reject(Package.formatError(info.errors));
                        return;
                    }

                    var extensionInfo, installedVersion, zipArray, existingItem,
                        extensionName   = info.metadata.name,
                        autoExtVersion  = autoExtensions[extensionName];

                    // Verify extension has not already been auto-installed/updated
                    if (autoExtVersion && semver.lte(info.metadata.version, autoExtVersion)) {
                        // Have already auto installed/updated version >= version of this extension
                        zipFilePromise.reject();
                        return;
                    }

                    // Verify extension has not already been installed/updated by some other means
                    extensionInfo = extensions[extensionName];
                    installedVersion = extensionInfo && extensionInfo.installInfo && extensionInfo.installInfo.metadata.version;
                    if (installedVersion && semver.lte(info.metadata.version, installedVersion)) {
                        // Have already manually installed/updated version >= version of this extension
                        zipFilePromise.reject();
                        return;
                    }

                    // Update appropriate zip array. There could be multiple zip files for an
                    // extension, so make sure only the latest is stored
                    zipArray = (installedVersion) ? updateZips : installZips;
                    zipArray.some(function (zip) {
                        if (zip.info.metadata.name === extensionName) {
                            existingItem = zip;
                            return true;
                        }
                        return false;
                    });
                    if (existingItem) {
                        if (semver.lt(existingItem.info.metadata.version, info.metadata.version)) {
                            existingItem.file = file;
                            existingItem.info = info;
                        }
                    } else {
                        zipArray.push({ file: file, info: info });
                    }

                    zipFilePromise.resolve();
                }).fail(function (err) {
                    zipFilePromise.reject(Package.formatError(err));
                });

                return zipFilePromise.promise();
            }).fail(function (errorArray) {
                // Async.doInParallel() fails if some are successful, so write errors
                // to console and always resolve
                errorArray.forEach(function (errorObj) {
                    // If we rejected without an error argument, it means it was no problem
                    // (e.g. same version of extension is already installed)
                    if (errorObj.error) {
                        if (errorObj.error.forEach) {
                            console.error("Errors for", errorObj.item);
                            errorObj.error.forEach(function (error) {
                                console.error(Package.formatError(error));
                            });
                        } else {
                            console.error("Error for", errorObj.item, errorObj);
                        }
                    }
                });
            }).always(function () {
                deferred.resolve({
                    installZips: installZips,
                    updateZips:  updateZips
                });
            });
        });

        return deferred.promise();
    }
示例#10
0
    /**
     * Load extensions.
     *
     * @param {?Array.<string>} A list containing references to extension source
     *      location. A source location may be either (a) a folder name inside
     *      src/extensions or (b) an absolute path.
     * @return {!$.Promise} A promise object that is resolved when all extensions complete loading.
     */
    function init(paths) {
        var params = new UrlParams();
        
        if (_init) {
            // Only init once. Return a resolved promise.
            return new $.Deferred().resolve().promise();
        }
        
        // Load *subset* of the usual builtin extensions list, and don't try to find any user/dev extensions
        if (brackets.inBrowser) {
            var basePath = PathUtils.directory(window.location.href) + "extensions/default/",
                defaultExtensions = [
                    // Core extensions we want to support in the browser
                    "CSSCodeHints",
                    "HTMLCodeHints",
                    "HtmlEntityCodeHints",
                    "InlineColorEditor",
                    "JavaScriptQuickEdit",
                    "JSLint",
                    "LESSSupport",
                    "QuickOpenCSS",
                    "QuickOpenHTML",
                    "QuickOpenJavaScript",
                    "QuickView",
                    "RecentProjects",
                    "UrlCodeHints",
                    "WebPlatformDocs",

                    // Custom extensions we want loaded by default
                    // NOTE: Maps to a folder inside /src/extensions/default/
                    "makedrive-sync-icon"

                    // "ExampleExtension",
                ];

            return Async.doInParallel(defaultExtensions, function (item) {
                var extConfig = {
                    baseUrl: basePath + item
                };
                return loadExtension(item, extConfig, "main");
            });
        }

        
        if (!paths) {
            params.parse();
            
            if (params.get("reloadWithoutUserExts") === "true") {
                paths = ["default"];
            } else {
                paths = ["default", "dev", getUserExtensionPath()];
            }
        }
        
        // Load extensions before restoring the project
        
        // Get a Directory for the user extension directory and create it if it doesn't exist.
        // Note that this is an async call and there are no success or failure functions passed
        // in. If the directory *doesn't* exist, it will be created. Extension loading may happen
        // before the directory is finished being created, but that is okay, since the extension
        // loading will work correctly without this directory.
        // If the directory *does* exist, nothing else needs to be done. It will be scanned normally
        // during extension loading.
        var extensionPath = getUserExtensionPath();
        FileSystem.getDirectoryForPath(extensionPath).create();
        
        // Create the extensions/disabled directory, too.
        var disabledExtensionPath = extensionPath.replace(/\/user$/, "/disabled");
        FileSystem.getDirectoryForPath(disabledExtensionPath).create();
        
        var promise = Async.doSequentially(paths, function (item) {
            var extensionPath = item;
            
            // If the item has "/" in it, assume it is a full path. Otherwise, load
            // from our source path + "/extensions/".
            if (item.indexOf("/") === -1) {
                extensionPath = FileUtils.getNativeBracketsDirectoryPath() + "/extensions/" + item;
            }
            
            return loadAllExtensionsInNativeDirectory(extensionPath);
        }, false);
        
        promise.always(function () {
            _init = true;
        });
        
        return promise;
    }
示例#11
0
    function _autoInstallBundles() {
        // Get list of extension bundles
        var validatePromise,
            dirPath     = FileUtils.getDirectoryPath(FileUtils.getNativeBracketsDirectoryPath()) + FOLDER_AUTOINSTALL + "/",
            bundles     = [],
            installZips = [],
            updateZips  = [],
            deferred    = new $.Deferred();

        FileSystem.getDirectoryForPath(dirPath).getContents(function (err, contents) {
            var autoExtensions = PreferencesManager.getViewState(FOLDER_AUTOINSTALL) || {},
                autoExtDirty   = false;

            if (!err) {
                bundles = contents.filter(function (dirItem) {
                    return (dirItem.isFile && FileUtils.getFileExtension(dirItem.fullPath) === "zip");
                });
            }

            // Parse zip files and separate new installs vs. updates
            validatePromise = Async.doInParallel(bundles, function (file) {
                var result = new $.Deferred();

                // Call validate() so that we open the local zip file and parse the
                // package.json. We need the name to detect if this zip will be a
                // new install or an update.
                Package.validate(file.fullPath, { requirePackageJSON: true }).done(function (info) {
                    if (info.errors.length) {
                        result.reject(Package.formatError(info.errors));
                        return;
                    }

                    var extensionInfo, installedVersion,
                        extensionName   = info.metadata.name,
                        autoExtVersion  = autoExtensions && autoExtensions[extensionName];

                    // Verify extension has not already been auto-installed/updated
                    if (autoExtVersion && semver.lte(info.metadata.version, autoExtVersion)) {
                        // Have already installed/updated version >= version of this extension
                        result.reject();
                        return;
                    }

                    // Verify extension has not already been installed/updated by some other means
                    extensionInfo = extensions[extensionName];
                    installedVersion = extensionInfo && extensionInfo.installInfo && extensionInfo.installInfo.metadata.version;
                    if (installedVersion && semver.lte(info.metadata.version, installedVersion)) {
                        // Have already installed/updated version >= version of this extension
                        result.reject();
                        return;
                    }

                    // Keep track of auto-installed extensions so we only install an extension once
                    autoExtensions[extensionName] = info.metadata.version;
                    autoExtDirty = true;

                    if (installedVersion) {
                        updateZips.push(file);
                    } else {
                        installZips.push(file);
                    }

                    result.resolve();
                }).fail(function (err) {
                    result.reject(Package.formatError(err));
                });

                return result.promise();
            });

            validatePromise.done(function () {
                var installPromise = Async.doSequentially(installZips, function (file) {
                    return Package.installFromPath(file.fullPath);
                });

                var updatePromise = installPromise.always(function () {
                    return Async.doSequentially(updateZips, function (file) {
                        return Package.installUpdate(file.fullPath);
                    });
                });

                // Always resolve the outer promise
                updatePromise.always(deferred.resolve);
            }).fail(function (errorArray) {
                deferred.reject(errorArray);
            }).always(function () {
                if (autoExtDirty) {
                    // Store info in prefs
                    PreferencesManager.setViewState(FOLDER_AUTOINSTALL, autoExtensions);
                }
            });
        });

        return deferred.promise();
    }
示例#12
0
 /**
  * Copy a directory source to a destination
  * @param {!Directory} source Directory for the source directory to copy
  * @param {!string} destination Destination path to copy the source directory
  * @param {?{parseOffsets:boolean, infos:Object, removePrefix:boolean}}} options
  *     parseOffsets - allows optional offset markup parsing. File is written to the
  *       destination path without offsets. Offset data is passed to the
  *       doneCallbacks of the promise.
  *     infos - an optional Object used when parseOffsets is true. Offset
  *       information is attached here, indexed by the file destination path.
  *     removePrefix - When parseOffsets is true, set removePrefix true
  *       to add a new key to the infos array that drops the destination
  *       path root.
  * @return {$.Promise} A promise resolved when the directory and all it's
  *     contents are copied to the destination or rejected immediately
  *     upon the first error.
  */
 function copyDirectoryEntry(source, destination, options) {
     options = options || {};
     options.infos = options.infos || {};
     
     var parseOffsets    = options.parseOffsets || false,
         removePrefix    = options.removePrefix || true,
         deferred        = new $.Deferred(),
         destDir         = FileSystem.getDirectoryForPath(destination);
     
     // create the destination folder
     destDir.create(function (err) {
         if (err && err !== FileSystemError.ALREADY_EXISTS) {
             deferred.reject();
             return;
         }
         
         source.getContents(function (err, contents) {
             if (!err) {
                 // copy all children of this directory
                 var copyChildrenPromise = Async.doInParallel(
                     contents,
                     function copyChild(child) {
                         var childDestination = destination + "/" + child.name,
                             promise;
                         
                         if (child.isDirectory) {
                             promise = copyDirectoryEntry(child, childDestination, options);
                         } else {
                             promise = copyFileEntry(child, childDestination, options);
                             
                             if (parseOffsets) {
                                 // save offset data for each file path
                                 promise.done(function (destinationEntry, offsets, text) {
                                     options.infos[childDestination] = {
                                         offsets     : offsets,
                                         fileEntry   : destinationEntry,
                                         text        : text
                                     };
                                 });
                             }
                         }
                         
                         return promise;
                     }
                 );
                 
                 copyChildrenPromise.then(deferred.resolve, deferred.reject);
             } else {
                 deferred.reject(err);
             }
         });
     });
     
     deferred.always(function () {
         // remove destination path prefix
         if (removePrefix && options.infos) {
             var shortKey;
             Object.keys(options.infos).forEach(function (key) {
                 shortKey = key.substr(destination.length + 1);
                 options.infos[shortKey] = options.infos[key];
             });
         }
     });
     
     return deferred.promise();
 }