particle-commands
Version:
Library of UX-neutral commands that provide key functionality for developer tools
357 lines (301 loc) • 14.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.LibraryInstallCommand = exports.LibraryInstallCommandSite = undefined;
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
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; }; }();
exports.buildAdapters = buildAdapters;
var _command = require('./command');
var _particleLibraryManager = require('particle-library-manager');
var _project_properties = require('./project_properties');
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _library_properties = require('./library_properties');
var _library_properties2 = _interopRequireDefault(_library_properties);
var _library = require('./library');
var _projects = require('./projects');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* Specification and base implementation for the site instance expected by
* the LibraryInstallCommand.
*/
var LibraryInstallCommandSite = exports.LibraryInstallCommandSite = function (_CommandSite) {
_inherits(LibraryInstallCommandSite, _CommandSite);
function LibraryInstallCommandSite() {
_classCallCheck(this, LibraryInstallCommandSite);
return _possibleConstructorReturn(this, (LibraryInstallCommandSite.__proto__ || Object.getPrototypeOf(LibraryInstallCommandSite)).call(this));
}
_createClass(LibraryInstallCommandSite, [{
key: 'apiClient',
value: function apiClient() {
throw new Error('not implemented');
}
// mdm - I'm not sure about having all these methods for accessing simple properties.
// It might be simpler to have a cmd args object with properties. It depends upon if the
// property values need to come from the user, e.g. an interactive prompt for all the values.
}, {
key: 'isVendored',
value: function isVendored() {
return false;
}
}, {
key: 'isAdaptersRequired',
value: function isAdaptersRequired() {
return false;
}
}, {
key: 'libraryName',
value: function libraryName() {
throw Error('not implemented');
}
/**
* The target directory containing the project to install the library into.
*/
}, {
key: 'targetDirectory',
value: function targetDirectory() {
throw Error('not implemented');
}
}, {
key: 'error',
value: function error(err) {
throw err;
}
}, {
key: 'notifyIncorrectLayout',
value: function notifyIncorrectLayout(_actualLayout, _expectedLayout, _libName, _targetDir) {
return Promise.resolve();
}
}, {
key: 'notifyCheckingLibrary',
value: function notifyCheckingLibrary(_libName) {
return Promise.resolve();
}
}, {
key: 'notifyFetchingLibrary',
value: function notifyFetchingLibrary(_lib, _targetDir) {
return Promise.resolve();
}
}, {
key: 'notifyInstalledLibrary',
value: function notifyInstalledLibrary(_lib, _targetDir) {
return Promise.resolve();
}
}]);
return LibraryInstallCommandSite;
}(_command.CommandSite);
function buildAdapters(libDir, libName) {
var fsrepo = new _particleLibraryManager.FileSystemLibraryRepository(libDir, _particleLibraryManager.FileSystemNamingStrategy.DIRECT);
return fsrepo.addAdapters(function () {}, libName, _path2.default.join(libDir, 'src'));
}
/**
* Key differences between vendored and non-vendored install:
*
* - vendored libraries
* - are installed using only the library name (since they are assumed to be unique within a given project)
* - require a project (since they are installed under src/libs
* - non-vendored libraries
* - are installed using name@version as the directory name
* - don't require a project.
*/
/**
* A strategy factory that determines where to place libraries when vendored in a project.
* @param {ProjectProperties} project The project to vendor a library into
* @returns {function} A function that provides the target library directory
*/
function vendoredInstallStrategy(project) {
return function (name, _version) {
return project.libraryDirectory(true, name);
};
}
/**
* A strategy factory that determines where to place libraries when installed to
* a shared directory.
* @param {string} baseDir the shared directory where the library should be installed ot
* @returns {function(*, *=)} A function that provides the target library directory
*/
function nameVersionInstallStrategy(baseDir) {
return function (name, version) {
if (!version) {
throw Error('hey I need a version!');
}
// todo - should probably instead instantiate the appropriate library repository
// so we get reuse and consistency
return _path2.default.join(baseDir, name + '@' + version);
};
}
/**
* Implements the library initialization command.
*/
var LibraryInstallCommand = exports.LibraryInstallCommand = function (_Command) {
_inherits(LibraryInstallCommand, _Command);
function LibraryInstallCommand() {
_classCallCheck(this, LibraryInstallCommand);
return _possibleConstructorReturn(this, (LibraryInstallCommand.__proto__ || Object.getPrototypeOf(LibraryInstallCommand)).apply(this, arguments));
}
_createClass(LibraryInstallCommand, [{
key: '_centralLibrariesDirectory',
/**
* @param {LibraryInstallCommandSite} _site for the `findHomePath`
* @returns {Object|string|*|Promise} The location to store libraries centrally on this machine
* @private
*/
value: function _centralLibrariesDirectory(_site) {
return new _projects.Libraries().communityLibrariesFolder();
}
/**
* @param {object} state The current conversation state.
* @param {LibraryInstallCommandSite} site external services.
* @returns {Promise} To run the library install command.
*/
}, {
key: 'run',
value: function run(state, site) {
var _this3 = this;
// the directory containing the target project
var targetDir = site.targetDirectory();
var _split = (site.libraryName() || '').split('@'),
_split2 = _slicedToArray(_split, 2),
libName = _split2[0],
libVersion = _split2[1];
var client = site.apiClient();
var cloudRepo = new _particleLibraryManager.CloudLibraryRepository({ client: client });
var context = {};
var vendored = site.isVendored();
var properties = void 0;
if (!vendored && !libName) {
throw Error('Please provide a library name to install');
}
var projectMustExist = vendored; // vendored libraries require a project to install into
return (0, _library.findProject)(targetDir, projectMustExist).then(function (_properties) {
properties = _properties;
return vendored ? vendoredInstallStrategy(properties) : _this3._centralLibrariesDirectory(site).then(function (dir) {
return nameVersionInstallStrategy(dir);
});
}).then(function (installStrategy) {
if (libName) {
return _this3.installSingleLib(site, cloudRepo, vendored, libName, libVersion, installStrategy, properties, context);
} else {
return _this3.installProjectLibs(site, cloudRepo, vendored, installStrategy, properties, context);
}
}).catch(function (err) {
return site.error(err);
});
}
}, {
key: 'installProjectLibs',
value: function installProjectLibs(site, cloudRepo, vendored, installStrategy, project, context) {
var _this4 = this;
// read the project
// todo - validate the project format first so we don't get repeated errors when
// attempting to vendor libraries from a simple project
return project.load().then(function () {
var deps = project.groups.dependencies || {};
var install = [];
for (var d in deps) {
var libName = d;
var libVersion = deps[d];
install.push(_this4.installSingleLib(site, cloudRepo, vendored, libName, libVersion, installStrategy, project, context));
}
return Promise.all(install);
});
}
/**
* Installs a library
* @param {LibraryInstallCommandSite} site The command site
* @param {CloudLibraryRepository} cloudRepo The cloud repo the library is fetched from
* @param {boolean} vendored true if the library is being vendored
* @param {String} libName The library name to install
* @param {String} libVersion the library version to install (undefined for latest)
* @param {function} libraryTargetStrategy Called with library name and version, used to retrieve the target directory.
* @param {ProjectProperties} project The project properties for the project being installed to.
* Only defined when vendored is true.
* @param {object} context The current operation context.
* @returns {Promise|Promise.<TResult>} A promise to install the library and dependents.
* @private
*/
}, {
key: '_installLib',
value: function _installLib(site, cloudRepo, vendored, libName, libVersion, libraryTargetStrategy, project, context) {
var _this5 = this;
context[libName] = libVersion || 'latest';
return site.notifyCheckingLibrary(libName).then(function () {
return cloudRepo.fetch(libName, libVersion);
}).then(function (lib) {
var libDir = libraryTargetStrategy(lib.metadata.name, lib.metadata.version);
return site.notifyFetchingLibrary(lib.metadata, libDir).then(function () {
return lib.copyTo(libDir);
}).then(function () {
if (site.isAdaptersRequired()) {
return buildAdapters(libDir, lib.metadata.name);
}
}).then(function () {
return site.notifyInstalledLibrary(lib.metadata, libDir);
}).then(function () {
return _this5._installDependents(site, cloudRepo, vendored, libraryTargetStrategy, project, context, libDir);
});
});
}
/**
* Installs the dependencies of a library.
* @param {LibraryInstallCommandSite} site The command site - used to provide notifications of installation progress
* @param {CloudLibraryRepository} cloudRepo The cloud repo used to retrieve libraries
* @param {boolean} vendored true if the library is being installed into the project
* @param {function} libraryTargetStrategy Retrieves the library directory to install to given the library name and version.
* @param {ProjectProperties} project The project being installed to.
* @param {object} context The current operation context - used to avoid infinite recursion in the event of cyclic dependencies.
* @param {String} libDir The directory containing the library whose dependences should be installed.
* @returns {*|Promise|Promise.<TResult>} returns a promise to install all library dependents
* @private
*/
}, {
key: '_installDependents',
value: function _installDependents(site, cloudRepo, vendored, libraryTargetStrategy, project, context, libDir) {
var _this6 = this;
var libraryProperties = new _library_properties2.default(libDir);
return libraryProperties.load().then(function () {
var resolve = [];
var dependencies = libraryProperties.dependencies();
for (var dependencyName in dependencies) {
var dependencyVersion = dependencies[dependencyName];
if (!context[dependencyName]) {
context[dependencyName] = dependencyVersion;
resolve.push(_this6._installLib(site, cloudRepo, vendored, dependencyName, dependencyVersion, libraryTargetStrategy, project, context));
}
}
return Promise.all(resolve);
});
}
/**
* Install a single library.
* @param {LibraryIntallCommandSite} site The command site to receive install updates
* @param {CloudLibraryRepository} cloudRepo The cloud repository that is used to retrieve the library.
* @param {bool} vendored true if the library should be vendored.
* @param {string} libName the name of the library to install
* @param {string} libVersion the version of the library to install, or undefined for the latest version.
* (currently unused.)
* @param {function(name,version)} installTarget The function that retrieves the target directory for the library
* @param {ProjectProperties} project the project to update
* @param {object} context Maintains the context for installing libraries.
* @returns {Promise} to install the library.
*/
}, {
key: 'installSingleLib',
value: function installSingleLib(site, cloudRepo, vendored, libName, libVersion, installTarget, project, context) {
var _this7 = this;
var fetchLayout = project && vendored ? project.projectLayout() : Promise.resolve(_project_properties.legacy);
return fetchLayout.then(function (layout) {
if (vendored && layout !== _project_properties.extended) {
return site.notifyIncorrectLayout(layout, _project_properties.extended, libName, installTarget(libName, libVersion));
} else {
return _this7._installLib(site, cloudRepo, vendored, libName, libVersion, installTarget, project, context);
}
});
}
}]);
return LibraryInstallCommand;
}(_command.Command);