UNPKG

@teambit/workspace

Version:
809 lines (790 loc) • 37.3 kB
"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