Skip to content

Commit

Permalink
feat: allow to set a custom bit roots directory (#9053)
Browse files Browse the repository at this point in the history
  • Loading branch information
zkochan authored Jul 23, 2024
1 parent 9875f26 commit e164e44
Show file tree
Hide file tree
Showing 14 changed files with 423 additions and 163 deletions.
21 changes: 21 additions & 0 deletions e2e/harmony/root-components.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getRootComponentDir } from '@teambit/workspace.root-components';
import { resolveFrom } from '@teambit/toolbox.modules.module-resolver';
import chai, { expect } from 'chai';
import fs from 'fs-extra';
Expand Down Expand Up @@ -1679,3 +1680,23 @@ describe('create with root components on', function () {
expect(path.join(helper.env.rootCompDirDep('teambit.react/react', 'my-button'), 'index.ts')).to.be.a.path();
});
});

describe('custom root components directory', function () {
let helper: Helper;
this.timeout(0);
describe('set a valid custom location', () => {
before(() => {
helper = new Helper();
helper.scopeHelper.setNewLocalAndRemoteScopes();
helper.extensions.workspaceJsonc.addKeyValToWorkspace('rootComponentsDirectory', '');
helper.extensions.workspaceJsonc.addKeyValToDependencyResolver('rootComponents', true);
helper.command.create('react', 'card', '--env teambit.react/react');
helper.command.install();
});
it('should create the root component directory at the specified location', () => {
expect(
getRootComponentDir(path.join(helper.scopes.localPath, '.bit_roots'), 'teambit.react/react')
).to.be.a.path();
});
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,12 @@
"@teambit/harmony.modules.feature-toggle": "~0.0.4",
"@teambit/legacy.scope-api": "~0.0.1",
"@teambit/bit-error": "~0.0.404",
"@teambit/bit-roots": "~0.0.133",
"@teambit/lane-id": "~0.0.311",
"@teambit/component-version": "^1.0.3",
"@teambit/toolbox.network.agent": "~0.0.554",
"@teambit/legacy-component-log": "~0.0.402",
"@teambit/graph.cleargraph": "0.0.11",
"@teambit/workspace.root-components": "1.0.0",
"@babel/core": "7.19.6",
"@babel/runtime": "7.23.2",
"@teambit/legacy-bit-id": "^1.1.1",
Expand Down
457 changes: 326 additions & 131 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions scopes/compilation/compiler/workspace-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { DependencyResolverMain } from '@teambit/dependency-resolver';
import { PathOsBasedAbsolute, PathOsBasedRelative } from '@teambit/toolbox.path.path';
import { componentIdToPackageName } from '@teambit/pkg.modules.component-package-name';
import { UiMain } from '@teambit/ui';
import { readBitRootsDir } from '@teambit/bit-roots';
import { readRootComponentsDir } from '@teambit/workspace.root-components';
import { groupBy, uniq } from 'lodash';
import type { PreStartOpts } from '@teambit/ui';
import { MultiCompiler } from '@teambit/multi-compiler';
Expand Down Expand Up @@ -148,7 +148,7 @@ ${this.compileErrors.map(formatError).join('\n')}`);
const injectedDirs = await this.workspace.getInjectedDirs(this.component);
if (injectedDirs.length > 0) return injectedDirs;

const rootDirs = await readBitRootsDir(this.workspace.path);
const rootDirs = await readRootComponentsDir(this.workspace.rootComponentsPath);
return rootDirs.map((rootDir) => path.relative(this.workspace.path, path.join(rootDir, packageName)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import multimatch from 'multimatch';
import mapSeries from 'p-map-series';
import { MainRuntime } from '@teambit/cli';
import { getAllCoreAspectsIds } from '@teambit/bit';
import { getRelativeRootComponentDir } from '@teambit/bit-roots';
import { getRootComponentDir } from '@teambit/workspace.root-components';
import { ComponentAspect, Component, ComponentMap, ComponentMain, IComponent } from '@teambit/component';
import type { ConfigMain } from '@teambit/config';
import { join } from 'path';
import { join, relative } from 'path';
import { compact, get, pick, uniq, omit, cloneDeep } from 'lodash';
import { ConfigAspect } from '@teambit/config';
import { EnvsAspect } from '@teambit/envs';
Expand Down Expand Up @@ -506,14 +506,23 @@ export class DependencyResolverMain {
* Returns the location where the component is installed with its peer dependencies
* This is used in cases you want to actually run the components and make sure all the dependencies (especially peers) are resolved correctly
*/
getRuntimeModulePath(component: Component, isInWorkspace = false) {
getRuntimeModulePath(
component: Component,
options: {
workspacePath: string;
rootComponentsPath: string;
isInWorkspace?: boolean;
}
) {
if (!this.hasRootComponents()) {
const modulePath = this.getModulePath(component);
return modulePath;
}
const pkgName = this.getPackageName(component);
const rootComponentsRelativePath = relative(options.workspacePath, options.rootComponentsPath);
const getRelativeRootComponentDir = getRootComponentDir.bind(null, rootComponentsRelativePath ?? '');
const selfRootDir = getRelativeRootComponentDir(
!isInWorkspace ? component.id.toString() : component.id.toStringWithoutVersion()
options.isInWorkspace ? component.id.toStringWithoutVersion() : component.id.toString()
);
// In case the component is it's own root we want to load it from it's own root folder
if (fs.pathExistsSync(selfRootDir)) {
Expand Down
11 changes: 7 additions & 4 deletions scopes/dependencies/pnpm/lynx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
PackageManagerNetworkConfig,
} from '@teambit/dependency-resolver';
import { BitError } from '@teambit/bit-error';
import { BIT_ROOTS_DIR } from '@teambit/legacy/dist/constants';
import {
MutatedProject,
mutateModules,
Expand All @@ -30,7 +31,7 @@ import { restartWorkerPool, finishWorkers } from '@pnpm/worker';
import { createPkgGraph } from '@pnpm/workspace.pkgs-graph';
import { PackageManifest, ProjectManifest, ReadPackageHook } from '@pnpm/types';
import { Logger } from '@teambit/logger';
import { VIRTUAL_STORE_DIR_MAX_LENGTH } from '@teambit/dependencies.pnpm.dep-path'
import { VIRTUAL_STORE_DIR_MAX_LENGTH } from '@teambit/dependencies.pnpm.dep-path';
import toNerfDart from 'nerf-dart';
import { pnpmErrorToBitError } from './pnpm-error-to-bit-error';
import { readConfig } from './read-config';
Expand Down Expand Up @@ -410,7 +411,7 @@ function readPackageHook(pkg: PackageManifest, workspaceDir?: string): PackageMa
return pkg;
}
// workspaceDir is set only for workspace packages
if (workspaceDir && !workspaceDir.includes('.bit_roots')) {
if (workspaceDir && !workspaceDir.includes(BIT_ROOTS_DIR)) {
return readWorkspacePackageHook(pkg);
}
return readDependencyPackageHook(pkg);
Expand Down Expand Up @@ -458,8 +459,10 @@ function readWorkspacePackageHook(pkg: PackageManifest): PackageManifest {
}

function groupPkgs(manifestsByPaths: Record<string, ProjectManifest>, opts: { update?: boolean }) {
const pkgs = Object.entries(manifestsByPaths)
.map(([rootDir, manifest]) => ({ rootDir: rootDir as ProjectRootDir, manifest }));
const pkgs = Object.entries(manifestsByPaths).map(([rootDir, manifest]) => ({
rootDir: rootDir as ProjectRootDir,
manifest,
}));
const { graph } = createPkgGraph(pkgs);
const chunks = sortPackages(graph as any);

Expand Down
3 changes: 2 additions & 1 deletion scopes/dependencies/pnpm/pnpm.package-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
import { renderTree } from '@pnpm/list';
import { readWantedLockfile } from '@pnpm/lockfile-file';
import { ProjectManifest } from '@pnpm/types';
import { BIT_ROOTS_DIR } from '@teambit/legacy/dist/constants';
import { join } from 'path';
import { readConfig } from './read-config';
import { pnpmPruneModules } from './pnpm-prune-modules';
Expand Down Expand Up @@ -305,7 +306,7 @@ export class PnpmPackageManager implements PackageManager {
const search = createPackagesSearcher([depName]);
const lockfile = await readWantedLockfile(opts.lockfileDir, { ignoreIncompatible: false });
const projectPaths = Object.keys(lockfile?.importers ?? {})
.filter((id) => !id.startsWith('node_modules/.bit_roots'))
.filter((id) => !id.includes(`${BIT_ROOTS_DIR}/`))
.map((id) => join(opts.lockfileDir, id));
const cache = new Map();
const modulesManifest = await this._readModulesManifest(opts.lockfileDir);
Expand Down
5 changes: 4 additions & 1 deletion scopes/pkg/pkg/pkg.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,10 @@ export class PkgMain {
* This is used in cases you want to actually run the components and make sure all the dependencies (especially peers) are resolved correctly
*/
getRuntimeModulePath(component: Component, options: GetModulePathOptions = {}) {
const relativePath = this.dependencyResolver.getRuntimeModulePath(component);
const relativePath = this.dependencyResolver.getRuntimeModulePath(component, {
workspacePath: this.workspace.path,
rootComponentsPath: this.workspace.rootComponentsPath,
});
if (options?.absPath) {
if (this.workspace) {
return join(this.workspace.path, relativePath);
Expand Down
26 changes: 14 additions & 12 deletions scopes/workspace/install/install.main.runtime.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs, { pathExists } from 'fs-extra';
import path from 'path';
import { getRootComponentDir, getBitRootsDir, linkPkgsToBitRoots } from '@teambit/bit-roots';
import { getRootComponentDir, linkPkgsToRootComponents } from '@teambit/workspace.root-components';
import { CompilerMain, CompilerAspect, CompilationInitiator } from '@teambit/compiler';
import { CLIMain, CommandList, CLIAspect, MainRuntime } from '@teambit/cli';
import chalk from 'chalk';
Expand Down Expand Up @@ -614,11 +614,10 @@ export class InstallMain {

private async _updateRootDirs(rootDirs: string[]) {
try {
const bitRootCompsDir = getBitRootsDir(this.workspace.path);
const existingDirs = await fs.readdir(bitRootCompsDir);
const existingDirs = await fs.readdir(this.workspace.rootComponentsPath);
await Promise.all(
existingDirs.map(async (dirName) => {
const dirPath = path.join(bitRootCompsDir, dirName);
const dirPath = path.join(this.workspace.rootComponentsPath, dirName);
if (!rootDirs.includes(dirPath)) {
await fs.remove(dirPath);
}
Expand Down Expand Up @@ -656,7 +655,7 @@ export class InstallMain {
await Promise.all(
envs.map(async (envId) => {
return [
await this.getRootComponentDirByRootId(this.workspace.path, envId),
await this.getRootComponentDirByRootId(this.workspace.rootComponentsPath, envId),
{
dependencies: {
...(await this._getEnvDependencies(envId)),
Expand Down Expand Up @@ -718,7 +717,7 @@ export class InstallMain {
if (!appManifest) return null;
const envId = await this.envs.calculateEnvId(app);
return [
await this.getRootComponentDirByRootId(this.workspace.path, app.id),
await this.getRootComponentDirByRootId(this.workspace.rootComponentsPath, app.id),
{
...omit(appManifest, ['name', 'version']),
dependencies: {
Expand Down Expand Up @@ -927,23 +926,26 @@ export class InstallMain {
const apps = (await this.app.listAppsComponents()).map((component) => component.id);
await Promise.all(
[...envs, ...apps].map(async (id) => {
const dir = await this.getRootComponentDirByRootId(this.workspace.path, id);
const dir = await this.getRootComponentDirByRootId(this.workspace.rootComponentsPath, id);
await fs.mkdirp(dir);
})
);
await linkPkgsToBitRoots(
this.workspace.path,
await linkPkgsToRootComponents(
{
rootComponentsPath: this.workspace.rootComponentsPath,
workspacePath: this.workspace.path,
},
compDirMap.components.map((component) => this.dependencyResolver.getPackageName(component))
);
}

private async getRootComponentDirByRootId(workspacePath: string, rootComponentId: ComponentID): Promise<string> {
private async getRootComponentDirByRootId(rootComponentsPath: string, rootComponentId: ComponentID): Promise<string> {
// Root directories for local envs and apps are created without their version number.
// This is done in order to avoid changes to the lockfile after such components are tagged.
const id = (await this.workspace.hasId(rootComponentId))
const id = this.workspace.hasId(rootComponentId)
? rootComponentId.toStringWithoutVersion()
: rootComponentId.toString();
return getRootComponentDir(workspacePath, id);
return getRootComponentDir(rootComponentsPath, id);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs-extra';
import pMapSeries from 'p-map-series';
import * as path from 'path';
import { linkPkgsToBitRoots } from '@teambit/bit-roots';
import { linkPkgsToRootComponents } from '@teambit/workspace.root-components';
import { ComponentID } from '@teambit/component-id';
import { IS_WINDOWS, PACKAGE_JSON, SOURCE_DIR_SYMLINK_TO_NM } from '@teambit/legacy/dist/constants';
import { BitMap } from '@teambit/legacy.bit-map';
Expand Down Expand Up @@ -69,8 +69,11 @@ export default class NodeModuleLinker {
this.workspace.clearAllComponentsCache();
}

await linkPkgsToBitRoots(
workspacePath,
await linkPkgsToRootComponents(
{
rootComponentsPath: this.workspace.rootComponentsPath,
workspacePath,
},
this.components.map((comp) => componentIdToPackageName(comp.state._consumer))
);
return linksResults;
Expand Down
6 changes: 6 additions & 0 deletions scopes/workspace/workspace/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export interface WorkspaceExtConfig {
*/
defaultDirectory: string;

/**
* sets the location of the root components directory.
* The location is a relative path to the workspace root and should use linux path separators (/).
*/
rootComponentsDirectory?: string;

/**
* set the default structure of components in your project
*/
Expand Down
19 changes: 17 additions & 2 deletions scopes/workspace/workspace/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { CompIdGraph, DepEdgeType } from '@teambit/graph';
import { slice, isEmpty, merge, compact, uniqBy } from 'lodash';
import {
MergeConfigFilename,
BIT_ROOTS_DIR,
CFG_DEFAULT_RESOLVE_ENVS_FROM_ROOTS,
CFG_USER_TOKEN_KEY,
} from '@teambit/legacy/dist/constants';
Expand Down Expand Up @@ -286,6 +287,17 @@ export class Workspace implements ComponentFactory {
return this.consumer.getPath();
}

/**
* Get the location of the bit roots folder
*/
get rootComponentsPath() {
const baseDir =
this.config.rootComponentsDirectory != null
? path.join(this.path, this.config.rootComponentsDirectory)
: this.modulesPath;
return path.join(baseDir, BIT_ROOTS_DIR);
}

/** get the `node_modules` folder of this workspace */
private get modulesPath() {
return path.join(this.path, 'node_modules');
Expand Down Expand Up @@ -1707,8 +1719,11 @@ the following envs are used in this workspace: ${availableEnvs.join(', ')}`);
}

async getComponentPackagePath(component: Component) {
const inInWs = await this.hasId(component.id);
const relativePath = this.dependencyResolver.getRuntimeModulePath(component, inInWs);
const relativePath = this.dependencyResolver.getRuntimeModulePath(component, {
workspacePath: this.path,
rootComponentsPath: this.rootComponentsPath,
isInWorkspace: this.hasId(component.id),
});
return path.join(this.path, relativePath);
}

Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ export const BIT_MAP = '.bitmap';

export const OLD_BIT_MAP = '.bit.map.json';

export const BIT_ROOTS_DIR = '.bit_roots';

export const TESTS_FORK_LEVEL = {
NONE: 'NONE',
ONE: 'ONE',
Expand Down
4 changes: 2 additions & 2 deletions src/e2e-helper/e2e-env-helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from 'path';
import { getRootComponentDir } from '@teambit/bit-roots';
import { getRootComponentDir } from '@teambit/workspace.root-components';
import CommandHelper from './e2e-command-helper';
import ExtensionsHelper from './e2e-extensions-helper';
import FixtureHelper, { GenerateEnvJsoncOptions } from './e2e-fixtures-helper';
Expand Down Expand Up @@ -47,7 +47,7 @@ export default class EnvHelper {
}

rootCompDir(envName: string) {
return getRootComponentDir(this.scopes.localPath, envName);
return getRootComponentDir(path.join(this.scopes.localPath, 'node_modules/.bit_roots'), envName);
}

getTypeScriptSettingsForES5() {
Expand Down

0 comments on commit e164e44

Please sign in to comment.