src/extensibility/node/ExtensionManagerDomain.js
function _cmdInstall(packagePath, destinationDirectory, options, callback, pCallback, _doUpdate) {
    if (!options || !options.disabledDirectory || !options.apiVersion || !options.systemExtensionDirectory) {
        callback(new Error(Errors.MISSING_REQUIRED_OPTIONS), null);
        return;
    }

    function validateCallback(err, validationResult) {
        validationResult.localPath = packagePath;

        // This is a wrapper for the callback that will delete the temporary
        // directory to which the package was unzipped.
        function deleteTempAndCallback(err) {
            if (validationResult.extractDir) {
                fs.remove(validationResult.extractDir);
                delete validationResult.extractDir;
            }
            callback(err, validationResult);
        }

        // If there was trouble at the validation stage, we stop right away.
        if (err || validationResult.errors.length > 0) {
            validationResult.installationStatus = Statuses.FAILED;
            deleteTempAndCallback(err);
            return;
        }

        // Prefers the package.json name field, but will take the zip
        // file's name if that's all that's available.
        var extensionName, guessedName;
        if (options.nameHint) {
            guessedName = path.basename(options.nameHint, ".zip");
        } else {
            guessedName = path.basename(packagePath, ".zip");
        }
        if (validationResult.metadata) {
            extensionName = validationResult.metadata.name;
        } else {
            extensionName = guessedName;
        }

        validationResult.name = extensionName;
        var installDirectory = path.join(destinationDirectory, extensionName),
            legacyDirectory = path.join(destinationDirectory, guessedName),
            systemInstallDirectory = path.join(options.systemExtensionDirectory, extensionName);

        if (validationResult.metadata && validationResult.metadata.engines &&
                validationResult.metadata.engines.brackets) {
            var compatible = semver.satisfies(options.apiVersion,
                                              validationResult.metadata.engines.brackets);
            if (!compatible) {
                installDirectory = path.join(options.disabledDirectory, extensionName);
                validationResult.installationStatus = Statuses.DISABLED;
                validationResult.disabledReason = Errors.API_NOT_COMPATIBLE;
                _removeAndInstall(packagePath, installDirectory, validationResult, deleteTempAndCallback);
                return;
            }
        }

        // The "legacy" stuff should go away after all of the commonly used extensions
        // have been upgraded with package.json files.
        var hasLegacyPackage = validationResult.metadata && legacyPackageCheck(legacyDirectory);

        // If the extension is already there, we signal to the front end that it's already installed
        // unless the front end has signaled an intent to update.
        if (hasLegacyPackage || fs.existsSync(installDirectory) || fs.existsSync(systemInstallDirectory)) {
            if (_doUpdate === true) {
                if (hasLegacyPackage) {
                    // When there's a legacy installed extension, remove it first,
                    // then also remove any new-style directory the user may have.
                    // This helps clean up if the user is in a state where they have
                    // both legacy and new extensions installed.
                    fs.remove(legacyDirectory, function (err) {
                        if (err) {
                            deleteTempAndCallback(err);
                            return;
                        }
                        _removeAndInstall(packagePath, installDirectory, validationResult, deleteTempAndCallback);
                    });
                } else {
                    _removeAndInstall(packagePath, installDirectory, validationResult, deleteTempAndCallback);
                }
            } else if (hasLegacyPackage) {
                validationResult.installationStatus = Statuses.NEEDS_UPDATE;
                validationResult.name = guessedName;
                deleteTempAndCallback(null);
            } else {
                _checkExistingInstallation(validationResult, installDirectory, systemInstallDirectory, deleteTempAndCallback);
            }
        } else {
            // Regular installation with no conflicts.
            validationResult.disabledReason = null;
            _performInstall(packagePath, installDirectory, validationResult, deleteTempAndCallback);
        }
    }

    validate(packagePath, options, validateCallback);
}
function _cmdInstall(packagePath, destinationDirectory, options, callback, pCallback, _doUpdate) {
    if (!options || !options.disabledDirectory || !options.apiVersion || !options.systemExtensionDirectory) {
        callback(new Error(Errors.MISSING_REQUIRED_OPTIONS), null);
        return;
    }

    function validateCallback(err, validationResult) {
        validationResult.localPath = packagePath;

        // This is a wrapper for the callback that will delete the temporary
        // directory to which the package was unzipped.
        function deleteTempAndCallback(err) {
            if (validationResult.extractDir) {
                fs.remove(validationResult.extractDir);
                delete validationResult.extractDir;
            }
            callback(err, validationResult);
        }

        // If there was trouble at the validation stage, we stop right away.
        if (err || validationResult.errors.length > 0) {
            validationResult.installationStatus = Statuses.FAILED;
            deleteTempAndCallback(err);
            return;
        }

        // Prefers the package.json name field, but will take the zip
        // file's name if that's all that's available.
        var extensionName, guessedName;
        if (options.nameHint) {
            guessedName = path.basename(options.nameHint, ".zip");
        } else {
            guessedName = path.basename(packagePath, ".zip");
        }
        if (validationResult.metadata) {
            extensionName = validationResult.metadata.name;
        } else {
            extensionName = guessedName;
        }

        validationResult.name = extensionName;
        var installDirectory = path.join(destinationDirectory, extensionName),
            legacyDirectory = path.join(destinationDirectory, guessedName),
            systemInstallDirectory = path.join(options.systemExtensionDirectory, extensionName);

        if (validationResult.metadata && validationResult.metadata.engines &&
                validationResult.metadata.engines.brackets) {
            var compatible = semver.satisfies(options.apiVersion,
                                              validationResult.metadata.engines.brackets);
            if (!compatible) {
                installDirectory = path.join(options.disabledDirectory, extensionName);
                validationResult.installationStatus = Statuses.DISABLED;
                validationResult.disabledReason = Errors.API_NOT_COMPATIBLE;
                _removeAndInstall(packagePath, installDirectory, validationResult, deleteTempAndCallback);
                return;
            }
        }

        // The "legacy" stuff should go away after all of the commonly used extensions
        // have been upgraded with package.json files.
        var hasLegacyPackage = validationResult.metadata && legacyPackageCheck(legacyDirectory);

        // If the extension is already there, we signal to the front end that it's already installed
        // unless the front end has signaled an intent to update.
        if (hasLegacyPackage || fs.existsSync(installDirectory) || fs.existsSync(systemInstallDirectory)) {
            if (_doUpdate === true) {
                if (hasLegacyPackage) {
                    // When there's a legacy installed extension, remove it first,
                    // then also remove any new-style directory the user may have.
                    // This helps clean up if the user is in a state where they have
                    // both legacy and new extensions installed.
                    fs.remove(legacyDirectory, function (err) {
                        if (err) {
                            deleteTempAndCallback(err);
                            return;
                        }
                        _removeAndInstall(packagePath, installDirectory, validationResult, deleteTempAndCallback);
                    });
                } else {
                    _removeAndInstall(packagePath, installDirectory, validationResult, deleteTempAndCallback);
                }
            } else if (hasLegacyPackage) {
                validationResult.installationStatus = Statuses.NEEDS_UPDATE;
                validationResult.name = guessedName;
                deleteTempAndCallback(null);
            } else {
                _checkExistingInstallation(validationResult, installDirectory, systemInstallDirectory, deleteTempAndCallback);
            }
        } else {
            // Regular installation with no conflicts.
            validationResult.disabledReason = null;
            _performInstall(packagePath, installDirectory, validationResult, deleteTempAndCallback);
        }
    }

    validate(packagePath, options, validateCallback);
}
function _cmdInstall ( packagePath , destinationDirectory , options , callback , pCallback , _doUpdate ) { if ( ! options || ! options . disabledDirectory || ! options . apiVersion || ! options . systemExtensionDirectory ) { callback ( new Error ( Errors . MISSING_REQUIRED_OPTIONS ) , null ) ; return ; } function validateCallback ( err , validationResult ) { validationResult . localPath = packagePath ; // This is a wrapper for the callback that will delete the temporary // directory to which the package was unzipped. function deleteTempAndCallback ( err ) { if ( validationResult . extractDir ) { fs . remove ( validationResult . extractDir ) ; delete validationResult . extractDir ; } callback ( err , validationResult ) ; } // If there was trouble at the validation stage, we stop right away. if ( err || validationResult . errors . length > 0 ) { validationResult . installationStatus = Statuses . FAILED ; deleteTempAndCallback ( err ) ; return ; } // Prefers the package.json name field, but will take the zip // file's name if that's all that's available. var extensionName , guessedName ; if ( options . nameHint ) { guessedName = path . basename ( options . nameHint , ".zip" ) ; } else { guessedName = path . basename ( packagePath , ".zip" ) ; } if ( validationResult . metadata ) { extensionName = validationResult . metadata . name ; } else { extensionName = guessedName ; } validationResult . name = extensionName ; var installDirectory = path . join ( destinationDirectory , extensionName ) , legacyDirectory = path . join ( destinationDirectory , guessedName ) , systemInstallDirectory = path . join ( options . systemExtensionDirectory , extensionName ) ; if ( validationResult . metadata && validationResult . metadata . engines && validationResult . metadata . engines . brackets ) { var compatible = semver . satisfies ( options . apiVersion , validationResult . metadata . engines . brackets ) ; if ( ! compatible ) { installDirectory = path . join ( options . disabledDirectory , extensionName ) ; validationResult . installationStatus = Statuses . DISABLED ; validationResult . disabledReason = Errors . API_NOT_COMPATIBLE ; _removeAndInstall ( packagePath , installDirectory , validationResult , deleteTempAndCallback ) ; return ; } } // The "legacy" stuff should go away after all of the commonly used extensions // have been upgraded with package.json files. var hasLegacyPackage = validationResult . metadata && legacyPackageCheck ( legacyDirectory ) ; // If the extension is already there, we signal to the front end that it's already installed // unless the front end has signaled an intent to update. if ( hasLegacyPackage || fs . existsSync ( installDirectory ) || fs . existsSync ( systemInstallDirectory ) ) { if ( _doUpdate === true ) { if ( hasLegacyPackage ) { // When there's a legacy installed extension, remove it first, // then also remove any new-style directory the user may have. // This helps clean up if the user is in a state where they have // both legacy and new extensions installed. fs . remove ( legacyDirectory , function ( err ) { if ( err ) { deleteTempAndCallback ( err ) ; return ; } _removeAndInstall ( packagePath , installDirectory , validationResult , deleteTempAndCallback ) ; } ) ; } else { _removeAndInstall ( packagePath , installDirectory , validationResult , deleteTempAndCallback ) ; } } else if ( hasLegacyPackage ) { validationResult . installationStatus = Statuses . NEEDS_UPDATE ; validationResult . name = guessedName ; deleteTempAndCallback ( null ) ; } else { _checkExistingInstallation ( validationResult , installDirectory , systemInstallDirectory , deleteTempAndCallback ) ; } } else { // Regular installation with no conflicts. validationResult . disabledReason = null ; _performInstall ( packagePath , installDirectory , validationResult , deleteTempAndCallback ) ; } } validate ( packagePath , options , validateCallback ) ; }
Implements the "install" command in the "extensions" domain.

There is no need to call validate independently. Validation is the first
thing that is done here.

After the extension is validated, it is installed in destinationDirectory
unless the extension is already present there. If it is already present,
a determination is made about whether the package being installed is
an update. If it does appear to be an update, then result.installationStatus
is set to NEEDS_UPDATE. If not, then it's set to ALREADY_INSTALLED.

If the installation succeeds, then result.installationStatus is set to INSTALLED.

The extension is unzipped into a directory in destinationDirectory with
the name of the extension (the name is derived either from package.json
or the name of the zip file).

The destinationDirectory will be created if it does not exist.

@param {string} Absolute path to the package zip file
@param {string} the destination directory
@param {{disabledDirectory: !string, apiVersion: !string, nameHint: ?string,
systemExtensionDirectory: !string}} additional settings to control the installation
@param {function} callback (err, result)
@param {function} pCallback (msg) callback for notifications about operation progress
@param {boolean} _doUpdate  private argument to signal that an update should be performed
Implements the install command in the extensions domain .