diff --git a/.bitmap b/.bitmap index 8a92b31899d7..05f0295b37c2 100644 --- a/.bitmap +++ b/.bitmap @@ -583,6 +583,19 @@ "mainFile": "index.ts", "rootDir": "scopes/workspace/eject" }, + "empty-env": { + "name": "empty-env", + "scope": "", + "version": "", + "defaultScope": "teambit.envs", + "mainFile": "index.ts", + "rootDir": "scopes/envs/empty-env", + "config": { + "teambit.envs/envs": { + "env": "teambit.envs/empty-env" + } + } + }, "entities/lane-diff": { "name": "entities/lane-diff", "scope": "teambit.lanes", diff --git a/components/legacy/e2e-helper/e2e-command-helper.ts b/components/legacy/e2e-helper/e2e-command-helper.ts index 4ec636340fb3..f3e2e364112d 100644 --- a/components/legacy/e2e-helper/e2e-command-helper.ts +++ b/components/legacy/e2e-helper/e2e-command-helper.ts @@ -894,7 +894,14 @@ export default class CommandHelper { runCmdOpts?: { envVariables?: Record } ) { const parsedOpts = this.parseOptions(options); - return this.runCmd(`bit install ${packages} ${parsedOpts}`, cwd, 'pipe', undefined, false, runCmdOpts?.envVariables); + return this.runCmd( + `bit install ${packages} ${parsedOpts}`, + cwd, + 'pipe', + undefined, + false, + runCmdOpts?.envVariables + ); } update(flags?: string) { return this.runCmd(`bit update ${flags || ''}`); diff --git a/components/ui/inputs/lane-selector_1/lane-menu-item.tsx b/components/ui/inputs/lane-selector_1/lane-menu-item.tsx index c70992527713..b94beb141abf 100644 --- a/components/ui/inputs/lane-selector_1/lane-menu-item.tsx +++ b/components/ui/inputs/lane-selector_1/lane-menu-item.tsx @@ -8,7 +8,7 @@ import { MenuLinkItem } from '@teambit/design.ui.surfaces.menu.link-item'; import { UserAvatar } from '@teambit/design.ui.avatar'; import { TimeAgo } from '@teambit/design.ui.time-ago'; import { Ellipsis } from '@teambit/design.ui.styles.ellipsis'; -import { Icon } from '@teambit/design.elements.icon' +import { Icon } from '@teambit/design.elements.icon'; import styles from './lane-menu-item.module.scss'; export type LaneMenuItemProps = { diff --git a/components/ui/models/lanes-model/lanes-model.ts b/components/ui/models/lanes-model/lanes-model.ts index 568cc82f270f..42e935f2406b 100644 --- a/components/ui/models/lanes-model/lanes-model.ts +++ b/components/ui/models/lanes-model/lanes-model.ts @@ -43,7 +43,7 @@ export type LaneQueryResult = { updatedAt?: Date; updatedBy?: LaneQueryLaneOwner; dependents?: Array; - slug?: string + slug?: string; deleted?: boolean; }; /** @@ -80,7 +80,7 @@ export type LaneModel = { updatedAt?: Date; updatedBy?: LaneQueryLaneOwner; dependents?: ComponentID[]; - slug?: string + slug?: string; deleted?: boolean; }; /** @@ -234,7 +234,7 @@ export class LanesModel { createdBy, deleted, dependents: dependents?.map((dependent) => ComponentID.fromObject(dependent)), - slug + slug, }; } @@ -387,10 +387,11 @@ export class LanesModel { }; resolveComponentFromUrl = (idFromUrl: string, laneId?: LaneId) => { - const comps = ( - (laneId && this.lanes.find((lane) => lane.slug === laneId.toString() || lane.id.isEqual(laneId))) || - this.viewedLane - )?.components || []; + const comps = + ( + (laneId && this.lanes.find((lane) => lane.slug === laneId.toString() || lane.id.isEqual(laneId))) || + this.viewedLane + )?.components || []; const includesScope = idFromUrl.includes('.'); if (includesScope) { return comps.find((component) => component.toStringWithoutVersion() === idFromUrl); diff --git a/docs/removing-core-envs.md b/docs/removing-core-envs.md new file mode 100644 index 000000000000..7fb1e7a733c3 --- /dev/null +++ b/docs/removing-core-envs.md @@ -0,0 +1,94 @@ +# Removing Core Environments from Bit Core + +## Overview + +This document describes the implementation for removing **all** core environments from Bit's core binary to reduce its size and node_modules footprint. These environments will become regular dependencies instead of bundled core aspects. + +### Environments Being Removed + +All of the following environments are being removed from core: + +- `teambit.harmony/aspect` - Aspect environment +- `teambit.html/html` - HTML environment +- `teambit.mdx/mdx` - MDX environment +- `teambit.envs/env` - Env environment (for creating custom envs) +- `teambit.mdx/readme` - Readme environment +- `teambit.harmony/bit-custom-aspect` - Custom aspect environment +- `teambit.harmony/node` - Node.js environment +- `teambit.react/react` - React environment +- `teambit.react/react-native` - React Native environment + +## Background + +**Problem**: Core environments are currently bundled with Bit, increasing binary size and node_modules footprint unnecessarily. + +**Challenge**: Components using core envs were saved with IDs without versions (e.g., `teambit.harmony/node`) because they were core aspects. After removal, they need versions like any other dependency. + +**Solution**: Implement backward compatibility by assigning versions to legacy core envs in memory during component load, without migrating existing snapshots. Use `teambit.envs/empty-env` as the new default environment. + +## Implementation + +### Empty Env Solution + +The key challenge was the default env (`teambit.harmony/node`) being removed from core but still needed during initialization. The solution: + +**Created `teambit.envs/empty-env`** - a minimal environment with no compiler, tester, or tools that remains in core as a lightweight aspect. This becomes the new `DEFAULT_ENV`. + +Files created in `scopes/envs/empty-env/`: + +- `empty-env.env.ts` - Empty env class +- `empty-env.aspect.ts` - Aspect definition +- `empty-env.main.runtime.ts` - Runtime registration +- `index.ts` - Type exports + +### Backward Compatibility + +**`scopes/envs/envs/environments.main.runtime.ts`** - Key changes: + +```typescript +// Track all 9 legacy core envs for backward compatibility +private getLegacyCoreEnvsIds(): string[] { + return [ + 'teambit.harmony/aspect', + 'teambit.html/html', + 'teambit.mdx/mdx', + 'teambit.envs/env', + 'teambit.mdx/readme', + 'teambit.harmony/bit-custom-aspect', + 'teambit.harmony/node', + 'teambit.react/react', + 'teambit.react/react-native', + ]; +} + +// Include empty-env as a core env along with legacy envs +getCoreEnvsIds(): string[] { + return ['teambit.envs/empty-env', ...this.getLegacyCoreEnvsIds()]; +} + +// Changed DEFAULT_ENV constant +export const DEFAULT_ENV = 'teambit.envs/empty-env'; +``` + +The `resolveEnv()` method handles version assignment for legacy envs: when loading components with envs lacking versions, it searches the component's aspects for a versioned reference, falls back to envSlot, and assigns the latest available version in memory. + +### Core Aspects + +**`scopes/harmony/bit/manifests.ts`** - Removed all env aspects from manifestsMap except EmptyEnvAspect: + +```typescript +import { EmptyEnvAspect } from '@teambit/empty-env'; + +export const manifestsMap = { + // ... other aspects + [EmptyEnvAspect.id]: EmptyEnvAspect, +}; +``` + +## Benefits + +- **Reduced binary size**: Core envs no longer bundled with Bit +- **Smaller node_modules**: Only needed envs are installed +- **Backward compatible**: Old components without env versions work via automatic version resolution +- **No breaking changes**: Version assignment happens transparently in memory +- **Minimal overhead**: Empty-env adds negligible size to core diff --git a/e2e/harmony/dependencies/allow-scripts.e2e.ts b/e2e/harmony/dependencies/allow-scripts.e2e.ts index 3269d1d4c584..6e19297ecdf3 100644 --- a/e2e/harmony/dependencies/allow-scripts.e2e.ts +++ b/e2e/harmony/dependencies/allow-scripts.e2e.ts @@ -90,7 +90,9 @@ chai.use(chaiFs); helper.command.setConfig('registry', npmCiRegistry.getRegistryUrl()); // The installation below would fail if we didn't explicitly disallow // @pnpm.e2e/failing-postinstall in allowScripts. - helper.command.install('@pnpm.e2e/failing-postinstall @pnpm.e2e/pre-and-postinstall-scripts-example --disallow-scripts=@pnpm.e2e/failing-postinstall --allow-scripts=@pnpm.e2e/pre-and-postinstall-scripts-example'); + helper.command.install( + '@pnpm.e2e/failing-postinstall @pnpm.e2e/pre-and-postinstall-scripts-example --disallow-scripts=@pnpm.e2e/failing-postinstall --allow-scripts=@pnpm.e2e/pre-and-postinstall-scripts-example' + ); workspaceJsonc = helper.workspaceJsonc.read(); }); after(() => { @@ -136,14 +138,19 @@ chai.use(chaiFs); }); // The installation below would fail if we didn't explicitly disallow // @pnpm.e2e/failing-postinstall in allowScripts. - helper.command.install('@pnpm.e2e/failing-postinstall @pnpm.e2e/pre-and-postinstall-scripts-example', undefined, undefined, { - envVariables: { - BIT_ALLOW_SCRIPTS: JSON.stringify({ - '@pnpm.e2e/failing-postinstall': false, - '@pnpm.e2e/pre-and-postinstall-scripts-example': true, - }), - }, - }); + helper.command.install( + '@pnpm.e2e/failing-postinstall @pnpm.e2e/pre-and-postinstall-scripts-example', + undefined, + undefined, + { + envVariables: { + BIT_ALLOW_SCRIPTS: JSON.stringify({ + '@pnpm.e2e/failing-postinstall': false, + '@pnpm.e2e/pre-and-postinstall-scripts-example': true, + }), + }, + } + ); workspaceJsonc = helper.workspaceJsonc.read(); }); after(() => { diff --git a/scopes/envs/empty-env/empty-env.aspect.ts b/scopes/envs/empty-env/empty-env.aspect.ts new file mode 100644 index 000000000000..20b4ddfd8ca0 --- /dev/null +++ b/scopes/envs/empty-env/empty-env.aspect.ts @@ -0,0 +1,5 @@ +import { Aspect } from '@teambit/harmony'; + +export const EmptyEnvAspect = Aspect.create({ + id: 'teambit.envs/empty-env', +}); diff --git a/scopes/envs/empty-env/empty-env.env.ts b/scopes/envs/empty-env/empty-env.env.ts new file mode 100644 index 000000000000..6b79c041967a --- /dev/null +++ b/scopes/envs/empty-env/empty-env.env.ts @@ -0,0 +1,17 @@ +/** + * EmptyEnv - A minimal empty environment used as the default fallback. + * This env has no compiler, tester, linter, or any other tools configured. + * It's used as the default env when no other env is specified. + */ +export class EmptyEnv { + /** + * mandatory! otherwise, it is not recognized as an env. (see getEnvDescriptorFromEnvDef) + */ + async __getDescriptor() { + return { + type: 'empty', + }; + } +} + +export default new EmptyEnv(); diff --git a/scopes/envs/empty-env/empty-env.main.runtime.ts b/scopes/envs/empty-env/empty-env.main.runtime.ts new file mode 100644 index 000000000000..d9f1a9c47369 --- /dev/null +++ b/scopes/envs/empty-env/empty-env.main.runtime.ts @@ -0,0 +1,17 @@ +import type { EnvsMain } from '@teambit/envs'; +import { EnvsAspect } from '@teambit/envs'; +import { MainRuntime } from '@teambit/cli'; +import { EmptyEnv } from './empty-env.env'; +import { EmptyEnvAspect } from './empty-env.aspect'; + +export class EmptyEnvMain { + static runtime = MainRuntime; + static dependencies = [EnvsAspect]; + static async provider([envs]: [EnvsMain]) { + const emptyEnv = new EmptyEnv(); + envs.registerEnv(emptyEnv); + return new EmptyEnvMain(); + } +} + +EmptyEnvAspect.addRuntime(EmptyEnvMain); diff --git a/scopes/envs/empty-env/index.ts b/scopes/envs/empty-env/index.ts new file mode 100644 index 000000000000..f53dfeded785 --- /dev/null +++ b/scopes/envs/empty-env/index.ts @@ -0,0 +1,3 @@ +export type { EmptyEnv } from './empty-env.env'; +export type { EmptyEnvMain } from './empty-env.main.runtime'; +export { EmptyEnvAspect, EmptyEnvAspect as default } from './empty-env.aspect'; diff --git a/scopes/envs/envs/environments.main.runtime.ts b/scopes/envs/envs/environments.main.runtime.ts index 86f6bf6f3788..346110835150 100644 --- a/scopes/envs/envs/environments.main.runtime.ts +++ b/scopes/envs/envs/environments.main.runtime.ts @@ -103,7 +103,7 @@ export type EnvCompDescriptor = EnvCompDescriptorProps & { export type Descriptor = RegularCompDescriptor | EnvCompDescriptor; -export const DEFAULT_ENV = 'teambit.harmony/node'; +export const DEFAULT_ENV = 'teambit.envs/empty-env'; export class EnvsMain { /** @@ -229,20 +229,31 @@ export class EnvsMain { return new EnvDefinition(DEFAULT_ENV, defaultEnv); } - getCoreEnvsIds(): string[] { + /** + * Returns IDs of legacy core envs that were removed from core. + * These envs were previously bundled with Bit but are now regular dependencies. + * Used for backward compatibility - old components reference these without versions. + */ + private getLegacyCoreEnvsIds(): string[] { return [ 'teambit.harmony/aspect', - 'teambit.react/react', - 'teambit.harmony/node', - 'teambit.react/react-native', 'teambit.html/html', 'teambit.mdx/mdx', 'teambit.envs/env', 'teambit.mdx/readme', 'teambit.harmony/bit-custom-aspect', + 'teambit.harmony/node', + 'teambit.react/react', + 'teambit.react/react-native', ]; } + getCoreEnvsIds(): string[] { + // All core envs have been removed from core and are now regular dependencies. + // Return only legacy core envs for backward compatibility with old components. + return ['teambit.envs/empty-env', ...this.getLegacyCoreEnvsIds()]; + } + /** * compose a new environment from a list of environment transformers. */ @@ -588,11 +599,32 @@ export class EnvsMain { }; } + /** + * Resolves an env ID to a ComponentID with version. + * For legacy core envs (removed from core), assigns the latest loaded version. + */ resolveEnv(component: Component, id: string) { const matchedEntry = component.state.aspects.entries.find((aspectEntry) => { return id === aspectEntry.id.toString() || id === aspectEntry.id.toString({ ignoreVersion: true }); }); + if (matchedEntry?.id) return matchedEntry.id; + + // Handle legacy core envs that were removed from core + // Old components have these envs stored without version + const withoutVersion = id.split('@')[0]; + if (this.getLegacyCoreEnvsIds().includes(withoutVersion)) { + // Try to find this env in the component's aspects (with version) + const legacyEnvWithVersion = component.state.aspects.entries.find((aspectEntry) => { + return aspectEntry.id.toStringWithoutVersion() === withoutVersion; + }); + if (legacyEnvWithVersion) return legacyEnvWithVersion.id; + + // Fallback: check if env is registered in slot (loaded from workspace/scope) + const fromSlot = this.envSlot.toArray().find(([envId]) => envId.startsWith(`${withoutVersion}@`)); + if (fromSlot) return ComponentID.fromString(fromSlot[0]); + } + return matchedEntry?.id; } @@ -609,8 +641,12 @@ export class EnvsMain { ? ComponentID.fromString(envIdFromEnvsConfig).toStringWithoutVersion() : undefined; + // Handle core envs (including legacy core envs that were removed from core) + // Legacy envs without version will get resolved via resolveEnv() later if (envIdFromEnvsConfig && this.isCoreEnv(envIdFromEnvsConfig)) { - return ComponentID.fromString(envIdFromEnvsConfig); + // Try to resolve version for legacy core envs + const resolved = this.resolveEnv(component, envIdFromEnvsConfig); + return resolved ? ComponentID.fromString(resolved.toString()) : ComponentID.fromString(envIdFromEnvsConfig); } // in some cases we have the id configured in the teambit.envs/envs but without the version diff --git a/scopes/harmony/bit/manifests.ts b/scopes/harmony/bit/manifests.ts index ed28cffbd0f2..d6568898dc2e 100644 --- a/scopes/harmony/bit/manifests.ts +++ b/scopes/harmony/bit/manifests.ts @@ -1,4 +1,4 @@ -import { AspectAspect } from '@teambit/aspect'; +// import { AspectAspect } from '@teambit/aspect'; // Removed from core - now a regular env import { AspectLoaderAspect } from '@teambit/aspect-loader'; import { BuilderAspect } from '@teambit/builder'; import { BundlerAspect } from '@teambit/bundler'; @@ -12,7 +12,7 @@ import { DependencyResolverAspect } from '@teambit/dependency-resolver'; import { DeprecationAspect } from '@teambit/deprecation'; import { DocsAspect } from '@teambit/docs'; import { EnvsAspect } from '@teambit/envs'; -import { EnvAspect } from '@teambit/env'; +// import { EnvAspect } from '@teambit/env'; // Removed from core - now a regular env import { ExpressAspect } from '@teambit/express'; import { YarnAspect } from '@teambit/yarn'; import { GeneratorAspect } from '@teambit/generator'; @@ -23,14 +23,14 @@ import { InsightsAspect } from '@teambit/insights'; import { IsolatorAspect } from '@teambit/isolator'; import { JestAspect } from '@teambit/jest'; import { LoggerAspect } from '@teambit/logger'; -import { NodeAspect } from '@teambit/node'; +// import { NodeAspect } from '@teambit/node'; // Removed from core - now a regular env import { NotificationsAspect } from '@teambit/notifications'; import { PanelUiAspect } from '@teambit/panels'; import { PkgAspect } from '@teambit/pkg'; import { PnpmAspect } from '@teambit/pnpm'; import { PreviewAspect } from '@teambit/preview'; import { ComponentSizerAspect } from '@teambit/component-sizer'; -import { ReactAspect } from '@teambit/react'; +// import { ReactAspect } from '@teambit/react'; // Removed from core - now a regular env import { VueAspect } from '@teambit/vue-aspect'; import { ReactRouterAspect } from '@teambit/react-router'; import { SchemaAspect } from '@teambit/schema'; @@ -61,8 +61,8 @@ import { PrettierAspect } from '@teambit/prettier'; import { WorkerAspect } from '@teambit/worker'; import { GlobalConfigAspect } from '@teambit/global-config'; import { MultiCompilerAspect } from '@teambit/multi-compiler'; -import { MDXAspect } from '@teambit/mdx'; -import { ReadmeAspect } from '@teambit/readme'; +// import { MDXAspect } from '@teambit/mdx'; // Removed from core - now a regular env +// import { ReadmeAspect } from '@teambit/readme'; // Removed from core - now a regular env import { ApplicationAspect } from '@teambit/application'; import { ExportAspect } from '@teambit/export'; import { ImporterAspect } from '@teambit/importer'; @@ -108,6 +108,7 @@ import { ConfigStoreAspect } from '@teambit/config-store'; import { CliMcpServerAspect } from '@teambit/cli-mcp-server'; import { CiAspect } from '@teambit/ci'; import { ScriptsAspect } from '@teambit/scripts'; +import { EmptyEnvAspect } from '@teambit/empty-env'; /** * this is the place to register core aspects. @@ -129,8 +130,8 @@ export const manifestsMap = { [FormatterAspect.id]: FormatterAspect, [ValidatorAspect.id]: ValidatorAspect, [ComponentAspect.id]: ComponentAspect, - [MDXAspect.id]: MDXAspect, - [ReadmeAspect.id]: ReadmeAspect, + // [MDXAspect.id]: MDXAspect, // Removed from core - now a regular env + // [ReadmeAspect.id]: ReadmeAspect, // Removed from core - now a regular env [PreviewAspect.id]: PreviewAspect, [ComponentSizerAspect.id]: ComponentSizerAspect, [DocsAspect.id]: DocsAspect, @@ -143,7 +144,7 @@ export const manifestsMap = { [UIAspect.id]: UIAspect, [GeneratorAspect.id]: GeneratorAspect, [EnvsAspect.id]: EnvsAspect, - [EnvAspect.id]: EnvAspect, + // [EnvAspect.id]: EnvAspect, // Removed from core - now a regular env [GraphAspect.id]: GraphAspect, [PubsubAspect.id]: PubsubAspect, [DependencyResolverAspect.id]: DependencyResolverAspect, @@ -151,7 +152,7 @@ export const manifestsMap = { [IsolatorAspect.id]: IsolatorAspect, [LoggerAspect.id]: LoggerAspect, [PkgAspect.id]: PkgAspect, - [ReactAspect.id]: ReactAspect, + // [ReactAspect.id]: ReactAspect, // Removed from core - now a regular env [VueAspect.id]: VueAspect, [WorkerAspect.id]: WorkerAspect, // [StencilAspect.id]: StencilAspect, @@ -162,14 +163,14 @@ export const manifestsMap = { [VariantsAspect.id]: VariantsAspect, [DeprecationAspect.id]: DeprecationAspect, [ExpressAspect.id]: ExpressAspect, - [AspectAspect.id]: AspectAspect, + // [AspectAspect.id]: AspectAspect, // Removed from core - now a regular env [WebpackAspect.id]: WebpackAspect, [SchemaAspect.id]: SchemaAspect, [ReactRouterAspect.id]: ReactRouterAspect, [TypescriptAspect.id]: TypescriptAspect, [PanelUiAspect.id]: PanelUiAspect, [BabelAspect.id]: BabelAspect, - [NodeAspect.id]: NodeAspect, + // [NodeAspect.id]: NodeAspect, // Removed from core - now a regular env [NotificationsAspect.id]: NotificationsAspect, [BundlerAspect.id]: BundlerAspect, [JestAspect.id]: JestAspect, @@ -224,6 +225,7 @@ export const manifestsMap = { [CliMcpServerAspect.id]: CliMcpServerAspect, [CiAspect.id]: CiAspect, [ScriptsAspect.id]: ScriptsAspect, + [EmptyEnvAspect.id]: EmptyEnvAspect, }; export function isCoreAspect(id: string) { diff --git a/scopes/preview/preview/preview.main.runtime.ts b/scopes/preview/preview/preview.main.runtime.ts index d9e7cfa4481b..3151eddd6e6c 100644 --- a/scopes/preview/preview/preview.main.runtime.ts +++ b/scopes/preview/preview/preview.main.runtime.ts @@ -449,11 +449,12 @@ export class PreviewMain { async calcPreviewDataFromEnv( component: Component ): Promise | undefined> { - // Prevent infinite loop that caused by the fact that the env of the aspect env or the env env is the same as the component + // Prevent infinite loop that caused by the fact that the env of the aspect env, env env, or empty-env is the same as the component // so we can't load it since during load we are trying to get env component and load it again if ( component.id.toStringWithoutVersion() === 'teambit.harmony/aspect' || - component.id.toStringWithoutVersion() === 'teambit.envs/env' + component.id.toStringWithoutVersion() === 'teambit.envs/env' || + component.id.toStringWithoutVersion() === 'teambit.envs/empty-env' ) { return { strategyName: COMPONENT_PREVIEW_STRATEGY_NAME, @@ -461,7 +462,8 @@ export class PreviewMain { }; } - const env = this.envs.getEnv(component).env; + // Use getOrCalculateEnv to handle components whose env may not be loaded yet + const env = this.envs.getOrCalculateEnv(component).env; const envPreviewConfig = this.getEnvPreviewConfig(env); const data = { strategyName: envPreviewConfig?.strategyName, diff --git a/scopes/react/aspect-docs/react/react.mdx b/scopes/react/aspect-docs/react/react.mdx index e2f3b0f76853..a49988ce8926 100644 --- a/scopes/react/aspect-docs/react/react.mdx +++ b/scopes/react/aspect-docs/react/react.mdx @@ -169,7 +169,7 @@ export class CustomReactExtension { } ``` -{/* ## Composition Providers +{/\* ## Composition Providers The React environment is able to "wrap" component compositions with an array of providers, each of which is simply a component which wraps its `children` with functionality, such as a context, styling, theme, etc. @@ -186,47 +186,43 @@ As such, they run in the environment's Preview runtime and not the Main runtime. For example, a provider that centers compositions in their rendering page, will look like this: ```tsx title="A composition provider example" -import React, { ReactReact, ReactElement } from 'react' +import React, { ReactReact, ReactElement } from 'react'; const style = { display: 'flex', justifyContent: 'center', alignItems: 'center', - height: '100vh' -} + height: '100vh', +}; -export const Center = ({ - children -}: { - children: ReactReact -}): ReactElement => { - return
{children}
-} +export const Center = ({ children }: { children: ReactReact }): ReactElement => { + return
{children}
; +}; ``` This `Center` provider component will be registered using the registerProvider method in the React extension's `*.preview.runtime.tsx` file: ```tsx title="react-with-providers.preview.runtime.tsx" -import { PreviewRuntime } from '@teambit/preview' -import { ReactAspect, ReactPreview } from '@teambit/react' -import { ReactWithProvidersAspect } from './react-with-providers.aspect' -import { Center } from './composition-providers/center' +import { PreviewRuntime } from '@teambit/preview'; +import { ReactAspect, ReactPreview } from '@teambit/react'; +import { ReactWithProvidersAspect } from './react-with-providers.aspect'; +import { Center } from './composition-providers/center'; export class ReactWithProvidersPreview { - static runtime = PreviewRuntime - static dependencies = [ReactAspect] + static runtime = PreviewRuntime; + static dependencies = [ReactAspect]; static async provider([react]: [ReactPreview]) { - react.registerProvider([Center]) + react.registerProvider([Center]); - return ReactWithProvidersPreview + return ReactWithProvidersPreview; } } -ReactWithProvidersAspect.addRuntime(ReactWithProvidersPreview) +ReactWithProvidersAspect.addRuntime(ReactWithProvidersPreview); ``` -> See the full demo project [here](https://github.com/teambit/react-env-with-providers). */} +> See the full demo project [here](https://github.com/teambit/react-env-with-providers). \*/} ### Transformers API docs diff --git a/scopes/react/ui/loader-fallback/loader-fallback.docs.mdx b/scopes/react/ui/loader-fallback/loader-fallback.docs.mdx index abc84697c65a..0d9ec7e8b549 100644 --- a/scopes/react/ui/loader-fallback/loader-fallback.docs.mdx +++ b/scopes/react/ui/loader-fallback/loader-fallback.docs.mdx @@ -22,9 +22,9 @@ const safeTarget = useFallback(Target && , , { tim 1. then, after _x_ seconds - show the default. {/* live playground doesn't keep state when editing :( */} -{/* Try it out: +{/\* Try it out: -```tsx live +````tsx live function Example() { return ( { + private *_parseCommaSeparatedPkgList(pkgList: string): IterableIterator { for (const pkgName of pkgList.split(',')) { const trimmed = pkgName.trim(); if (trimmed) { diff --git a/scopes/workspace/workspace/workspace-aspects-loader.ts b/scopes/workspace/workspace/workspace-aspects-loader.ts index 9c60439d18a2..064df5e0533a 100644 --- a/scopes/workspace/workspace/workspace-aspects-loader.ts +++ b/scopes/workspace/workspace/workspace-aspects-loader.ts @@ -158,7 +158,8 @@ needed-for: ${neededFor || ''}. using opts: ${JSON.stringify(mergedOpts const potentialPluginsIndexes = compact( manifests.map((manifest, index) => { - if (this.aspectLoader.isValidAspect(manifest)) return undefined; + const isValid = this.aspectLoader.isValidAspect(manifest); + if (isValid) return undefined; return index; }) ); diff --git a/workspace.jsonc b/workspace.jsonc index 7eff41df72cb..e47b8d7574e5 100644 --- a/workspace.jsonc +++ b/workspace.jsonc @@ -5,10 +5,17 @@ "icon": "https://static.bit.dev/bit-logo.svg", "defaultScope": "teambit.bit", "defaultDirectory": "components", - // Setting this to false since the external core aspect env uses the core aspects - // so we need it to be installed in a capsule - "resolveEnvsFromRoots": false + // Setting this to true to load envs from node_modules instead of capsules + // This allows envs with only .bit-env.ts files (like core-aspect-env) to load via plugin mechanism + "resolveEnvsFromRoots": true }, + // These envs are loaded from node_modules (no longer bundled as core aspects) + "teambit.harmony/envs/core-aspect-env": {}, + "teambit.envs/env": {}, + "teambit.harmony/node": {}, + "teambit.react/react": {}, + "teambit.mdx/mdx": {}, + "teambit.harmony/aspect": {}, "teambit.dependencies/dependency-resolver": { "allowScripts": { "@apollo/protobufjs": true,