From d757c14be54ca881e2c3900bd79829b594749603 Mon Sep 17 00:00:00 2001 From: David First Date: Tue, 25 Nov 2025 14:56:25 -0500 Subject: [PATCH] fix: detect missing deps in custom file extensions on first install ## Problem When importing a component that uses a custom env (e.g., Vue env) with custom file extension detectors (e.g., .vue files), the first install after import fails to detect dependencies imported in those custom files. A second `bit install` is required to detect and install the missing packages. This happens because: 1. During the first install, the env package isn't yet in node_modules 2. Components are loaded and dependencies detected without the env's detectors 3. After install, the env is available but cached component data with incomplete dependency info is used 4. Missing dependencies are never detected or installed ## Solution 1. **Capture envs that fail to load before install**: Track which envs aren't available before the first install runs. 2. **Re-check for missing deps after install**: When `addMissingDeps` is enabled and there were envs that failed to load before install: - Clear cache to get fresh component data - Re-detect missing packages with env detectors now available - If found, add them to root policy and run another install 3. **Enable addMissingDeps during import**: Set `addMissingDeps: true` when `installPackagesGracefully` is called during component import to trigger the post-install re-check. ## Test Plan Tested with `learnbit-vue.graphql/user-app` which uses `.vue` files: - `bit import learnbit-vue.graphql/user-app` now correctly installs missing Vue composable dependency on first import - `bit import -x` followed by `bit install --add-missing-deps` works correctly - `bit status` shows no issues after import --- .../component-writer.main.runtime.ts | 5 +++ .../workspace/install/install.main.runtime.ts | 34 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/scopes/component/component-writer/component-writer.main.runtime.ts b/scopes/component/component-writer/component-writer.main.runtime.ts index 9f1d8fd20b80..8424decd3a30 100644 --- a/scopes/component/component-writer/component-writer.main.runtime.ts +++ b/scopes/component/component-writer/component-writer.main.runtime.ts @@ -111,6 +111,11 @@ export class ComponentWriterMain { import: false, writeConfigFiles: !skipWriteConfigFiles, dependenciesGraph: await this.workspace.scope.getDependenciesGraphByComponentIds(componentIds), + // Enable addMissingDeps to handle cases where env detectors aren't available on first install + // (e.g., when importing a component that uses a custom env with custom file extension + // detectors like .vue files). The env gets installed during the first pass, and then + // addMissingDeps will re-detect dependencies with the env now available. + addMissingDeps: true, }; await this.installer.install(undefined, installOpts); this.logger.debug('installPackagesGracefully, completed installing packages successfully'); diff --git a/scopes/workspace/install/install.main.runtime.ts b/scopes/workspace/install/install.main.runtime.ts index 22673d1149a4..73602110cf20 100644 --- a/scopes/workspace/install/install.main.runtime.ts +++ b/scopes/workspace/install/install.main.runtime.ts @@ -369,6 +369,9 @@ export class InstallMain { linkNestedDepsInNM: !this.workspace.isLegacy && !hasRootComponents, }; const { linkedRootDeps } = await this.calculateLinks(linkOpts); + // Capture envs that failed to load before install - they may become available after install + // and we'll need to re-detect dependencies for components using custom file extensions (e.g., .vue) + const envsFailedToLoadBeforeInstall = this.envs.getFailedToLoadEnvs(); // eslint-disable-next-line prefer-const let { mergedRootPolicy, componentsAndManifests: current } = await this._getComponentsManifestsAndRootPolicy( installer, @@ -484,6 +487,37 @@ export class InstallMain { // After pruning we need reload moved envs, as during the pruning the old location might be deleted await this.reloadMovedEnvs(); } + + // If addMissingDeps was requested and there were envs that failed to load before install, + // we need to re-check for missing dependencies now that envs may be available. + // This handles the case where a component uses a custom file extension (e.g., .vue) that requires + // an env detector that wasn't available during the first dependency detection pass. + if (options?.addMissingDeps && envsFailedToLoadBeforeInstall.length > 0) { + await this.workspace.clearCache(); + const missingPackages = await this._getAllMissingPackages(); + if (missingPackages.length > 0) { + this.logger.debug( + `found ${missingPackages.length} missing packages after envs became available: ${missingPackages.join(', ')}` + ); + await this._addPackages(missingPackages, { skipUnavailable: options?.skipUnavailable }); + // Run another install to install the newly discovered missing packages + const newManifests = await this._getComponentsManifests(installer, mergedRootPolicy, calcManifestsOpts); + await installer.installComponents( + this.workspace.path, + newManifests.manifests, + mergedRootPolicy, + newManifests.componentDirectoryMap, + { + linkedDependencies, + installTeambitBit: false, + forcedHarmonyVersion, + }, + pmInstallOptions + ); + current = newManifests; + } + } + // this is now commented out because we assume we don't need it anymore. // even when the env was not loaded before and it is loaded now, it should be fine because the dependencies-data // is only about the auto-detect-deps. there are two more steps: version-resolution and apply-overrides that