UNPKG

particle-commands

Version:

Library of UX-neutral commands that provide key functionality for developer tools

357 lines (301 loc) 14.8 kB
'use strict'; 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);