Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
db94f29
feat(bit-map): allow a component to own the workspace root (rootDir ".")
davidfirst Sep 10, 2026
f0ca113
feat(bit-map): track .bitmap in the workspace-root component
davidfirst Sep 10, 2026
d166385
feat(issues): skip env/compiler-derived issues for the workspace-root…
davidfirst Sep 10, 2026
016b3c4
feat(workspace-root): use the empty env, and keep it out of install a…
davidfirst Sep 10, 2026
b2c0a9d
fix(workspace-root): address review findings on the root component
davidfirst Sep 10, 2026
99c1fd8
fix(component-writer): allow importing the workspace-root component o…
davidfirst Sep 10, 2026
b49b410
chore: merge master into feat/workspace-root-component-nesting
davidfirst Sep 11, 2026
d2b6186
feat(workspace): add trackAllFiles to track package.json and other bi…
davidfirst Sep 11, 2026
442238c
refactor(workspace-root): address review and simplify the root-compon…
davidfirst Sep 11, 2026
189c57f
fix(bit-map): disable globby directory expansion in the scan, it brok…
davidfirst Sep 11, 2026
a6943c1
fix(workspace-root): keep the root out of node_modules, and tighten t…
davidfirst Sep 11, 2026
30c946d
fix(workspace-root): track the root with the empty env as explicit co…
davidfirst Sep 11, 2026
e9b233f
fix(tracker): re-add the workspace root without its name, escape nest…
davidfirst Sep 11, 2026
9984ba1
Merge branch 'master' into feat/workspace-root-component-nesting
davidfirst Sep 11, 2026
1ca46ad
fix(tracker): keep the workspace-root main file safe on bulk tracking…
davidfirst Sep 11, 2026
3bf4c68
fix(workspace-root): require --override for user files on a root impo…
davidfirst Sep 11, 2026
b08e4ef
fix(workspace-root): keep .bitmap in the add-time file-set, skip the …
davidfirst Sep 11, 2026
74c694d
fix(workspace-root): treat symlinks and dirs as import conflicts, app…
davidfirst Sep 11, 2026
6e31b80
fix(workspace-root): honor nested ignore files and skip nested maps i…
davidfirst Sep 14, 2026
6b25177
fix(workspace-root): keep ignore options on an empty map, directory-o…
davidfirst Sep 14, 2026
2320d97
fix(workspace-root): evaluate nested ignore files with the root ones …
davidfirst Sep 14, 2026
e3aace0
fix(workspace-root): keep bit-owned exclusions non-negatable, never s…
davidfirst Sep 14, 2026
8543f90
fix(workspace-root): scan the root on bulk tracking, refuse ordinary …
davidfirst Sep 14, 2026
d002b9a
fix(tracker): scan dotfiles at add time for every component, as the r…
davidfirst Sep 14, 2026
3054c16
fix(bit-map): drop lane state from the versioned map, honor nested ig…
davidfirst Sep 14, 2026
0cbbcdb
fix(tracker): keep the tracked id on bulk re-track, apply the own ign…
davidfirst Sep 14, 2026
8ab2f69
fix(component-writer): accept "--path ." only for a component its ver…
davidfirst Sep 14, 2026
872d041
fix(tracker): guard the workspace-root main file on bulk tracking, ke…
davidfirst Sep 14, 2026
ad5d1a2
fix(workspace-root): refuse symlinked ancestors on a root import, wat…
davidfirst Sep 14, 2026
8a4a106
fix(bit-map): do not follow symlinks in the root scan, resolve the ma…
davidfirst Sep 14, 2026
472549e
fix(tracker): apply the component's own ignore file at add time, as t…
davidfirst Sep 14, 2026
c62e5ee
fix(tracker): guard the workspace-root main file by directory, not by…
davidfirst Sep 14, 2026
176b92c
fix(component-writer): refuse a symlinked destination on a root impor…
davidfirst Sep 14, 2026
9bbf81f
feat(tracker): default the workspace-root main file to workspace.jsonc
davidfirst Sep 14, 2026
71d06d9
test(e2e): assert a second root import is refused on local changes, n…
davidfirst Sep 14, 2026
3eeed3c
fix(pkg): do not report missing node_modules links for the workspace-…
davidfirst Sep 14, 2026
f19a107
Merge remote-tracking branch 'origin/master' into feat/workspace-root…
davidfirst Sep 14, 2026
0d90dd0
fix(bit-map): read a component's own ignore file from disk, apply the…
davidfirst Sep 14, 2026
481443d
fix(bit-map): prefer the scope-qualified map key when checking the wo…
davidfirst Sep 14, 2026
c0cb2f7
fix(install): run the rewire codemod on the workspace-root component …
davidfirst Sep 15, 2026
401b4e4
revert: fix(install): run the rewire codemod on the workspace-root co…
davidfirst Sep 15, 2026
1208b92
feat(dependencies): skip dependency detection for the workspace-root …
davidfirst Sep 15, 2026
a6fd3a9
feat(workspace-root): new aspect, members record the root component t…
davidfirst Sep 15, 2026
e3fd2df
feat(workspace-root): snap a new or modified root along with its memb…
davidfirst Sep 16, 2026
f5f7916
fix(workspace-root): symlink guard for tracked roots, keep same-named…
davidfirst Sep 16, 2026
1d6d001
feat(workspace-root): bit clone, a workspace from its workspace-root …
davidfirst Sep 16, 2026
57708e5
fix(workspace-root): validate cloned dirs, drop the lanes dependency,…
davidfirst Sep 16, 2026
216f3ee
fix(workspace-root): name the scope when the cloned root is not on th…
davidfirst Sep 16, 2026
bcfb8b3
perf(importer): write a set of components to their own directories in…
davidfirst Sep 16, 2026
47c9c0b
fix(bit-map): version a component's scope in one field, so the first …
davidfirst Sep 16, 2026
fbbf9b4
fix(workspace-root): refuse a symlinked clone target, keep dotted dir…
davidfirst Sep 16, 2026
835765e
Merge remote-tracking branch 'origin/master' into feat/workspace-root…
davidfirst Sep 16, 2026
f51ca92
chore(deps): refresh the lockfile after the merge
davidfirst Sep 16, 2026
16122c6
fix(component-writer): refuse to move a component out of the workspac…
davidfirst Sep 16, 2026
65c74ee
fix(snapping): record the workspace root only on components the works…
davidfirst Sep 16, 2026
be52daf
fix(workspace-root): restore the caller's working directory after a s…
davidfirst Sep 16, 2026
dfda122
fix(watcher): ignore every file bit generates, not only package.json,…
davidfirst Sep 16, 2026
3578fa4
test(watcher): cover the ignore patterns a tracked workspace root dep…
davidfirst Sep 16, 2026
b46e253
fix(bit-map): apply nested ignore files deepest-last, and skip the sy…
davidfirst Sep 17, 2026
6735043
Merge branch 'master' into feat/workspace-root-component-nesting
davidfirst Sep 17, 2026
d0a9800
fix(snapping): keep a versions file from dictating the auto-added roo…
davidfirst Sep 17, 2026
424d8e8
test(tracker): cover the workspace-root add cases in a spec instead o…
davidfirst Sep 17, 2026
58c550a
fix(workspace-root): keep a clone and a root snapshot within their ow…
davidfirst Sep 17, 2026
a8013dc
fix(bit-map): compare root-dirs as directories, not as strings
davidfirst Sep 17, 2026
5366037
fix(bit-map): drop a deleted component from the versioned .bitmap
davidfirst Sep 17, 2026
47709cf
fix(workspace-root): render the components a clone could not fetch as…
davidfirst Sep 17, 2026
ef5271d
test(tracker): cover add-time ignore filtering in a spec instead of e2e
davidfirst Sep 17, 2026
38fcc79
fix(tracker): report a main file the component's ignore file excludes…
davidfirst Sep 17, 2026
3f5bb49
fix(component-writer): keep a root version from overwriting the sourc…
davidfirst Sep 17, 2026
3ddfdb6
fix(bit-map): let the root scan the dir of a removed or other-lane co…
davidfirst Sep 17, 2026
e38fc00
fix(component-writer): write an ordinary component's own .bitmap, onl…
davidfirst Sep 17, 2026
ff8fe8e
fix(tracker): keep the files bit generated when trackAllFiles is on, …
davidfirst Sep 17, 2026
6c3a6e6
test(e2e): drop an add case the getFilesByDir unit tests already cove…
davidfirst Sep 17, 2026
e18b450
fix(component-writer): keep skipping .bitmap for every component, no …
davidfirst Sep 17, 2026
7914f2f
fix(component-writer): apply the nested-component filter to the root …
davidfirst Sep 17, 2026
1a68acd
test(tracker): cover the root-and-nested batch add in a spec instead …
davidfirst Sep 17, 2026
4e0642c
fix(bit-map): drop a trailing slash from a nested root-dir, callers a…
davidfirst Sep 17, 2026
ec7bf2a
test(component-writer): pin that a tracked root component is written …
davidfirst Sep 17, 2026
e88a8a6
fix(component-writer): skip the live .bitmap in the root import prefl…
davidfirst Sep 17, 2026
482c1ec
fix(workspace-root): prefix the clone missing-components title with t…
davidfirst Sep 17, 2026
b6f8611
fix(tracker): apply the generated-files rule to an explicit main file…
davidfirst Sep 17, 2026
498fbd3
fix(bit-map): one lane-aware lookup for the component that owns the w…
davidfirst Sep 17, 2026
579f999
docs(watcher): say which generated configs the ignore patterns do not…
davidfirst Sep 17, 2026
44c56fa
fix(install): clear the install context in a finally, a caught failur…
davidfirst Sep 17, 2026
f51d709
fix(component-writer): check the generated config file for a symlink …
davidfirst Sep 17, 2026
eaffb84
test(e2e): drop a root-manifest assertion the clone flow already proves
davidfirst Sep 17, 2026
f34b8c1
Merge remote-tracking branch 'origin/master' into feat/workspace-root…
davidfirst Sep 17, 2026
66c26e7
test(e2e): cover cloning a lane with a version pinned on the root id
davidfirst Sep 17, 2026
de95944
fix(workspace-root): refuse an absolute rootDir that resolves inside …
davidfirst Sep 17, 2026
54d7da7
fix(workspace-root): resolve a symlinked ancestor before checking for…
davidfirst Sep 17, 2026
6705cd9
test: move the symlink preflight variants off the e2e tier
davidfirst Sep 17, 2026
363b7ab
fix(workspace-root): remove the directories a failed clone created on…
davidfirst Sep 17, 2026
ac10a7f
test(component-writer): cover that the batch write leaves the workspa…
davidfirst Sep 17, 2026
d6e24fa
Merge remote-tracking branch 'origin/master' into feat/workspace-root…
davidfirst Sep 17, 2026
f308627
fix(bit-map): keep .bitmap in the root file-set when the workspace ig…
davidfirst Sep 17, 2026
ca0cd2c
fix(bit-map): let the entry that owns a dir win the path index over a…
davidfirst Sep 17, 2026
2ae6395
fix(workspace-root): clear a root a component was snapped in when thi…
davidfirst Sep 17, 2026
4c7db9d
fix(workspace-root): reject a non-string rootDir with the clone error…
davidfirst Sep 17, 2026
783f894
fix(workspace-root): clear the former root pointer when a member beco…
davidfirst Sep 17, 2026
6ff664e
refactor(component-writer): resolve the per-component write path insi…
davidfirst Sep 18, 2026
eb1e095
test(workspace-root): cover a member imported alone into a workspace …
davidfirst Sep 18, 2026
8b59694
refactor(workspace-root): drop the clear helpers, aspect data is not …
davidfirst Sep 18, 2026
a34cb87
feat(tracker): require --root to track the workspace root with bit add
davidfirst Sep 18, 2026
4f2e1c3
docs(cli-reference): regenerate for the new bit add --root flag
davidfirst Sep 18, 2026
113ad10
fix(workspace-root): close three write and tracking holes found in re…
davidfirst Sep 18, 2026
49aefcb
fix(tracker): let the programmatic api track the workspace root witho…
davidfirst Sep 18, 2026
9e2becd
fix(snapping): keep a hidden-only merge from snapping the workspace root
davidfirst Sep 18, 2026
bb6f383
fix(workspace-root): refuse member directories a remote map should no…
davidfirst Sep 18, 2026
4ba95d3
revert(watcher): drop the never-tracked names from the watch ignore list
davidfirst Sep 18, 2026
ea42c93
Merge branch 'master' into feat/workspace-root-component-nesting
davidfirst Sep 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion components/legacy/bit-map/bit-map.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { expect } from 'chai';
import { ComponentID } from '@teambit/component-id';
import { BitId } from '@teambit/legacy-bit-id';
import { logger } from '@teambit/legacy.logger';
import { BitMap } from './bit-map';
import { BitMap, normalizeBitmapContentForVersioning } from './bit-map';
import { WORKSPACE_ROOT_DIR } from './component-map';
import { DuplicateRootDir } from './exceptions/duplicate-root-dir';

const getBitmapInstance = async () => {
Expand Down Expand Up @@ -90,4 +91,83 @@ describe('BitMap', function () {
);
});
});
describe('workspace-root component', () => {
const rootComponentParams = {
componentId: ComponentID.fromObject({ name: 'ws-root' }, 'my-scope'),
files: [{ name: 'README.md', relativePath: 'README.md', test: false }],
mainFile: 'README.md',
defaultScope: 'my-scope',
rootDir: WORKSPACE_ROOT_DIR,
};
const nestedComponentParams = {
componentId: ComponentID.fromObject({ name: 'comp1' }, 'my-scope'),
files: [{ name: 'index.js', relativePath: 'index.js', test: false }],
mainFile: 'index.js',
defaultScope: 'my-scope',
rootDir: 'packages/comp1',
};
it('should allow a rootDir of "." to contain other components, in both add orders', async () => {
const rootFirst = await getBitmapInstance();
rootFirst.addComponent(rootComponentParams);
expect(() => rootFirst.addComponent(nestedComponentParams)).to.not.throw();

const nestedFirst = await getBitmapInstance();
nestedFirst.addComponent(nestedComponentParams);
expect(() => nestedFirst.addComponent(rootComponentParams)).to.not.throw();
});
it('should keep rejecting nesting between two non-root components', async () => {
const bitMap = await getBitmapInstance();
bitMap.addComponent(nestedComponentParams);
expect(() =>
bitMap.addComponent({
...nestedComponentParams,
componentId: ComponentID.fromObject({ name: 'comp2' }, 'my-scope'),
rootDir: 'packages/comp1/nested',
})
).to.throw();
});
it('getNestedRootDirs should return the nested components for the root, and nothing for a leaf', async () => {
const bitMap = await getBitmapInstance();
bitMap.addComponent(rootComponentParams);
bitMap.addComponent(nestedComponentParams);
expect(bitMap.getNestedRootDirs(WORKSPACE_ROOT_DIR)).to.deep.equal(['packages/comp1']);
expect(bitMap.getNestedRootDirs('packages/comp1')).to.deep.equal([]);
});
});
describe('normalizeBitmapContentForVersioning', () => {
const rawBitmap = JSON.stringify(
{
comp1: {
name: 'comp1',
scope: 'my-scope',
version: '0a14284ddaadde623d5c11f5511594485a14b3c8',
defaultScope: 'my-org.demo',
mainFile: 'index.ts',
rootDir: 'comp1',
},
'$schema-version': '17.0.0',
},
null,
4
);
let normalized: string;
let parsed: Record<string, any>;
before(() => {
normalized = normalizeBitmapContentForVersioning(rawBitmap);
parsed = JSON.parse(normalized.slice(normalized.indexOf('{')));
});
it('should empty the fields that change on every snap and export', () => {
expect(parsed.comp1.version).to.equal('');
expect(parsed.comp1.scope).to.equal('');
});
it('should keep the durable map intact', () => {
expect(parsed.comp1.rootDir).to.equal('comp1');
expect(parsed.comp1.mainFile).to.equal('index.ts');
expect(parsed.comp1.defaultScope).to.equal('my-org.demo');
expect(parsed['$schema-version']).to.equal('17.0.0');
});
it('should be idempotent, otherwise the root component would never converge', () => {
expect(normalizeBitmapContentForVersioning(normalized)).to.equal(normalized);
});
});
});
55 changes: 52 additions & 3 deletions components/legacy/bit-map/bit-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type {
} from '@teambit/toolbox.path.path';
import { pathJoinLinux, pathNormalizeToLinux } from '@teambit/toolbox.path.path';
import type { ComponentMapFile, Config, PathChange } from './component-map';
import { ComponentMap, getFilesByDir, getGitIgnoreHarmony } from './component-map';
import { ComponentMap, getFilesByDir, getGitIgnoreHarmony, WORKSPACE_ROOT_DIR } from './component-map';
import { InvalidBitMap, MissingBitMapComponent } from './exceptions';
import { DuplicateRootDir } from './exceptions/duplicate-root-dir';

Expand Down Expand Up @@ -99,15 +99,20 @@ export class BitMap {

/**
* in case the added component's root-dir is a parent-dir of other components
* or other component's root-dir is a parent root-dir of this component, throw an error
* or other component's root-dir is a parent root-dir of this component, throw an error.
*
* the workspace-root component (rootDir ".") is the one exception - it is allowed to contain
* other components. its file-set subtracts their root-dirs, so the two never claim the same file.
*/
private throwForExistingParentDir({ id, rootDir }: ComponentMap) {
if (rootDir === WORKSPACE_ROOT_DIR) return;
const isParentDir = (parent: string, child: string) => {
const relative = path.relative(parent, child);
return relative && !relative.startsWith('..');
};
this.components.forEach((existingComponentMap) => {
if (!existingComponentMap.rootDir) return;
if (existingComponentMap.rootDir === WORKSPACE_ROOT_DIR) return;
if (isParentDir(existingComponentMap.rootDir, rootDir)) {
throw new BitError(
`unable to add "${id.toString()}", its rootDir ${rootDir} is inside ${
Expand Down Expand Up @@ -212,14 +217,34 @@ export class BitMap {
delete componentsJson[LANE_KEY];
}

/**
* root-dirs of the components nested inside the given root-dir. their files belong to them, so
* the containing component must subtract them from its own file-set.
*/
getNestedRootDirs(rootDir: PathLinuxRelative): PathLinuxRelative[] {
return this.components
.map((componentMap) => componentMap.rootDir)
.filter((nested): nested is PathLinuxRelative => {
if (!nested || nested === rootDir) return false;
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
Outdated
if (rootDir === WORKSPACE_ROOT_DIR) return nested !== WORKSPACE_ROOT_DIR;
const relative = path.relative(rootDir, nested);
return Boolean(relative) && !relative.startsWith('..');
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
Outdated
});
}

async loadFiles() {
const gitIgnore = await getGitIgnoreHarmony(this.projectRoot, this.ignoredFiles);
await Promise.all(
this.components.map(async (componentMap) => {
const rootDir = componentMap.rootDir;
if (!rootDir) return;
try {
componentMap.files = await getFilesByDir(rootDir, this.projectRoot, gitIgnore);
componentMap.files = await getFilesByDir(
rootDir,
this.projectRoot,
gitIgnore,
this.getNestedRootDirs(rootDir)
);
componentMap.recentlyTracked = true;
} catch (err: any) {
componentMap.files = [];
Expand Down Expand Up @@ -1019,6 +1044,30 @@ type OutputFileParams = {
prefixMessage?: string;
};

/**
* the workspace-root component tracks `.bitmap` so a git-free workspace can be restored from the
* scope. the `version` and `scope` of every entry change on each snap and export - including the
* root component's own entry - so versioning them verbatim would leave that component modified
* immediately after every snap, forever, and never converge.
*
* only the durable part of the map is versioned: which components exist and where they live. the
* versions themselves are restored from the component heads on import, which is the correct source
* for them anyway.
*/
export function normalizeBitmapContentForVersioning(rawContent: string): string {
const parsed = json.parse(rawContent, undefined, true) as Record<string, any> | undefined;
if (!parsed) return rawContent;
Object.keys(parsed).forEach((key) => {
Comment thread
davidfirst marked this conversation as resolved.
const entry = parsed[key];
// component entries are objects with a mainFile. skips the schema field (a string) and the
// lanes key (an object without a mainFile).
if (!entry || typeof entry !== 'object' || Array.isArray(entry) || !('mainFile' in entry)) return;
if ('version' in entry) entry.version = '';
Comment thread
davidfirst marked this conversation as resolved.
Outdated
if ('scope' in entry) entry.scope = '';
Comment thread
davidfirst marked this conversation as resolved.
Outdated
});
return `${AUTO_GENERATED_MSG}${BITMAP_PREFIX_MESSAGE}${JSON.stringify(parsed, null, 4)}`;
}

async function outputFile({
filePath,
content,
Expand Down
71 changes: 59 additions & 12 deletions components/legacy/bit-map/component-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import globby from 'globby';
import ignore from 'ignore';
import { pickBy, isNil, sortBy, isEmpty } from 'lodash';
import type { ComponentID } from '@teambit/component-id';
import { BIT_MAP, Extensions, PACKAGE_JSON, IGNORE_ROOT_ONLY_LIST } from '@teambit/legacy.constants';
import {
BIT_HIDDEN_DIR,
BIT_MAP,
DOT_GIT_DIR,
Extensions,
PACKAGE_JSON,
IGNORE_ROOT_ONLY_LIST,
} from '@teambit/legacy.constants';
import { ValidationError } from '@teambit/legacy.cli.error';
import { logger } from '@teambit/legacy.logger';
import { isValidPath } from '@teambit/legacy.utils';
Expand All @@ -26,6 +33,24 @@ import { IgnoredDirectory, ComponentNotFoundInPath } from '@teambit/legacy.consu

export type Config = { [aspectId: string]: Record<string, any> | '-' };

/**
* rootDir of a component that owns the workspace root. such a component holds the files that no
* other component claims - e.g. the workspace config, CI config, README and license files.
* it is the only rootDir allowed to contain other components' root-dirs.
*/
export const WORKSPACE_ROOT_DIR = '.';

/**
* paths the workspace-root component must never own. relevant only for that component - for any
* other component these live outside its root-dir and are never reached by the scan.
*
* note that `.bitmap` is deliberately NOT here. it is the map of the workspace and a git-free
* workspace has to be able to restore it, so the root component tracks it like any other file.
* `.bit` (the local object store) and `.git` are the outputs of versioning, not sources, so they
* stay excluded.
*/
const WORKSPACE_ROOT_IGNORE_LIST = [`${BIT_HIDDEN_DIR}/**`, `${DOT_GIT_DIR}/**`];

export type ComponentMapFile = {
relativePath: PathLinux;
/**
Expand Down Expand Up @@ -265,13 +290,17 @@ export class ComponentMap {
* if the component dir has changed since the last tracking, re-scan the component-dir to get the
* updated list of the files
*/
async trackDirectoryChangesHarmony(consumerPath: PathOsBasedAbsolute, ignoredFiles?: string[]): Promise<void> {
async trackDirectoryChangesHarmony(
consumerPath: PathOsBasedAbsolute,
ignoredFiles?: string[],
excludeDirs: PathLinux[] = []
): Promise<void> {
const trackDir = this.rootDir;
if (!trackDir) {
return;
}
const gitIgnore = await getGitIgnoreHarmony(consumerPath, ignoredFiles);
this.files = await getFilesByDir(trackDir, consumerPath, gitIgnore);
this.files = await getFilesByDir(trackDir, consumerPath, gitIgnore, excludeDirs);
}

updateNextVersion(nextVersion: NextVersion) {
Expand Down Expand Up @@ -347,10 +376,8 @@ export class ComponentMap {
if (!isValidPath(this.mainFile)) {
throw new ValidationError(`${errorMessage} mainFile attribute ${this.mainFile} is invalid`);
}
if (this.rootDir && !isValidPath(this.rootDir)) {
throw new ValidationError(`${errorMessage} rootDir attribute ${this.rootDir} is invalid`);
}
if (this.rootDir && this.rootDir === '.') {
// "." is valid - it marks the component that owns the workspace root. see WORKSPACE_ROOT_DIR.
if (this.rootDir && this.rootDir !== WORKSPACE_ROOT_DIR && !isValidPath(this.rootDir)) {
throw new ValidationError(`${errorMessage} rootDir attribute ${this.rootDir} is invalid`);
}
if (this.nextVersion && !this.nextVersion.version) {
Expand Down Expand Up @@ -382,19 +409,39 @@ if you renamed the mainFile, please re-add the component with the "--main" flag
}
}

export async function getFilesByDir(dir: string, consumerPath: string, gitIgnore: any): Promise<ComponentMapFile[]> {
const matches = await globby(dir, {
/**
* scan a component's root-dir for its files.
*
* `excludeDirs` holds the root-dirs of components nested inside `dir`. their files belong to the
* nested component, not to this one. this is what makes a workspace-root component (rootDir ".")
* possible: it owns every file that no other component claims.
*/
export async function getFilesByDir(
dir: string,
consumerPath: string,
gitIgnore: any,
excludeDirs: PathLinux[] = []
): Promise<ComponentMapFile[]> {
const isWorkspaceRoot = dir === WORKSPACE_ROOT_DIR;
const matches = await globby(isWorkspaceRoot ? '**' : dir, {
Comment thread
davidfirst marked this conversation as resolved.
Outdated
cwd: consumerPath,
dot: true,
onlyFiles: true,
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
Outdated
// must ignore node_modules at this stage, although we check for gitignore later on.
// otherwise, it hurts performance dramatically for components that have node_modules in the comp-dir.
ignore: [`${dir}/node_modules/`],
ignore: [
isWorkspaceRoot ? '**/node_modules/**' : `${dir}/node_modules/`,
...(isWorkspaceRoot ? WORKSPACE_ROOT_IGNORE_LIST : []),
...excludeDirs.map((excludeDir) => `${excludeDir}/**`),
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
Outdated
],
});
if (!matches.length) throw new ComponentNotFoundInPath(dir);
const filteredMatches: string[] = gitIgnore.filter(matches);
// the path is relative to consumer. remove the rootDir.
const relativePathsLinux = filteredMatches.map((match) => pathNormalizeToLinux(match).replace(`${dir}/`, ''));
// the path is relative to consumer. remove the rootDir. for the workspace-root component the
// paths are already relative to the consumer, so there is nothing to strip.
const relativePathsLinux = filteredMatches.map((match) =>
isWorkspaceRoot ? pathNormalizeToLinux(match) : pathNormalizeToLinux(match).replace(`${dir}/`, '')
);
const filteredByIgnoredFromRoot = relativePathsLinux.filter((match) => !IGNORE_ROOT_ONLY_LIST.includes(match));
const bitOrGitIgnore = filteredByIgnoredFromRoot.includes(BIT_IGNORE)
? await getBitIgnoreFile(dir)
Expand Down
2 changes: 2 additions & 0 deletions components/legacy/bit-map/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export {
CURRENT_BITMAP_SCHEMA,
SCHEMA_FIELD,
LANE_KEY,
normalizeBitmapContentForVersioning,
} from './bit-map';
export { MissingBitMapComponent, MissingMainFile, InvalidBitMap } from './exceptions';
export {
Expand All @@ -14,4 +15,5 @@ export {
Config,
getIgnoreListHarmony,
NextVersion,
WORKSPACE_ROOT_DIR,
} from './component-map';
20 changes: 18 additions & 2 deletions components/legacy/consumer-component/consumer-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import { IssuesList } from '@teambit/component-issues';
import { BitId } from '@teambit/legacy-bit-id';
import { BitError } from '@teambit/bit-error';
import type { BuildStatus } from '@teambit/legacy.constants';
import { getCloudDomain, BIT_WORKSPACE_TMP_DIRNAME, DEFAULT_LANGUAGE, Extensions } from '@teambit/legacy.constants';
import {
getCloudDomain,
BIT_MAP,
BIT_WORKSPACE_TMP_DIRNAME,
DEFAULT_LANGUAGE,
Extensions,
} from '@teambit/legacy.constants';
import type { Doclet } from '@teambit/semantics.doc-parser';
import { parser as docsParser } from '@teambit/semantics.doc-parser';
import { logger } from '@teambit/legacy.logger';
Expand All @@ -16,6 +22,7 @@ import type { PathLinux, PathOsBased, PathOsBasedRelative } from '@teambit/toolb
import { pathNormalizeToLinux } from '@teambit/toolbox.path.path';
import { sha1 } from '@teambit/toolbox.crypto.sha1';
import type { ComponentMap } from '@teambit/legacy.bit-map';
import { normalizeBitmapContentForVersioning, WORKSPACE_ROOT_DIR } from '@teambit/legacy.bit-map';
import { IgnoredDirectory } from './exceptions/ignored-directory';
import type { Dist, PackageJsonFile, DataToPersist } from '@teambit/component.sources';
import { License, SourceFile } from '@teambit/component.sources';
Expand Down Expand Up @@ -600,10 +607,19 @@ async function getLoadedFiles(
logger.error(`rethrowing an error of ${componentMap.noFilesError.message}`);
throw componentMap.noFilesError;
}
await componentMap.trackDirectoryChangesHarmony(consumer.getPath(), consumer.config.ignoredFiles);
await componentMap.trackDirectoryChangesHarmony(
consumer.getPath(),
consumer.config.ignoredFiles,
consumer.bitMap.getNestedRootDirs(componentMap.getRootDir())
);
const sourceFiles = componentMap.files.map((file) => {
const filePath = path.join(bitDir, file.relativePath);
const sourceFile = SourceFile.load(filePath, bitDir, consumer.getPath(), { test: file.test || false });
// the workspace-root component owns .bitmap. strip the fields that change on every snap so the
// component converges instead of being modified again the moment it is snapped.
if (componentMap.rootDir === WORKSPACE_ROOT_DIR && file.relativePath === BIT_MAP) {
sourceFile.contents = Buffer.from(normalizeBitmapContentForVersioning(sourceFile.contents.toString()));
}
Comment thread
davidfirst marked this conversation as resolved.
Outdated
return sourceFile;
});
const filePaths = componentMap.getAllFilesPaths();
Expand Down
Loading
Loading