feat(envs): remove core envs from the manifest and their sources from the workspace - #10465
feat(envs): remove core envs from the manifest and their sources from the workspace#10465davidfirst wants to merge 203 commits into
Conversation
PR Summary by QodoLoad former core envs as regular registry envs with legacy version pinning
AI Description
Diagram
High-Level Assessment
Files changed (15)
|
Code Review by Qodo
1. @ts-nocheck disables fixture checking
|
…cope capsules, restore bit-aspect cmd
|
Code review by qodo was updated up to the latest commit c7dd1a7 |
…cted pre-install warning, regen references
|
Code review by qodo was updated up to the latest commit e6418b9 |
|
Code review by qodo was updated up to the latest commit c9eca3d |
…aspect and env envs to core
…eambit/bit into remove-core-envs-from-manifest
|
Code review by qodo was updated up to the latest commit 3f5c24e |
…nv templates on demand - register legacy core env ids as core extension names so their config entries stay name-only (versionless): prevents env-as-dependency edges that created circular TS project references in lane/tag builds - require the aspect env eslint/prettier configs lazily (saves ~400 file reads per bit command) - bit create: fall back to --env for template lookup, incl. templates registered on the generator slot by envs loaded from the global scope - rewrite e2e node-env fixtures to compose on the core aspect env instead of @teambit/node
|
Code review by qodo was updated up to the latest commit 0607c7d |
- teambit.harmony/aspect and teambit.envs/env become regular envs with pinned legacy versions. components using them get the exact released behavior after bit install (the react-free aspect env rewrite is reverted) - move the bit-aspect template and harmony starters to the generator aspect so 'bit create bit-aspect' and 'bit new' work without loading the env - bind manifest deps of legacy envs to pinned versions in scope context (models built when these envs were core don't list them as dependencies) - load the full manifest graph when loading aspects from the global scope - keep legacy core env ids versionless when configured via bit create/env set
|
Code review by qodo was updated up to the latest commit b23b273 |
…ion when available - fixes the ci snap failure: pinned-version copies of workspace components leaked into the load groups and into the snap list - review fixes: index-based BFS queue in getDependentsIds, guard the typescript require in the fallback compiler, suppress legacy-env load failures only when the env package itself is missing, match both quote styles when detecting fixture env packages
|
Code review by qodo was updated up to the latest commit 94eddce |
| if (this.workspace.hasId(aspectComponent.id, { ignoreVersion: true })) { | ||
| const localPath = await this.workspace.getComponentPackagePath(aspectComponent); | ||
| this.resolvedInstalledAspects.set(aspectStringId, localPath); | ||
| return localPath; |
There was a problem hiding this comment.
1. Wrong aspect path resolution 🐞 Bug ≡ Correctness
WorkspaceAspectsLoader.resolveInstalledAspectRecursively detects a workspace aspect using ignoreVersion=true but then calls workspace.getComponentPackagePath(), which computes isInWorkspace using an exact-version hasId(). When the dependency graph provides a versioned ID that doesn’t exactly match the workspace bitmap, getRuntimeModulePath may pick the versioned root path instead of the workspace instance, causing aspect resolution/require to fail despite the aspect existing in the workspace.
Agent Prompt
### Issue description
`resolveInstalledAspectRecursively()` treats an aspect as a workspace component when `workspace.hasId(id, { ignoreVersion: true })` is true, but then calls `workspace.getComponentPackagePath(aspectComponent)`. `getComponentPackagePath()` passes `isInWorkspace: this.hasId(component.id)` (exact version) into `dependencyResolver.getRuntimeModulePath()`, so versioned IDs coming from the graph can be treated as “not in workspace” even though an ignore-version match exists.
This can select a different module root (often the versioned/root-node_modules path) instead of the workspace component instance intended by the ignore-version check, which can break requiring aspects (e.g. missing/incorrect dists).
### Issue Context
This inconsistency only triggers when the graph supplies a versioned `aspectComponent.id` that does not exactly match the bitmap version for the same component (but does match without version).
### Fix Focus Areas
- scopes/workspace/workspace/workspace-aspects-loader.ts[892-901]
- scopes/workspace/workspace/workspace.ts[2182-2188]
- scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts[567-571]
### Suggested fix
Pick one of these (prefer the simplest consistent approach):
1) **Make `Workspace.getComponentPackagePath()` consider ignore-version membership**:
- Change `isInWorkspace: this.hasId(component.id)` to `isInWorkspace: this.hasId(component.id, { ignoreVersion: true })` (or equivalent via `getIdIfExist`).
2) **Normalize to the bitmap ID before computing the path** inside `resolveInstalledAspectRecursively()`:
- If `workspace.hasId(aspectComponent.id, { ignoreVersion: true })` is true, obtain the canonical workspace id (e.g. `workspace.getIdIfExist(aspectComponent.id)`), and compute the runtime module path using that canonical id / force `isInWorkspace: true` for the path selection.
Add/adjust a test or log-based reproduction that exercises a workspace aspect whose dependency graph references a different version than the bitmap but same id-without-version.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 841136a |
…om-manifest # Conflicts: # .bitmap # e2e/harmony/env.e2e.ts # pnpm-lock.yaml # scopes/compilation/compiler/compiler.main.runtime.ts # scopes/workspace/workspace/workspace.ts
|
Code review by qodo was updated up to the latest commit e9adba6 |
|
Code review by qodo was updated up to the latest commit b0175b9 |
|
Code review by qodo was updated up to the latest commit e9924d4 |
…om-manifest # Conflicts: # .bitmap # e2e/harmony/deps-in-capsules.e2e.ts # e2e/harmony/dev-files.e2e.ts # e2e/harmony/pkg-manager-config.e2e.ts # e2e/harmony/root-components.e2e.ts # pnpm-lock.yaml
| const [inFlightIds, idsToLoad] = partition(nonVisitedId, (id) => | ||
| this.scope.inFlightAspectLoads.has(aspectLoadInFlightKey(id)) | ||
| ); | ||
| if (inFlightIds.length) { | ||
| this.logger.debug( | ||
| `getManifestsGraphRecursively, skipping aspects that are already loading: ${inFlightIds.join(', ')}` | ||
| ); | ||
| } | ||
| nonVisitedId = idsToLoad; | ||
| if (!nonVisitedId.length) { | ||
| return { manifests: [], potentialPluginsIds: [] }; |
There was a problem hiding this comment.
1. Scope loads return prematurely 🐞 Bug ☼ Reliability
The shared in-flight set treats an aspect being loaded by an independent concurrent scope.loadAspects() call as a recursive cycle, causing the second call to return an empty manifest result immediately. Its caller then completes without waiting for the required aspect to be registered, creating timing-dependent missing-aspect failures.
Agent Prompt
## Issue description
`ScopeMain.inFlightAspectLoads` is shared across separate `ScopeAspectsLoader` instances, but an in-flight ID is simply omitted as though it were recursion in the current call. Concurrent callers therefore return successfully without waiting for the aspect load that they depend on.
## Issue Context
`ScopeMain.loadAspects()` constructs a new loader for every invocation. `getManifestsAndLoadAspects()` treats the empty manifest result as success and returns, so there is no later synchronization with the original load.
## Fix Focus Areas
- scopes/scope/scope/scope-aspects-loader.ts[89-112]
- scopes/scope/scope/scope-aspects-loader.ts[115-151]
- scopes/scope/scope/scope.main.runtime.ts[205-210]
- scopes/scope/scope/scope.main.runtime.ts[309-317]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 994599c |
| if (componentId.hasVersion()) return componentId; | ||
| if (!this.envs.isLegacyCoreEnv(componentId.toStringWithoutVersion())) return componentId; |
There was a problem hiding this comment.
1. Legacy pin applied too late 🐞 Bug ≡ Correctness
resolveLegacyCoreEnvsVersions() preserves an already-versioned ID, but its inputs have already passed through resolveMultipleComponentIds(), which can resolve a versionless former-core env to the latest locally available version. A workspace containing a newer env version can therefore load it instead of the compatibility-pinned version, making legacy component behavior non-deterministic.
Agent Prompt
## Issue description
Versionless legacy-core env IDs are resolved before their pinned compatibility version is applied. If scope resolution supplies a locally available version, `resolveLegacyCoreEnvsVersions()` returns it unchanged instead of selecting the declared pin.
## Issue Context
Apply legacy-core pinning to the original versionless request before calling `resolveMultipleComponentIds()`, while continuing to prefer an actual workspace component when one exists. Add coverage where the local scope contains a newer former-core env version and assert that a versionless request still loads the pinned version.
## Fix Focus Areas
- scopes/workspace/workspace/workspace-aspects-loader.ts[159-160]
- scopes/workspace/workspace/workspace-aspects-loader.ts[250-263]
- scopes/workspace/workspace/workspace.ts[2369-2372]
- scopes/scope/scope/scope.main.runtime.ts[1033-1044]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 35b91bf |
| @@ -1,3 +1,7 @@ | |||
| // @ts-nocheck | |||
There was a problem hiding this comment.
1. @ts-nocheck disables fixture checking 📘 Rule violation ⚙ Maintainability
The added @ts-nocheck directive suppresses strict TypeScript checking for the entire fixture instead of resolving the incompatible types. Similar suppressions were added across several modified environment fixtures, allowing future type errors to pass unnoticed.
Agent Prompt
## Issue description
The PR adds `@ts-nocheck` to environment fixtures, disabling TypeScript checking for each entire file and weakening strict compilation guarantees.
## Issue Context
The comments identify nominal incompatibilities between repository-source and published-package types. Address those incompatibilities with narrowly typed adapters or localized casts rather than suppressing all checking.
## Fix Focus Areas
- components/legacy/e2e-helper/excluded-fixtures/extensions/babel-env/babel-env.extension.ts[1-4]
- components/legacy/e2e-helper/excluded-fixtures/extensions/custom-react-env/custom-react-env.main.runtime.ts[1-4]
- components/legacy/e2e-helper/excluded-fixtures/extensions/multiple-compilers-env/multiple-compilers-env.extension.ts[1-4]
- components/legacy/e2e-helper/excluded-fixtures/extensions/node-env/node-env.extension.ts[1-5]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| // keys) are typically versionless while the graph provides versioned ids, and the resolved | ||
| // path (the package dir in the workspace node_modules) is version-agnostic anyway. | ||
| const aspectIdWithoutVersion = aspectComponent.id.toStringWithoutVersion(); | ||
| if (rootIds.some((rootId) => rootId === aspectStringId || rootId.split('@')[0] === aspectIdWithoutVersion)) { |
There was a problem hiding this comment.
2. Versioned roots load wrong code 🐞 Bug ≡ Correctness
resolveInstalledAspectRecursively() treats a versioned configured root such as scope/aspect@1 as matching a graph node for scope/aspect@2. It then resolves the @2 node from the root package path, potentially executing @1 code under the @2 component identity.
Agent Prompt
## Issue description
Installed-aspect root matching strips the version from every configured root ID. This allows a graph node for one version to resolve from another version's root package path.
## Issue Context
Use exact matching when a root ID has a version. Ignore versions only when the root ID is itself versionless, or where explicitly required for legacy single-instance envs.
## Fix Focus Areas
- scopes/workspace/workspace/workspace-aspects-loader.ts[883-900]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| const isRequested = | ||
| !opts.requestedIds || | ||
| requestedIds.has(compStringId) || | ||
| requestedIdsWithoutVersion.has(component.id.toStringWithoutVersion()); |
There was a problem hiding this comment.
3. Dependency version becomes requested 🐞 Bug ☼ Reliability
getInstalledAspectResolver() classifies a dependency as explicitly requested whenever its versionless ID matches a request, even if the request specified another version. With throwOnError enabled, a resolution failure for that dependency version aborts the load instead of following the intended dependency skip-and-report path.
Agent Prompt
## Issue description
Requested-aspect classification ignores versions for every request, causing a different dependency version to be treated as an explicitly requested root and changing failure handling from skip to throw.
## Issue Context
Use exact matching for versioned requests. Apply versionless matching only when the original requested ID is versionless or belongs to an explicitly single-instance legacy env.
## Fix Focus Areas
- scopes/workspace/workspace/workspace-aspects-loader.ts[827-839]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 8541cd2 |
…om-manifest # Conflicts: # .bitmap # pnpm-lock.yaml # scopes/harmony/node/jest/jest.config.js # scopes/mdx/mdx/mdx.detector.spec.ts # scopes/react/react/webpack/webpack.config.base.ts # scopes/semantics/schema/schema.main.runtime.ts
| function buildFallbackTypescriptCompiler() { | ||
| let ts; | ||
| try { | ||
| ts = require('typescript'); | ||
| } catch { | ||
| throw new Error( | ||
| 'the fallback compiler requires the "typescript" package, which is not installed. run "bit install" to install the component env' | ||
| ); | ||
| } |
There was a problem hiding this comment.
1. Fallback compiler hard-fails 🐞 Bug ☼ Reliability
The new fallback TypeScript compiler throws if the "typescript" module can’t be resolved, so code paths that intentionally fall back to this env (e.g. legacy-core env configured but not installed/loaded yet) can fail with an exception instead of staying operational until "bit install" runs.
Agent Prompt
### Issue description
The fallback compiler used by the fallback default env throws when `require('typescript')` fails. This defeats the fallback env’s intent (“allows bit to keep functioning until `bit install` installs the env”) because the fallback path can turn a missing/unloaded env into a hard failure.
### Issue Context
The fallback env is used when an env can’t be resolved/loaded (including legacy core envs not installed yet). The fallback env advertises a minimal TS transpiler, but that transpiler is gated on a dependency that may not be available.
### Fix Focus Areas
- Ensure the fallback compiler always has a transpiler available (e.g., add `typescript` as a guaranteed runtime dependency of the relevant package/binary, or replace the fallback transpiler with an already-guaranteed transpiler in the repo).
- Alternatively, degrade gracefully: when `typescript` is missing, return a no-op compiler or surface a controlled `NonLoadedEnv`/actionable issue rather than throwing during env bootstrap.
- scopes/envs/envs/fallback-typescript-compiler.ts[17-25]
- scopes/envs/envs/environments.main.runtime.ts[242-257]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 86d1aed |
…om-manifest # Conflicts: # .bitmap # .circleci/config.yml # e2e/harmony/dependency-resolver.e2e.ts # pnpm-lock.yaml # scopes/envs/envs/environments.main.runtime.ts # scopes/harmony/aspect/babel/babel-config.ts # scopes/harmony/bit/manifests.ts # scopes/harmony/testing/load-aspect/core-aspects-ids.json
| const compsToImportDepsFor = useLazyImport | ||
| ? components | ||
| : components.filter((comp) => workspaceIds.find((id) => id.isEqual(comp.id))); | ||
|
|
||
| const allDeps = (await Promise.all(compOnWorkspaceOnly.map(getDepsFunc))).flat(); | ||
| const allDepsNotImported = allDeps.filter((d) => !this.importedIds.includes(d.toString())); | ||
| const allDeps = (await Promise.all(compsToImportDepsFor.map((c) => this.getAllDepsUnfiltered(c)))).flat(); | ||
| const allDepsNotImported = allDeps.filter((d) => !this.importedIds.has(d.toString())); |
There was a problem hiding this comment.
1. Lazy import contradicts docs 🐞 Bug ⚙ Maintainability
GraphFromFsBuilder.importObjects() documents that lazy mode "only import[s] filtered dependencies" to avoid fetching huge trees, but the updated implementation always prefetches direct deps via getAllDepsUnfiltered() when shouldLoadItsDeps is set. This mismatch can mislead maintainers and can increase work when building aspects-only graphs (e.g. from WorkspaceAspectsLoader.getAspectsGraphWithoutCore).
Agent Prompt
### Issue description
`GraphFromFsBuilder.importObjects()` states that in lazy-import mode it will "only import filtered dependencies", but the current implementation collects dependencies using `getAllDepsUnfiltered()` when `useLazyImport` is true. This contradicts the method’s own docstring and the intended purpose of `shouldLoadItsDeps` (building a filtered/aspects-only graph without pulling large dependency trees).
### Issue Context
This graph builder is used by `WorkspaceAspectsLoader.getAspectsGraphWithoutCore()` to build an aspects-only graph.
### Fix Focus Areas
- scopes/workspace/workspace/build-graph-from-fs.ts[121-147]
### What to change
Choose one of the following (either is acceptable, but it should be explicit):
1) **Update the docstring/comments** to reflect the actual behavior (lazy mode batch-prefetches *unfiltered direct deps* to avoid per-dep network round-trips caused by `shouldLoadItsDeps`), *or*
2) **Change the lazy-mode dep collection** to match the documentation (only prefetch deps that pass the `shouldLoadItsDeps` filter), and ensure this doesn’t regress performance (e.g., avoid per-dep remote fetches by batching where possible).
Add/adjust a regression test (if available for graph building) asserting the intended behavior for lazy mode with a `shouldLoadItsDeps` filter.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 53f7c45 |
Removes the env aspects (
teambit.react/react,teambit.harmony/node,teambit.harmony/aspect,teambit.envs/env,teambit.mdx/mdx,teambit.mdx/readme) from the core manifest to slim Bit. They now act like any other env, installed from the registry.New default env:
teambit.harmony/empty-env(core). A totally empty env - no compiler, no tester, no preview, no dependency policy. Components with no env configured use it and work fully offline out of the box (add → compile no-op → tag/snap → export). Since it has no behavior, it has nothing to drift when bit itself changes - the one env that is safe to keep core (and versionless in models) forever. To get a dev experience, users configure a real env (bit createflows already do).teambit.harmony/aspectandteambit.envs/envare removed like the rest, with zero behavior change. Their implementation is untouched (react-based, preview and all) - users get the exact released behavior afterbit install(the pinned-version machinery auto-installs them). New envs are created from the bitdev env packages (bit create react-envetc.), so these built-in envs are legacy surface. Thebit-aspecttemplate and the harmony starters moved to the core generator aspect, sobit create bit-aspectandbit newkeep working out of the box (the created aspect needsbit installbefore it loads, like any env).Versionless by design. Config entries for the removed env ids are persisted by name, without a version - exactly as they were when core (registered as core-extension names). Keeping them versionless is deliberate on two counts. First, it keeps the env from becoming a dependency edge of its own components; otherwise an env such as react, whose dependency closure includes components that use it as their env, creates circular TS project references and breaks lane/tag builds. Second, it preserves forward compatibility: a re-tag under the new bit keeps the env id versionless, so a teammate who has not upgraded yet (whose bit still ships these as core) can import the re-tagged component and resolve the env - instead of receiving a versioned id their bit has no component for. The alternative (showing the component as modified and pinning the env on the next tag) would silently break not-yet-upgraded consumers.
Backward compatibility. Old components have the removed envs saved without a version.
legacy-core-envs.tsmaps them to pinned versions, applied only at the resolution/loading/install level - stored objects are never mutated. Versionless legacy ids match the env slot ignoring version,bit installauto-adds their packages, and single-instance semantics are enforced (a loaded version is reused rather than loading another copy). Not-installed legacy envs fail fast with aNonLoadedEnvissue suggestingbit install- no scope-capsule isolation in workspace context (which used to take minutes). Old components load without being reported as modified, and re-tagging keeps the env versionless - covered end-to-end bye2e/harmony/legacy-core-env-back-compat.e2e.ts, which imports a component exported by a pre-removal bit (env saved versionless) and asserts it is not modified and stays versionless after a re-tag.Relocated core wiring: the
bit aspectCLI command moved toteambit.workspace/workspace;validateBeforePersistHookmoved toteambit.dependencies/dependency-resolver; the dead@teambit/legacylink is now skipped instead of crashing.Also fixes latent issues this path exposed: versionless seeders filtering out all manifests in
loadExtensionsByManifests, circular env chains causing infinite component-load recursion, versioned core-aspect ids escaping core filters anddoRequiremutating shared core manifests, stack overflows from recursive graph traversal, and a spuriousMissingDistsissue for compiler-less envs.Verified locally: fresh workspace (JS and TS components) - clean status in ~1s, tag/snap/export offline,
bit envs/bit testgraceful; this repo's workspace - status/insights/list-core clean; the seven repo components that relied on the default env are now explicitly set to the node env.bit create <template> --env <removed-env>loads the env's templates on demand from the global scope (pinned version); this path also loads the full manifest graph, and binds manifest deps of legacy envs to their pinned versions (models built when these envs were core don't list them as dependencies). The e2esetCustomEnvhelper installs the env package the fixture imports (e.g.@teambit/node).Also removes the former-core env sources from this repo's workspace (
scopes/harmony/node,scopes/react/react,scopes/harmony/aspect,scopes/envs/env,scopes/mdx/mdx,scopes/docs/readme) - bit now dogfoods them as installed packages like any consumer, and the source-vs-installed duality is gone. Making this pass end-to-end surfaced several general fixes that ride along:.docs.mdximports are detected even when the mdx aspect isn't loaded (latent gap once mdx is no longer core - without it, docs deps silently drop from dependency computation and preview bundling fails).Module._extensionsrequire hooks are restored after each build task. An in-process tester leaves@babel/register's pirates hook installed; the hook claims all.jsfiles (including node_modules, regardless of babelignoreconfig) and breaksrequire()of ESM-only packages in every later task in the process (pirates drops theformatarg node >=22.12 uses to routerequire(esm)).import()instead of a top-level require, immune to the same stale-hook hazard.@bit-no-check; timings manifest covers the split spec files so shard balancing accounts for the heavier env-install suites.