UNPKG

atom-nuclide

Version:

A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.

370 lines (320 loc) 14.7 kB
Object.defineProperty(exports, '__esModule', { value: true }); /* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { var callNext = step.bind(null, 'next'); var callThrow = step.bind(null, 'throw'); function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(callNext, callThrow); } } callNext(); }); }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _assert2; function _assert() { return _assert2 = _interopRequireDefault(require('assert')); } var _commonsNodeStream2; function _commonsNodeStream() { return _commonsNodeStream2 = require('../../commons-node/stream'); } var _ServerConnection2; function _ServerConnection() { return _ServerConnection2 = require('./ServerConnection'); } var _atom2; function _atom() { return _atom2 = require('atom'); } var _commonsNodeNuclideUri2; function _commonsNodeNuclideUri() { return _commonsNodeNuclideUri2 = _interopRequireDefault(require('../../commons-node/nuclideUri')); } var _RemoteConnectionConfigurationManager2; function _RemoteConnectionConfigurationManager() { return _RemoteConnectionConfigurationManager2 = require('./RemoteConnectionConfigurationManager'); } var _nuclideLogging2; function _nuclideLogging() { return _nuclideLogging2 = require('../../nuclide-logging'); } var logger = (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)(); var FILE_WATCHER_SERVICE = 'FileWatcherService'; var FILE_SYSTEM_SERVICE = 'FileSystemService'; // key for https connection. // A RemoteConnection represents a directory which has been opened in Nuclide on a remote machine. // This corresponds to what atom calls a 'root path' in a project. // // TODO: The _entries and _hgRepositoryDescription should not be here. // Nuclide behaves badly when remote directories are opened which are parent/child of each other. // And there needn't be a 1:1 relationship between RemoteConnections and hg repos. var RemoteConnection = (function () { _createClass(RemoteConnection, null, [{ key: 'findOrCreate', value: _asyncToGenerator(function* (config) { var serverConnection = yield (_ServerConnection2 || _ServerConnection()).ServerConnection.getOrCreate(config); var connection = new RemoteConnection(serverConnection, config.cwd, config.displayTitle); return yield connection._initialize(); }) // Do NOT call this directly. Use findOrCreate instead. }, { key: '_emitter', value: new (_atom2 || _atom()).Emitter(), enumerable: true }]); function RemoteConnection(connection, cwd, displayTitle) { _classCallCheck(this, RemoteConnection); this._cwd = cwd; this._subscriptions = new (_atom2 || _atom()).CompositeDisposable(); this._hgRepositoryDescription = null; this._connection = connection; this._displayTitle = displayTitle; } _createClass(RemoteConnection, [{ key: '_setHgRepoInfo', // A workaround before Atom 2.0: Atom's Project::setPaths currently uses // ::repositoryForDirectorySync, so we need the repo information to already be // available when the new path is added. t6913624 tracks cleanup of this. value: _asyncToGenerator(function* () { var remotePath = this.getPathForInitialWorkingDirectory(); var _ref = this.getService('SourceControlService'); var getHgRepository = _ref.getHgRepository; this._setHgRepositoryDescription((yield getHgRepository(remotePath))); }) }, { key: 'getUriOfRemotePath', value: function getUriOfRemotePath(remotePath) { return 'nuclide://' + this.getRemoteHostname() + remotePath; } }, { key: 'getPathOfUri', value: function getPathOfUri(uri) { return (_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.parse(uri).path; } }, { key: 'createDirectory', value: function createDirectory(uri) { var symlink = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; return this._connection.createDirectory(uri, this._hgRepositoryDescription, symlink); } // A workaround before Atom 2.0: see ::getHgRepoInfo of main.js. }, { key: '_setHgRepositoryDescription', value: function _setHgRepositoryDescription(hgRepositoryDescription) { this._hgRepositoryDescription = hgRepositoryDescription; } }, { key: 'getHgRepositoryDescription', value: function getHgRepositoryDescription() { return this._hgRepositoryDescription; } }, { key: 'createFile', value: function createFile(uri) { var symlink = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; return this._connection.createFile(uri, symlink); } }, { key: '_initialize', value: _asyncToGenerator(function* () { var attemptShutdown = false; // Must add first to prevent the ServerConnection from going away // in a possible race. this._connection.addConnection(this); try { var FileSystemService = this.getService(FILE_SYSTEM_SERVICE); var resolvedPath = yield FileSystemService.resolveRealPath(this._cwd); // Now that we know the real path, it's possible this collides with an existing connection. // If so, we should just stop immediately. if (resolvedPath !== this._cwd) { var existingConnection = RemoteConnection.getByHostnameAndPath(this.getRemoteHostname(), resolvedPath); (0, (_assert2 || _assert()).default)(this !== existingConnection); if (existingConnection != null) { this.close(attemptShutdown); return existingConnection; } this._cwd = resolvedPath; } // A workaround before Atom 2.0: see ::getHgRepoInfo. yield this._setHgRepoInfo(); RemoteConnection._emitter.emit('did-add', this); this._watchRootProjectDirectory(); } catch (e) { this.close(attemptShutdown); throw e; } return this; }) }, { key: '_watchRootProjectDirectory', value: function _watchRootProjectDirectory() { var _this = this; var rootDirectoryUri = this.getUriForInitialWorkingDirectory(); var rootDirectotyPath = this.getPathForInitialWorkingDirectory(); var FileWatcherService = this.getService(FILE_WATCHER_SERVICE); (0, (_assert2 || _assert()).default)(FileWatcherService); var watchDirectoryRecursive = FileWatcherService.watchDirectoryRecursive; // Start watching the project for changes and initialize the root watcher // for next calls to `watchFile` and `watchDirectory`. var watchStream = watchDirectoryRecursive(rootDirectoryUri).refCount(); var subscription = watchStream.subscribe(function (watchUpdate) { // Nothing needs to be done if the root directory was watched correctly. // Let's just console log it anyway. logger.info('Watcher Features Initialized for project: ' + rootDirectoryUri, watchUpdate); }, _asyncToGenerator(function* (error) { var warningMessageToUser = 'You just connected to a remote project ' + ('`' + rootDirectotyPath + '` but we recommend you remove this directory now ') + 'because crucial features like synced remote file editing, file search, ' + 'and Mercurial-related updates will not work.<br/>'; var loggedErrorMessage = error.message || error; logger.error('Watcher failed to start - watcher features disabled! Error: ' + loggedErrorMessage); var FileSystemService = _this.getService(FILE_SYSTEM_SERVICE); if (yield FileSystemService.isNfs(rootDirectotyPath)) { warningMessageToUser += 'This project directory: `' + rootDirectotyPath + '` is on <b>`NFS`</b> filesystem. ' + 'Nuclide works best with local (non-NFS) root directory.' + 'e.g. `/data/users/$USER`'; } else { warningMessageToUser += '<b><a href="https://facebook.github.io/watchman/">Watchman</a> Error:</b>' + loggedErrorMessage; } // Add a persistent warning message to make sure the user sees it before dismissing. atom.notifications.addWarning(warningMessageToUser, { dismissable: true }); }), function () { // Nothing needs to be done if the root directory watch has ended. logger.info('Watcher Features Ended for project: ' + rootDirectoryUri); }); this._subscriptions.add(new (_commonsNodeStream2 || _commonsNodeStream()).DisposableSubscription(subscription)); } }, { key: 'close', value: _asyncToGenerator(function* (shutdownIfLast) { this._subscriptions.dispose(); yield this._connection.removeConnection(this, shutdownIfLast); RemoteConnection._emitter.emit('did-close', this); }) }, { key: 'getConnection', value: function getConnection() { return this._connection; } }, { key: 'getPort', value: function getPort() { return this._connection.getPort(); } }, { key: 'getRemoteHostname', value: function getRemoteHostname() { return this._connection.getRemoteHostname(); } }, { key: 'getDisplayTitle', value: function getDisplayTitle() { return this._displayTitle; } }, { key: 'getUriForInitialWorkingDirectory', value: function getUriForInitialWorkingDirectory() { return this.getUriOfRemotePath(this.getPathForInitialWorkingDirectory()); } }, { key: 'getPathForInitialWorkingDirectory', value: function getPathForInitialWorkingDirectory() { return this._cwd; } }, { key: 'getConfig', value: function getConfig() { return _extends({}, this._connection.getConfig(), { cwd: this._cwd, displayTitle: this._displayTitle }); } }, { key: 'getService', value: function getService(serviceName) { return this._connection.getService(serviceName); } }, { key: 'isOnlyConnection', value: function isOnlyConnection() { return this._connection.getConnections().length === 1; } }], [{ key: '_createInsecureConnectionForTesting', value: function _createInsecureConnectionForTesting(cwd, port) { var config = { host: 'localhost', port: port, cwd: cwd, displayTitle: '' }; return RemoteConnection.findOrCreate(config); } /** * Create a connection by reusing the configuration of last successful connection associated with * given host. If the server's certs has been updated or there is no previous successful * connection, null (resolved by promise) is returned. */ }, { key: 'createConnectionBySavedConfig', value: _asyncToGenerator(function* (host, cwd, displayTitle) { var connectionConfig = (0, (_RemoteConnectionConfigurationManager2 || _RemoteConnectionConfigurationManager()).getConnectionConfig)(host); if (!connectionConfig) { return null; } try { var config = _extends({}, connectionConfig, { cwd: cwd, displayTitle: displayTitle }); return yield RemoteConnection.findOrCreate(config); } catch (e) { var log = e.name === 'VersionMismatchError' ? logger.warn : logger.error; log('Failed to reuse connectionConfiguration for ' + host, e); return null; } }) }, { key: 'onDidAddRemoteConnection', value: function onDidAddRemoteConnection(handler) { return RemoteConnection._emitter.on('did-add', handler); } }, { key: 'onDidCloseRemoteConnection', value: function onDidCloseRemoteConnection(handler) { return RemoteConnection._emitter.on('did-close', handler); } }, { key: 'getForUri', value: function getForUri(uri) { var _default$parse = (_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.parse(uri); var hostname = _default$parse.hostname; var path = _default$parse.path; if (hostname == null) { return null; } return RemoteConnection.getByHostnameAndPath(hostname, path); } /** * Get cached connection match the hostname and the path has the prefix of connection.cwd. * @param hostname The connected server host name. * @param path The absolute path that's has the prefix of cwd of the connection. * If path is null, empty or undefined, then return the connection which matches * the hostname and ignore the initial working directory. */ }, { key: 'getByHostnameAndPath', value: function getByHostnameAndPath(hostname, path) { return RemoteConnection.getByHostname(hostname).filter(function (connection) { return path.startsWith(connection.getPathForInitialWorkingDirectory()); })[0]; } }, { key: 'getByHostname', value: function getByHostname(hostname) { var server = (_ServerConnection2 || _ServerConnection()).ServerConnection.getByHostname(hostname); return server == null ? [] : server.getConnections(); } }]); return RemoteConnection; })(); exports.RemoteConnection = RemoteConnection; // host nuclide server is running on. // port to connect to. // Path to remote directory user should start in upon connection. // Name of the saved connection profile. // certificate of certificate authority. // client certificate for https connection. // Path to remote directory user should start in upon connection.