@teambit/workspace
Version:
809 lines (790 loc) • 37.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WorkspaceAspectsLoader = void 0;
function _legacy() {
const data = require("@teambit/legacy.constants");
_legacy = function () {
return data;
};
return data;
}
function _findRoot() {
const data = _interopRequireDefault(require("find-root"));
_findRoot = function () {
return data;
};
return data;
}
function _toolboxModules() {
const data = require("@teambit/toolbox.modules.module-resolver");
_toolboxModules = function () {
return data;
};
return data;
}
function _aspectLoader() {
const data = require("@teambit/aspect-loader");
_aspectLoader = function () {
return data;
};
return data;
}
function _cli() {
const data = require("@teambit/cli");
_cli = function () {
return data;
};
return data;
}
function _fsExtra() {
const data = _interopRequireDefault(require("fs-extra"));
_fsExtra = function () {
return data;
};
return data;
}
function _harmonyModules() {
const data = require("@teambit/harmony.modules.requireable-component");
_harmonyModules = function () {
return data;
};
return data;
}
function _workspaceModules() {
const data = require("@teambit/workspace.modules.node-modules-linker");
_workspaceModules = function () {
return data;
};
return data;
}
function _componentId() {
const data = require("@teambit/component-id");
_componentId = function () {
return data;
};
return data;
}
function _legacy2() {
const data = require("@teambit/legacy.scope");
_legacy2 = function () {
return data;
};
return data;
}
function _pMapSeries() {
const data = _interopRequireDefault(require("p-map-series"));
_pMapSeries = function () {
return data;
};
return data;
}
function _lodash() {
const data = require("lodash");
_lodash = function () {
return data;
};
return data;
}
function _bitError() {
const data = require("@teambit/bit-error");
_bitError = function () {
return data;
};
return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
class WorkspaceAspectsLoader {
constructor(workspace, scope, aspectLoader, envs, dependencyResolver, logger, configStore, harmony, onAspectsResolveSlot, onRootAspectAddedSlot, resolveAspectsFromNodeModules = false, resolveEnvsFromRoots = false) {
this.workspace = workspace;
this.scope = scope;
this.aspectLoader = aspectLoader;
this.envs = envs;
this.dependencyResolver = dependencyResolver;
this.logger = logger;
this.configStore = configStore;
this.harmony = harmony;
this.onAspectsResolveSlot = onAspectsResolveSlot;
this.onRootAspectAddedSlot = onRootAspectAddedSlot;
this.resolveAspectsFromNodeModules = resolveAspectsFromNodeModules;
this.resolveEnvsFromRoots = resolveEnvsFromRoots;
_defineProperty(this, "consumer", void 0);
_defineProperty(this, "resolvedInstalledAspects", void 0);
this.consumer = this.workspace.consumer;
this.resolvedInstalledAspects = new Map();
// Only enable this when root components is enabled as well
this.resolveEnvsFromRoots = this.resolveEnvsFromRoots && this.dependencyResolver.hasRootComponents();
}
/**
* load aspects from the workspace and if not exists in the workspace, load from the node_modules.
* keep in mind that the graph may have circles.
*/
async loadAspects(ids = [], throwOnError, neededFor, opts = {}) {
const calculatedThrowOnError = throwOnError ?? false;
const defaultOpts = {
useScopeAspectsCapsule: false,
throwOnError: calculatedThrowOnError,
runSubscribers: true,
skipDeps: false,
hideMissingModuleError: !!this.workspace.inInstallContext,
ignoreErrorFunc: this.workspace.inInstallContext ? ignoreAspectLoadingError : () => false,
ignoreErrors: false,
resolveEnvsFromRoots: this.resolveEnvsFromRoots,
forceLoad: false
};
const mergedOpts = _objectSpread(_objectSpread({}, defaultOpts), opts);
// generate a random callId to be able to identify the call from the logs
const callId = Math.floor(Math.random() * 1000);
const loggerPrefix = `[${callId}] loadAspects,`;
this.logger.profileTrace(`[${callId}] workspace.loadAspects`);
this.logger.info(`${loggerPrefix} loading ${ids.length} aspects.
ids: ${ids.join(', ')}
needed-for: ${neededFor || '<unknown>'}. using opts: ${JSON.stringify(mergedOpts, null, 2)}`);
const [localAspects, nonLocalAspects] = (0, _lodash().partition)(ids, id => id.startsWith('file:'));
const localAspectsMap = await this.aspectLoader.loadAspectFromPath(localAspects);
this.workspace.localAspects = _objectSpread(_objectSpread({}, this.workspace.localAspects), localAspectsMap);
let notLoadedIds = nonLocalAspects;
if (!mergedOpts.forceLoad) {
notLoadedIds = nonLocalAspects.filter(id => !this.aspectLoader.isAspectLoaded(id));
}
if (!notLoadedIds.length) {
this.logger.profileTrace(`[${callId}] workspace.loadAspects`);
return [];
}
const coreAspectsStringIds = this.aspectLoader.getCoreAspectIds();
const idsWithoutCore = (0, _lodash().difference)(notLoadedIds, coreAspectsStringIds);
const componentIds = await this.workspace.resolveMultipleComponentIds(idsWithoutCore);
const {
workspaceIds,
nonWorkspaceIds
} = await this.groupIdsByWorkspaceExistence(componentIds, mergedOpts.resolveEnvsFromRoots);
this.logFoundWorkspaceVsScope(loggerPrefix, workspaceIds, nonWorkspaceIds);
let idsToLoadFromWs = componentIds;
let scopeAspectIds = [];
// TODO: hard coded use the old approach and loading from the scope capsules
// This is because right now loading from the ws node_modules causes issues in some cases
// like for the cloud app
// it should be removed once we fix the issues
if (!this.resolveAspectsFromNodeModules) {
mergedOpts.useScopeAspectsCapsule = true;
}
if (mergedOpts.useScopeAspectsCapsule) {
idsToLoadFromWs = workspaceIds;
scopeAspectIds = await this.loadFromScopeAspectsCapsule(nonWorkspaceIds, throwOnError, neededFor);
}
const aspectsDefs = await this.resolveAspects(undefined, idsToLoadFromWs, _objectSpread({
excludeCore: true,
requestedOnly: false
}, mergedOpts));
const {
manifests,
requireableComponents
} = await this.loadAspectDefsByOrder(aspectsDefs, idsWithoutCore, mergedOpts.throwOnError, mergedOpts.hideMissingModuleError, mergedOpts.ignoreErrorFunc, neededFor, mergedOpts.runSubscribers);
const potentialPluginsIndexes = (0, _lodash().compact)(manifests.map((manifest, index) => {
if (this.aspectLoader.isValidAspect(manifest)) return undefined;
return index;
}));
// Try require components for potential plugins
const pluginsRequireableComponents = potentialPluginsIndexes.map(index => {
return requireableComponents[index];
});
// Do the require again now that the plugins defs already registered
const pluginsManifests = await this.aspectLoader.getManifestsFromRequireableExtensions(pluginsRequireableComponents, throwOnError, opts.runSubscribers);
await this.aspectLoader.loadExtensionsByManifests(pluginsManifests, undefined, {
throwOnError
});
const manifestIds = manifests.map(manifest => manifest.id);
this.logger.debug(`${loggerPrefix} finish loading aspects`);
this.logger.profileTrace(`[${callId}] workspace.loadAspects`);
return (0, _lodash().compact)(manifestIds.concat(scopeAspectIds));
}
async loadFromScopeAspectsCapsule(ids, throwOnError, neededFor) {
let scopeAspectIds = [];
const currentLane = await this.consumer.getCurrentLaneObject();
if (!ids.length) return [];
const nonWorkspaceIdsString = ids.map(id => id.toString());
try {
scopeAspectIds = await this.scope.loadAspects(nonWorkspaceIdsString, throwOnError, neededFor, currentLane, {
packageManagerConfigRootDir: this.workspace.path,
workspaceName: this.workspace.name
});
return scopeAspectIds;
} catch (err) {
this.throwWsJsoncAspectNotFoundError(err);
return scopeAspectIds;
throw err;
}
}
throwWsJsoncAspectNotFoundError(err) {
if (err instanceof _legacy2().ComponentNotFound) {
const config = this.harmony.get('teambit.harmony/config');
const configStr = JSON.stringify(config.workspaceConfig?.raw || {});
if (configStr.includes(err.id)) {
throw new (_bitError().BitError)(`error: a component "${err.id}" was not found
your workspace.jsonc has this component-id set. you might want to remove/change it.`);
}
}
}
async loadAspectDefsByOrder(aspectsDefs, seeders, throwOnError, hideMissingModuleError, ignoreErrorFunc, neededFor, runSubscribers = true) {
const {
nonWorkspaceDefs
} = await this.groupAspectDefsByWorkspaceExistence(aspectsDefs);
const scopeAspectsLoader = this.scope.getScopeAspectsLoader();
const scopeIds = (0, _lodash().compact)(nonWorkspaceDefs.map(aspectDef => aspectDef.getId));
const scopeIdsGrouped = await scopeAspectsLoader.groupAspectIdsByEnvOfTheList(scopeIds);
// Make sure to first load envs from the list otherwise it will fail when trying to load other aspects
// as their envs might not be loaded yet
if (scopeIdsGrouped.envs && scopeIdsGrouped.envs.length && !runSubscribers) {
await this.scope.loadAspects(scopeIdsGrouped.envs, throwOnError, 'workspace.loadAspects loading scope aspects');
}
const requireableComponents = this.aspectDefsToRequireableComponents(aspectsDefs);
const manifests = await this.aspectLoader.getManifestsFromRequireableExtensions(requireableComponents, throwOnError, runSubscribers);
await this.aspectLoader.loadExtensionsByManifests(manifests, {
seeders,
neededFor
}, {
throwOnError,
hideMissingModuleError,
ignoreErrorFunc
});
return {
manifests,
requireableComponents
};
}
async resolveAspects(runtimeName, componentIds, opts) {
const callId = Math.floor(Math.random() * 1000);
const loggerPrefix = `[${callId}] workspace resolveAspects,`;
this.logger.debug(`${loggerPrefix}, resolving aspects for - runtimeName: ${runtimeName}, componentIds: ${componentIds}`);
const defaultOpts = {
excludeCore: false,
requestedOnly: false,
filterByRuntime: true,
useScopeAspectsCapsule: false,
workspaceName: this.workspace.name,
resolveEnvsFromRoots: this.resolveEnvsFromRoots,
packageManagerConfigRootDir: this.workspace.path
};
const mergedOpts = _objectSpread(_objectSpread({}, defaultOpts), opts);
const idsToResolve = componentIds ? componentIds.map(id => id.toString()) : this.harmony.extensionsIds;
const workspaceLocalAspectsIds = Object.keys(this.workspace.localAspects);
const [localAspectsIds, nonLocalAspectsIds] = (0, _lodash().partition)(idsToResolve, id => workspaceLocalAspectsIds.includes(id));
const localDefs = await this.aspectLoader.resolveLocalAspects(localAspectsIds.map(id => this.workspace.localAspects[id]), runtimeName);
const coreAspectsIds = this.aspectLoader.getCoreAspectIds();
const configuredAspects = this.aspectLoader.getConfiguredAspects();
// it's possible that componentIds are core-aspects that got version for some reason, remove the version to
// correctly filter them out later.
const userAspectsIds = nonLocalAspectsIds ? nonLocalAspectsIds.filter(id => !coreAspectsIds.includes(id.split('@')[0])).map(id => id.toString()) : (0, _lodash().difference)(this.harmony.extensionsIds, coreAspectsIds);
const rootAspectsIds = (0, _lodash().difference)(configuredAspects, coreAspectsIds);
const componentIdsToResolve = await this.workspace.resolveMultipleComponentIds(userAspectsIds);
const components = await this.importAndGetAspects(componentIdsToResolve, opts?.throwOnError);
// Run the on load slot
await this.runOnAspectsResolveFunctions(components);
if (opts?.skipDeps) {
const wsAspectDefs = await this.aspectLoader.resolveAspects(components, this.getWorkspaceAspectResolver([], runtimeName));
const coreAspectDefs = await Promise.all(coreAspectsIds.map(async coreId => {
const rawDef = await (0, _aspectLoader().getAspectDef)(coreId, runtimeName);
return this.aspectLoader.loadDefinition(rawDef);
}));
const idsToFilter = idsToResolve.map(idStr => _componentId().ComponentID.fromString(idStr));
const targetDefs = wsAspectDefs.concat(coreAspectDefs).concat(localDefs);
const finalDefs = this.aspectLoader.filterAspectDefs(targetDefs, idsToFilter, runtimeName, mergedOpts);
return finalDefs;
}
const groupedByIsPlugin = (0, _lodash().groupBy)(components, component => {
return this.aspectLoader.hasPluginFiles(component);
});
const graph = await this.getAspectsGraphWithoutCore(groupedByIsPlugin.false, this.isAspect.bind(this));
const aspectsComponents = graph.nodes.map(node => node.attr).concat(groupedByIsPlugin.true || []);
this.logger.debug(`${loggerPrefix} found ${aspectsComponents.length} aspects in the aspects-graph`);
const {
workspaceComps,
nonWorkspaceComps
} = await this.groupComponentsByWorkspaceExistence(aspectsComponents, mergedOpts.resolveEnvsFromRoots);
const workspaceCompsIds = workspaceComps.map(c => c.id);
const nonWorkspaceCompsIds = nonWorkspaceComps.map(c => c.id);
this.logFoundWorkspaceVsScope(loggerPrefix, workspaceCompsIds, nonWorkspaceCompsIds);
const stringIds = [];
const wsAspectDefs = await this.aspectLoader.resolveAspects(workspaceComps, this.getWorkspaceAspectResolver(stringIds, runtimeName));
await this.linkIfMissingWorkspaceAspects(wsAspectDefs);
// TODO: hard coded use the old approach and loading from the scope capsules
// This is because right now loading from the ws node_modules causes issues in some cases
// like for the cloud app
// it should be removed once we fix the issues
if (!this.resolveAspectsFromNodeModules) {
mergedOpts.useScopeAspectsCapsule = true;
}
let componentsToResolveFromScope = nonWorkspaceComps;
let componentsToResolveFromInstalled = [];
if (!mergedOpts.useScopeAspectsCapsule) {
const nonWorkspaceCompsGroups = (0, _lodash().groupBy)(nonWorkspaceComps, component => this.envs.isEnv(component));
componentsToResolveFromScope = nonWorkspaceCompsGroups.true || [];
componentsToResolveFromInstalled = nonWorkspaceCompsGroups.false || [];
}
const scopeIds = componentsToResolveFromScope.map(c => c.id);
this.logger.debug(`${loggerPrefix} ${scopeIds.length} components are not in the workspace and are loaded from the scope capsules:\n${scopeIds.map(id => id.toString()).join('\n')}`);
const scopeAspectsDefs = scopeIds.length ? await this.scope.resolveAspects(runtimeName, scopeIds, mergedOpts) : [];
this.logger.debug(`${loggerPrefix} ${componentsToResolveFromInstalled.length} components are not in the workspace and are loaded from the node_modules:\n${componentsToResolveFromInstalled.map(c => c.id.toString()).join('\n')}`);
const installedAspectsDefs = componentsToResolveFromInstalled.length ? await this.aspectLoader.resolveAspects(componentsToResolveFromInstalled, this.getInstalledAspectResolver(graph, rootAspectsIds, runtimeName, {
throwOnError: opts?.throwOnError ?? false
})) : [];
let coreAspectDefs = await Promise.all(coreAspectsIds.map(async coreId => {
const rawDef = await (0, _aspectLoader().getAspectDef)(coreId, runtimeName);
return this.aspectLoader.loadDefinition(rawDef);
}));
// due to lack of workspace and scope runtimes. TODO: fix after adding them.
if (runtimeName && mergedOpts.filterByRuntime) {
coreAspectDefs = coreAspectDefs.filter(coreAspect => {
return coreAspect.runtimePath;
});
}
const localResolved = await this.aspectLoader.resolveLocalAspects(Object.keys(this.workspace.localAspects), runtimeName);
const allDefsExceptLocal = [...wsAspectDefs, ...coreAspectDefs, ...scopeAspectsDefs, ...installedAspectsDefs];
const withoutLocalAspects = allDefsExceptLocal.filter(aspectId => {
return !localResolved.find(localAspect => {
return localAspect.id === aspectId.component?.id?.toStringWithoutVersion();
});
});
const allDefs = [...withoutLocalAspects, ...localResolved];
const idsToFilter = idsToResolve.map(idStr => _componentId().ComponentID.fromString(idStr));
const filteredDefs = this.aspectLoader.filterAspectDefs(allDefs, idsToFilter, runtimeName, mergedOpts);
return filteredDefs;
}
shouldUseHashForCapsules() {
return !this.configStore.getConfig(_legacy().CFG_CAPSULES_BUILD_COMPONENTS_BASE_DIR);
}
getCapsulePath() {
const defaultPath = this.workspace.path;
return this.configStore.getConfig(_legacy().CFG_CAPSULES_BUILD_COMPONENTS_BASE_DIR) || defaultPath;
}
logFoundWorkspaceVsScope(loggerPrefix, workspaceIds, nonWorkspaceIds) {
const workspaceIdsStr = workspaceIds.length ? workspaceIds.map(id => id.toString()).join('\n') : '';
const nonWorkspaceIdsStr = nonWorkspaceIds.length ? nonWorkspaceIds.map(id => id.toString()).join('\n') : '';
this.logger.debug(`${loggerPrefix} found ${workspaceIds.length} components in the workspace, ${nonWorkspaceIds.length} not in the workspace`);
if (workspaceIdsStr) this.logger.debug(`${loggerPrefix} workspace components:\n${workspaceIdsStr}`);
if (nonWorkspaceIdsStr) this.logger.debug(`${loggerPrefix} non workspace components (loaded from the scope capsules or from the node_modules):\n${nonWorkspaceIdsStr}`);
}
async use(aspectIdStr) {
let aspectId = await this.workspace.resolveComponentId(aspectIdStr);
const inWs = await this.workspace.hasId(aspectId);
let aspectIdToAdd = aspectId.toStringWithoutVersion();
let aspectsComponent;
// let aspectPackage;
if (!inWs) {
const aspectsComponents = await this.importAndGetAspects([aspectId]);
if (aspectsComponents[0]) {
aspectsComponent = aspectsComponents[0];
aspectId = aspectsComponent.id;
aspectIdToAdd = aspectId.toString();
}
}
const config = this.harmony.get('teambit.harmony/config').workspaceConfig;
if (!config) {
throw new Error(`use() unable to get the workspace config`);
}
config.setExtension(aspectIdToAdd, {}, {
overrideExisting: false,
ignoreVersion: false
});
await config.write({
reasonForChange: `use (${aspectIdStr})`
});
this.aspectLoader.addInMemoryConfiguredAspect(aspectIdToAdd);
await this.runOnRootAspectAddedFunctions(aspectId, inWs);
return aspectIdToAdd;
}
async getConfiguredUserAspectsPackages(options = {}) {
const rawConfiguredAspects = this.workspace.getWorkspaceConfig()?.extensionsIds;
const coreAspectsIds = this.aspectLoader.getCoreAspectIds();
const userAspectsIds = (0, _lodash().difference)(rawConfiguredAspects, coreAspectsIds);
const componentIdsToResolve = await this.workspace.resolveMultipleComponentIds(userAspectsIds.filter(id => !id.startsWith('file:')));
const aspectsComponents = await this.importAndGetAspects(componentIdsToResolve);
let componentsToGetPackages = aspectsComponents;
if (options.externalsOnly) {
const {
nonWorkspaceComps
} = await this.groupComponentsByWorkspaceExistence(aspectsComponents);
componentsToGetPackages = nonWorkspaceComps;
}
const packages = componentsToGetPackages.map(aspectComponent => {
const packageName = this.dependencyResolver.getPackageName(aspectComponent);
const version = aspectComponent.id.version || '*';
return {
packageName,
version
};
});
return packages;
}
aspectDefsToRequireableComponents(aspectDefs) {
const requireableComponents = aspectDefs.map(aspectDef => {
const localPath = aspectDef.aspectPath;
const component = aspectDef.component;
if (!component) return undefined;
const requireFunc = async () => {
const plugins = this.aspectLoader.getPlugins(component, localPath);
if (plugins.has()) {
return plugins.load(_cli().MainRuntime.name);
}
const isModule = await this.aspectLoader.isEsmModule(localPath);
const aspect = !isModule ?
// eslint-disable-next-line global-require, import/no-dynamic-require
require(localPath) :
// : await this.aspectLoader.loadEsm(join(localPath, 'dist', 'index.js'));
await this.aspectLoader.loadEsm(localPath);
// require aspect runtimes
const runtimePath = await this.aspectLoader.getRuntimePath(component, localPath, _cli().MainRuntime.name);
if (runtimePath) {
if (isModule) await this.aspectLoader.loadEsm(runtimePath);
// eslint-disable-next-line global-require, import/no-dynamic-require
require(runtimePath);
}
return aspect;
};
return new (_harmonyModules().RequireableComponent)(component, requireFunc);
});
return (0, _lodash().compact)(requireableComponents);
}
async linkIfMissingWorkspaceAspects(aspects) {
const idsToLink = await Promise.all(aspects.map(async aspect => {
if (!aspect.component) throw new Error(`linkIfMissingWorkspaceAspects, aspect.component is missing for ${aspect.aspectPath}`);
const isInWs = await this.workspace.hasId(aspect.component.id);
if (!isInWs) return null;
const exist = await _fsExtra().default.pathExists(aspect.aspectPath);
if (!exist) return aspect.component.id;
return null;
}));
const idsToLinkWithoutNull = (0, _lodash().compact)(idsToLink);
if (!idsToLinkWithoutNull.length) return;
await (0, _workspaceModules().linkToNodeModulesByIds)(this.workspace, idsToLinkWithoutNull);
}
/**
* This will return a resolver that knows to resolve aspects which are part of the workspace.
* means aspects exist in the bitmap file
* @param stringIds
* @param runtimeName
* @returns
*/
getWorkspaceAspectResolver(stringIds, runtimeName) {
const workspaceAspectResolver = async component => {
const compStringId = component.id.toString();
stringIds.push(compStringId);
const localPath = await this.workspace.getComponentPackagePath(component);
const runtimePath = runtimeName ? await this.aspectLoader.getRuntimePath(component, localPath, runtimeName) : null;
const aspectFilePath = await this.aspectLoader.getAspectFilePath(component, localPath);
this.logger.debug(`workspace resolveAspects, resolving id: ${compStringId}, localPath: ${localPath}, runtimePath: ${runtimePath}`);
return {
aspectPath: localPath,
aspectFilePath,
runtimePath
};
};
return workspaceAspectResolver;
}
async runOnAspectsResolveFunctions(aspectsComponents) {
const funcs = this.getOnAspectsResolveFunctions();
await (0, _pMapSeries().default)(funcs, async func => {
try {
await func(aspectsComponents);
} catch (err) {
this.logger.error('failed running onAspectsResolve function', err);
}
});
}
getOnAspectsResolveFunctions() {
const aspectsResolveFunctions = this.onAspectsResolveSlot.values();
return aspectsResolveFunctions;
}
async runOnRootAspectAddedFunctions(aspectsId, inWs) {
const funcs = this.getOnRootAspectAddedFunctions();
await (0, _pMapSeries().default)(funcs, async func => {
try {
await func(aspectsId, inWs);
} catch (err) {
this.logger.error('failed running onRootAspectAdded function', err);
}
});
}
getOnRootAspectAddedFunctions() {
const RootAspectAddedFunctions = this.onRootAspectAddedSlot.values();
return RootAspectAddedFunctions;
}
/**
* This will return a resolver that knows to resolve aspects which are not part of the workspace.
* means aspects that does not exist in the bitmap file
* instead it will resolve them from the node_modules recursively
* @param graph
* @param rootIds
* @param runtimeName
* @returns
*/
getInstalledAspectResolver(graph, rootIds, runtimeName, opts = {
throwOnError: false
}) {
const installedAspectsResolver = async component => {
const compStringId = component.id.toString();
// stringIds.push(compStringId);
const localPath = await this.resolveInstalledAspectRecursively(component, rootIds, graph, opts);
if (!localPath) return undefined;
const runtimePath = runtimeName ? await this.aspectLoader.getRuntimePath(component, localPath, runtimeName) : null;
const aspectFilePath = await this.aspectLoader.getAspectFilePath(component, localPath);
this.logger.debug(`workspace resolveInstalledAspects, resolving id: ${compStringId}, localPath: ${localPath}, runtimePath: ${runtimePath}`);
return {
aspectPath: localPath,
aspectFilePath,
runtimePath
};
};
return installedAspectsResolver;
}
async resolveInstalledAspectRecursively(aspectComponent, rootIds, graph, opts = {
throwOnError: false
}) {
const aspectStringId = aspectComponent.id.toString();
if (this.resolvedInstalledAspects.has(aspectStringId)) {
const resolvedPath = this.resolvedInstalledAspects.get(aspectStringId);
return resolvedPath;
}
if (rootIds.includes(aspectStringId)) {
const localPath = await this.workspace.getComponentPackagePath(aspectComponent);
this.resolvedInstalledAspects.set(aspectStringId, localPath);
return localPath;
}
const parent = graph.predecessors(aspectStringId)[0];
if (!parent) return undefined;
const parentPath = await this.resolveInstalledAspectRecursively(parent.attr, rootIds, graph);
if (!parentPath) {
this.resolvedInstalledAspects.set(aspectStringId, null);
return undefined;
}
const packageName = this.dependencyResolver.getPackageName(aspectComponent);
try {
const resolvedPath = (0, _toolboxModules().resolveFrom)(parentPath, [packageName]);
const localPath = (0, _findRoot().default)(resolvedPath);
this.resolvedInstalledAspects.set(aspectStringId, localPath);
return localPath;
} catch (error) {
this.resolvedInstalledAspects.set(aspectStringId, null);
if (opts.throwOnError) {
throw error;
}
this.logger.consoleWarning(`failed resolving aspect ${aspectStringId} from ${parentPath}, error: ${error.message}`);
return undefined;
}
}
/**
* Create a graph of aspects without the core aspects.
* @param components
* @param isAspect
* @returns
*/
async getAspectsGraphWithoutCore(components = [], isAspect) {
const ids = components.map(component => component.id);
const coreAspectsStringIds = this.aspectLoader.getCoreAspectIds();
// TODO: @gilad it causes many issues we need to find a better solution. removed for now.
// const coreAspectsComponentIds = coreAspectsStringIds.map((id) => ComponentID.fromString(id));
// const aspectsIds = components.reduce((acc, curr) => {
// const currIds = curr.state.aspects.ids;
// acc = acc.concat(currIds);
// return acc;
// }, [] as any);
// const otherDependenciesMap = components.reduce((acc, curr) => {
// // const currIds = curr.state.dependencies.dependencies.map(dep => dep.id.toString());
// const currMap = curr.state.dependencies.getIdsMap();
// Object.assign(acc, currMap);
// return acc;
// }, {});
// const depsWhichAreNotAspects = difference(Object.keys(otherDependenciesMap), aspectsIds);
// const depsWhichAreNotAspectsBitIds = depsWhichAreNotAspects.map((strId) => otherDependenciesMap[strId]);
// We only want to load into the graph components which are aspects and not regular dependencies
// This come to solve a circular loop when an env aspect use an aspect (as regular dep) and the aspect use the env aspect as its env
return this.workspace.buildOneGraphForComponents(ids, coreAspectsStringIds, isAspect);
}
/**
* Load all unloaded extensions from an extension list
* this will resolve the extensions from the scope aspects capsules if they are not in the ws
* Only use it for component extensions
* for workspace/scope root aspect use the load aspects directly
*
* The reason we are loading component extensions with "scope aspects capsules" is because for component extensions
* we might have the same extension in multiple versions
* (for example I might have 2 components using different versions of the same env)
* in such case, I can't install both version into the root of the node_modules so I need to place it somewhere else (capsules)
* @param extensions list of extensions with config to load
*/
async loadComponentsExtensions(extensions, originatedFrom, opts = {}) {
const defaultOpts = {
useScopeAspectsCapsule: true,
throwOnError: false,
hideMissingModuleError: !!this.workspace.inInstallContext,
ignoreErrorFunc: this.workspace.inInstallContext ? ignoreAspectLoadingError : undefined,
resolveEnvsFromRoots: this.resolveEnvsFromRoots
};
const mergedOpts = _objectSpread(_objectSpread({}, defaultOpts), opts);
const extensionsIdsP = extensions.map(async extensionEntry => {
// Core extension
if (!extensionEntry.extensionId) {
return extensionEntry.stringId;
}
const id = await this.workspace.resolveComponentId(extensionEntry.extensionId);
// return this.resolveComponentId(extensionEntry.extensionId);
return id.toString();
});
const extensionsIds = await Promise.all(extensionsIdsP);
const harmonyExtensions = this.harmony.extensionsIds;
const loadedExtensions = harmonyExtensions.filter(extId => {
return this.harmony.extensions.get(extId)?.loaded;
});
const extensionsToLoad = (0, _lodash().difference)(extensionsIds, loadedExtensions);
if (!extensionsToLoad.length) return;
await this.loadAspects(extensionsToLoad, undefined, originatedFrom?.toString(), mergedOpts);
}
async isAspect(id) {
const component = await this.workspace.get(id);
const isUsingAspectEnv = this.envs.isUsingAspectEnv(component);
const isUsingEnvEnv = this.envs.isUsingEnvEnv(component);
const isValidAspect = isUsingAspectEnv || isUsingEnvEnv;
return isValidAspect;
}
/**
* same as `this.importAndGetMany()` with a specific error handling of ComponentNotFound
*/
async importAndGetAspects(componentIds, throwOnError = true) {
try {
// We don't want to load the seeders as aspects as it will cause an infinite loop
// once you try to load the seeder it will try to load the workspace component
// that will arrive here again and again
const loadOpts = {
idsToNotLoadAsAspects: componentIds.map(id => id.toString())
};
return await this.workspace.importAndGetMany(componentIds, 'to load aspects from the workspace', loadOpts, throwOnError);
} catch (err) {
this.throwWsJsoncAspectNotFoundError(err);
throw err;
}
}
/**
* split the provided components into 2 groups, one which are workspace components and the other which are not.
* @param components
* @returns
*/
async groupComponentsByWorkspaceExistence(components, resolveEnvsFromRoots) {
let workspaceComps = [];
let nonWorkspaceComps = [];
await Promise.all(components.map(async component => {
const existOnWorkspace = await this.workspace.hasId(component.id);
existOnWorkspace ? workspaceComps.push(component) : nonWorkspaceComps.push(component);
}));
if (resolveEnvsFromRoots) {
const {
rootComps,
nonRootComps
} = await this.groupComponentsByLoadFromRootComps(nonWorkspaceComps);
workspaceComps = workspaceComps.concat(rootComps);
nonWorkspaceComps = nonRootComps;
}
return {
workspaceComps,
nonWorkspaceComps
};
}
async groupComponentsByLoadFromRootComps(components) {
const rootComps = [];
const nonRootComps = [];
await Promise.all(components.map(async component => {
const shouldLoadFromRootComps = await this.shouldLoadFromRootComps(component);
if (shouldLoadFromRootComps) {
rootComps.push(component);
return;
}
nonRootComps.push(component);
}));
return {
rootComps,
nonRootComps
};
}
async shouldLoadFromRootComps(component) {
const rootDir = await this.workspace.getComponentPackagePath(component);
const rootDirExist = await _fsExtra().default.pathExists(rootDir);
const aspectFilePath = await this.aspectLoader.getAspectFilePath(component, rootDir);
const aspectFilePathExist = aspectFilePath ? await _fsExtra().default.pathExists(aspectFilePath) : false;
const pluginFiles = await this.aspectLoader.getPluginFiles(component, rootDir);
// checking that we have the root dir (this means it's an aspect that needs to be loaded from there)
// and validate that localPathExist so we can
// really load the component from that path (if it's there it means that it's an env)
if (rootDirExist && (aspectFilePathExist || pluginFiles.length)) {
return true;
}
// If the component has env.jsonc we want to list it to be loaded from the root folder
// even if it's not there yet
// in that case we will fail to load it, and the user will need to run bit install
if (this.envs.hasEnvManifest(component)) {
return true;
}
return false;
}
/**
* split the provided components into 2 groups, one which are workspace components and the other which are not.
* @param components
* @returns
*/
async groupAspectDefsByWorkspaceExistence(aspectDefs) {
const workspaceDefs = [];
const nonWorkspaceDefs = [];
await Promise.all(aspectDefs.map(async aspectDef => {
const id = aspectDef.component?.id;
const existOnWorkspace = id ? await this.workspace.hasId(id) : true;
if (existOnWorkspace) {
workspaceDefs.push(aspectDef);
return;
}
const shouldLoadFromRootComps = aspectDef.component ? await this.shouldLoadFromRootComps(aspectDef.component) : undefined;
if (shouldLoadFromRootComps) {
workspaceDefs.push(aspectDef);
return;
}
nonWorkspaceDefs.push(aspectDef);
}));
return {
workspaceDefs,
nonWorkspaceDefs
};
}
async groupIdsByWorkspaceExistence(ids, resolveEnvsFromRoots) {
let workspaceIds = [];
let nonWorkspaceIds = [];
await Promise.all(ids.map(async id => {
const existOnWorkspace = await this.workspace.hasId(id);
existOnWorkspace ? workspaceIds.push(id) : nonWorkspaceIds.push(id);
}));
// We need to bring the components in order to really group them with taking the root comps into account
const scopeComponents = await this.importAndGetAspects(nonWorkspaceIds);
const {
nonWorkspaceComps,
workspaceComps
} = await this.groupComponentsByWorkspaceExistence(scopeComponents, resolveEnvsFromRoots);
workspaceIds = workspaceIds.concat(workspaceComps.map(c => c.id));
nonWorkspaceIds = nonWorkspaceComps.map(c => c.id);
return {
workspaceIds,
nonWorkspaceIds
};
}
}
exports.WorkspaceAspectsLoader = WorkspaceAspectsLoader;
function ignoreAspectLoadingError(err) {
// Ignoring that error as probably we are in the middle of the installation process
// so we didn't yet compile the aspect to esm correctly
if (err.message.includes(`Cannot use 'import.meta' outside a module`)) {
return true;
}
return false;
}
//# sourceMappingURL=workspace-aspects-loader.js.map