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
JavaScript
"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;
}