UNPKG

bit-bin

Version:

<a href="https://opensource.org/licenses/Apache-2.0"><img alt="apache" src="https://img.shields.io/badge/License-Apache%202.0-blue.svg"></a> <a href="https://github.com/teambit/bit/blob/master/CONTRIBUTING.md"><img alt="prs" src="https://img.shields.io/b

744 lines (576 loc) 20.2 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; function _bluebird() { const data = require("bluebird"); _bluebird = function () { return data; }; return data; } function _defineProperty2() { const data = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); _defineProperty2 = function () { return data; }; return data; } function path() { const data = _interopRequireWildcard(require("path")); path = function () { return data; }; return data; } function _ramda() { const data = _interopRequireDefault(require("ramda")); _ramda = function () { return data; }; return data; } function _fsExtra() { const data = _interopRequireDefault(require("fs-extra")); _fsExtra = function () { return data; }; return data; } function _ajv() { const data = _interopRequireDefault(require("ajv")); _ajv = function () { return data; }; return data; } function _semver() { const data = _interopRequireDefault(require("semver")); _semver = function () { return data; }; return data; } function _logger() { const data = _interopRequireWildcard(require("../logger/logger")); _logger = function () { return data; }; return data; } function _scope() { const data = require("../scope"); _scope = function () { return data; }; return data; } function _bitId() { const data = require("../bit-id"); _bitId = function () { return data; }; return data; } function _extensionNameNotValid() { const data = _interopRequireDefault(require("./exceptions/extension-name-not-valid")); _extensionNameNotValid = function () { return data; }; return data; } function _extensionGetDynamicConfigError() { const data = _interopRequireDefault(require("./exceptions/extension-get-dynamic-config-error")); _extensionGetDynamicConfigError = function () { return data; }; return data; } function _analytics() { const data = require("../analytics/analytics"); _analytics = function () { return data; }; return data; } function _extensionLoadError() { const data = _interopRequireDefault(require("./exceptions/extension-load-error")); _extensionLoadError = function () { return data; }; return data; } function _environment() { const data = _interopRequireDefault(require("../environment")); _environment = function () { return data; }; return data; } function _extensionSchemaError() { const data = _interopRequireDefault(require("./exceptions/extension-schema-error")); _extensionSchemaError = function () { return data; }; return data; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2().default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } const ajv = new (_ajv().default)(); const CORE_EXTENSIONS_PATH = './core-extensions'; // export type BaseExtensionProps = { // ...InstanceSpecificProps, // ...StaticProps // }; // type staticProps = $Diff<BaseExtensionProps, instanceSpecificProps> class BaseExtension { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // Store the required plugin // Store the required plugin constructor(extensionProps) { (0, _defineProperty2().default)(this, "name", void 0); (0, _defineProperty2().default)(this, "loaded", void 0); (0, _defineProperty2().default)(this, "initialized", void 0); (0, _defineProperty2().default)(this, "disabled", void 0); (0, _defineProperty2().default)(this, "filePath", void 0); (0, _defineProperty2().default)(this, "rootDir", void 0); (0, _defineProperty2().default)(this, "rawConfig", void 0); (0, _defineProperty2().default)(this, "schema", void 0); (0, _defineProperty2().default)(this, "options", void 0); (0, _defineProperty2().default)(this, "dynamicConfig", void 0); (0, _defineProperty2().default)(this, "context", void 0); (0, _defineProperty2().default)(this, "script", void 0); (0, _defineProperty2().default)(this, "_initOptions", void 0); (0, _defineProperty2().default)(this, "api", _getConcreteBaseAPI({ name: this.name })); this.name = extensionProps.name; this.rawConfig = extensionProps.rawConfig; this.schema = extensionProps.schema; this.options = extensionProps.options; this.dynamicConfig = extensionProps.dynamicConfig || extensionProps.rawConfig; this.context = extensionProps.context; this.script = extensionProps.script; this.disabled = extensionProps.disabled; this.filePath = extensionProps.filePath; this.rootDir = extensionProps.rootDir || ''; this.loaded = extensionProps.loaded; this.api = extensionProps.api; } // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! get writeConfigFilesOnAction() { if (!this.initOptions) { return false; } // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! return this.initOptions.writeConfigFilesOnAction; } // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! get initOptions() { return this._initOptions; } // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! set initOptions(opts) { const defaultInitOpts = { writeConfigFilesOnAction: false }; if (!opts) { this._initOptions = defaultInitOpts; return; } const res = {}; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (opts.write) { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! res.writeConfigFilesOnAction = true; } // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! this._initOptions = res; } /** * Run the extension's init function */ init(throws = false) { var _this = this; return (0, _bluebird().coroutine)(function* () { _analytics().Analytics.addBreadCrumb('base-extension', 'initialize extension'); try { let initOptions = {}; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (_this.script && _this.script.init && typeof _this.script.init === 'function') { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! initOptions = _this.script.init({ rawConfig: _this.rawConfig, dynamicConfig: _this.dynamicConfig, api: _this.api }); } // wrap in promise, in case a script has async init _this.initOptions = yield Promise.resolve(initOptions); _this.initialized = true; // Make sure to not kill the process if an extension didn't load correctly } catch (err) { _logger().default.error(`initialized extension ${_this.name} failed`, err); if (throws) { throw new (_extensionLoadError().default)(err, _this.name); } _this.initialized = false; return false; } return true; })(); } extendAPI(baseApi, api) { this.api = _ramda().default.merge(baseApi, api); } toString() { return JSON.stringify(this, null, 2); } toBitJsonObject() { return { [this.name]: { rawConfig: this.rawConfig, options: this.options } }; } toModelObject() { return { name: this.name, config: this.dynamicConfig }; } toObject() { return this.toModelObject(); } /** * Reload the extension, this mainly contain the process of going to the extension file requiring it and get the dynamic config * It mostly used for env extension when sometime on the first load the env didn't installed yet (only during build / test) phase */ // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! reload(scopePath, { throws }) { var _this2 = this; return (0, _bluebird().coroutine)(function* () { _analytics().Analytics.addBreadCrumb('base-extension', 'reload extension'); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (!_this2.filePath && !_this2.options.core) { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const { resolvedPath, componentPath } = _getExtensionPath(_this2.name, scopePath, _this2.options.core); _this2.filePath = resolvedPath; _this2.rootDir = componentPath; } _this2.name = _addVersionToNameFromPathIfMissing(_this2.name, _this2.rootDir, _this2.options); const baseProps = yield BaseExtension.loadFromFile({ name: _this2.name, filePath: _this2.filePath, rootDir: _this2.rootDir, rawConfig: _this2.rawConfig, options: _this2.options, throws }); if (baseProps.loaded) { _this2.loaded = baseProps.loaded; _this2.script = baseProps.script; _this2.dynamicConfig = baseProps.dynamicConfig; yield _this2.init(); } })(); } setExtensionPathInScope(scopePath) { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const { resolvedPath, componentPath } = _getExtensionPath(this.name, scopePath, this.options.core); this.filePath = resolvedPath; this.rootDir = componentPath; } static transformStringToModelObject(name) { return { name, config: {} }; } /** * Load extension by name * The extension will be from scope by default or from file * if there is file(path) in the options * The file path is relative to the bit.json of the project or absolute * @param {string} name - name of the extension * @param {Object} rawConfig - raw config for the extension * @param {Object} options - extension options such as - disabled, file, core * @param {string} consumerPath - path to the consumer folder (to load the file relatively) * @param {string} scopePath - scope which stores the extension code */ static load({ name, rawConfig = {}, options = {}, consumerPath, scopePath, throws = false, context }) { return (0, _bluebird().coroutine)(function* () { _logger().default.debug(`base-extension loading ${name}`); const concreteBaseAPI = _getConcreteBaseAPI({ name }); if (options.file) { let absPath = options.file; const file = options.file || ''; if (!path().isAbsolute(options.file) && consumerPath) { absPath = path().resolve(consumerPath, file); } const staticExtensionProps = yield BaseExtension.loadFromFile({ name, filePath: absPath, rawConfig, options, throws }); const extensionProps = _objectSpread({ api: concreteBaseAPI, context }, staticExtensionProps); extensionProps.api = concreteBaseAPI; return extensionProps; } let staticExtensionProps = { name, rawConfig, dynamicConfig: rawConfig, options, disabled: false, loaded: false, filePath: '' }; // Require extension from scope if (scopePath) { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const { resolvedPath, componentPath } = _getExtensionPath(name, scopePath, options.core); const nameWithVersion = _addVersionToNameFromPathIfMissing(name, componentPath, options); staticExtensionProps = yield BaseExtension.loadFromFile({ name: nameWithVersion, filePath: resolvedPath, rootDir: componentPath, rawConfig, options, throws }); } const extensionProps = _objectSpread({ api: concreteBaseAPI, context }, staticExtensionProps); return extensionProps; })(); } static loadFromModelObjectBase(modelObject) { let staticExtensionProps; if (typeof modelObject === 'string') { staticExtensionProps = { name: modelObject, rawConfig: {}, dynamicConfig: {}, options: {}, disabled: false, loaded: false, filePath: '' }; } else { staticExtensionProps = { name: modelObject.name, rawConfig: modelObject.config, dynamicConfig: modelObject.config, options: {}, disabled: false, loaded: false, filePath: '' }; } const concreteBaseAPI = _getConcreteBaseAPI({ name: staticExtensionProps.name }); const extensionProps = _objectSpread({ api: concreteBaseAPI }, staticExtensionProps); return extensionProps; } static loadFromFile({ name, filePath, rootDir, rawConfig = {}, options = {}, throws = false }) { return (0, _bluebird().coroutine)(function* () { _logger().default.debug(`base-extension, loading extension ${name} from ${filePath}`); const extensionProps = { name, rawConfig, dynamicConfig: rawConfig, options, disabled: false, loaded: false, filePath: '', rootDir: '' }; // Skip disabled extensions // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (options.disabled) { extensionProps.disabled = true; _logger().default.info(`skip extension ${extensionProps.name} because it is disabled`); extensionProps.loaded = false; return extensionProps; } extensionProps.filePath = filePath; extensionProps.rootDir = rootDir; const isFileExist = yield _fsExtra().default.pathExists(filePath); if (!isFileExist) { // Do not throw an error if the file not exist since we will install it later // unless you specify the options.file which means you want a specific file which won't be installed automatically later if (throws && options.file) { const err = new Error(`the file ${filePath} not found`); throw new (_extensionLoadError().default)(err, extensionProps.name); } extensionProps.loaded = false; return extensionProps; } if (rootDir && !_environment().default.isEnvironmentInstalled(rootDir)) { extensionProps.loaded = false; return extensionProps; } try { const script = require(filePath); // eslint-disable-line extensionProps.script = script.default ? script.default : script; // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (extensionProps.script.getSchema && typeof extensionProps.script.getSchema === 'function') { // the function may or may not be a promise // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! extensionProps.schema = yield Promise.resolve(extensionProps.script.getSchema()); // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! const valid = ajv.validate(extensionProps.schema, rawConfig); if (!valid) { throw new (_extensionSchemaError().default)(name, ajv.errorsText()); } } // Make sure to not kill the process if an extension didn't load correctly } catch (err) { if (err.code === 'MODULE_NOT_FOUND') { const msg = `loading extension ${extensionProps.name} failed, the file ${extensionProps.filePath} not found`; _logger().default.warn(msg); // console.error(msg); // eslint-disable-line no-console } _logger().default.error(`loading extension ${extensionProps.name} failed`, err); extensionProps.loaded = false; if (throws) { let printStack = true; if (err instanceof _extensionSchemaError().default) { printStack = false; } throw new (_extensionLoadError().default)(err, extensionProps.name, printStack); } return extensionProps; } extensionProps.loaded = true; return extensionProps; })(); } static loadDynamicConfig(extensionProps) { _logger().default.debug('base-extension - loadDynamicConfig'); const getDynamicConfig = _ramda().default.path(['script', 'getDynamicConfig'], extensionProps); if (getDynamicConfig && typeof getDynamicConfig === 'function') { try { const dynamicConfig = getDynamicConfig({ rawConfig: extensionProps.rawConfig }); return dynamicConfig; } catch (err) { throw new (_extensionGetDynamicConfigError().default)(err, extensionProps.name); } } return undefined; } } exports.default = BaseExtension; function _getExtensionPath(name, scopePath, isCore = false) { if (isCore) { return _getCoreExtensionPath(name); } if (!scopePath) { throw new Error('base-extension._getExtensionPath expects to get scopePath'); } return _getRegularExtensionPath(name, scopePath); } function _getCoreExtensionPath(name) { const componentPath = path().join(__dirname, CORE_EXTENSIONS_PATH, name); return { resolvedPath: componentPath, componentPath }; } function _getRegularExtensionPath(name, scopePath) { let bitId; try { bitId = _bitId().BitId.parse(name, true); // todo: make sure it always has a scope } catch (err) { throw new (_extensionNameNotValid().default)(name); } if (!bitId || !bitId.scope) throw new (_extensionNameNotValid().default)(name); const internalComponentsPath = _scope().Scope.getComponentsRelativePath(); const internalComponentPath = _scope().Scope.getComponentRelativePath(bitId, scopePath); const componentPath = path().join(scopePath, internalComponentsPath, internalComponentPath); try { // This might throw an error in case of imported component when the env // isn't installed yet // It will be handled in higher functions const resolved = require.resolve(componentPath); return { resolvedPath: typeof resolved === 'string' ? resolved : componentPath, componentPath }; } catch (e) { return { resolvedPath: componentPath, componentPath }; } } function _getExtensionVersionFromComponentPath(componentPath) { const parsed = path().parse(componentPath); const version = parsed.base; if (!_semver().default.valid(version)) { return undefined; } return version; } function _addVersionToNameFromPathIfMissing(name, componentPath, options) { // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX! if (options && options.core) return name; // if it's a core extension, it's not a bit-id. let bitId; try { bitId = _bitId().BitId.parse(name, true); // @todo: make sure it always has a scope name } catch (err) { throw new (_extensionNameNotValid().default)(name); } if (bitId.getVersion().latest) { const version = _getExtensionVersionFromComponentPath(componentPath); return bitId.changeVersion(version).toString(); } return name; } const baseApi = { /** * API to get logger */ getLogger: name => () => (0, _logger().createExtensionLogger)(name) }; /** * Function which get actual params and return a concrete base api */ function _getConcreteBaseAPI({ name }) { const concreteBaseAPI = _ramda().default.clone(baseApi); concreteBaseAPI.getLogger = baseApi.getLogger(name); return concreteBaseAPI; }