@teambit/workspace 1.0.967 → 1.0.969

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import * as compositions_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad/teambit.workspace_workspace@1.0.967/dist/workspace.composition.js';
2
- import * as overview_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad/teambit.workspace_workspace@1.0.967/dist/workspace.docs.mdx';
1
+ import * as compositions_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad/teambit.workspace_workspace@1.0.969/dist/workspace.composition.js';
2
+ import * as overview_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad/teambit.workspace_workspace@1.0.969/dist/workspace.docs.mdx';
3
3
 
4
4
  export const compositions = [compositions_0];
5
5
  export const overview = [overview_0];
package/dist/types.d.ts CHANGED
@@ -81,5 +81,22 @@ export interface WorkspaceExtConfig {
81
81
  * Example: ["oxlint.config.json", "biome.json", "*.bak"]
82
82
  */
83
83
  ignoredFiles?: string[];
84
+ /**
85
+ * If set to `true`, Bit auto-syncs the local `.bitmap` to the latest scope HEAD versions
86
+ * whenever the git HEAD has moved since the last sync (sentinel-driven, runs once per
87
+ * `git pull`). Designed for repos with strict branch-protection rules: combined with
88
+ * `bit ci merge --no-bitmap-commit`, the CI never commits `.bitmap` to the default
89
+ * branch — every developer's first Bit command after `git pull` reconciles their
90
+ * local `.bitmap` with the latest exported scope versions automatically.
91
+ *
92
+ * The mechanism is a no-op when:
93
+ * - the workspace is not inside a git repo,
94
+ * - the workspace is on a lane (lanes have their own sync flow),
95
+ * - git HEAD is unchanged since the last successful reconciliation.
96
+ *
97
+ * On a failed remote-scope fetch, the command continues with the cached `.bitmap`
98
+ * state and the sentinel is NOT advanced, so the next command retries.
99
+ */
100
+ bitmapAutoSync?: boolean;
84
101
  }
85
102
  export {};
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["interface VendorConfig {\n directory: string;\n}\n\nexport interface WorkspaceExtConfig {\n /**\n * name of the workspace.\n */\n name: string;\n\n /**\n * path to icon.\n */\n icon: string;\n\n /**\n * set the default scope when there is no matching for the component in the components array.\n */\n defaultScope: string;\n\n /**\n * set the default directory when there is no matching for the component in the components array.\n */\n defaultDirectory: string;\n\n /**\n * sets the location of the root components directory.\n * The location is a relative path to the workspace root and should use linux path separators (/).\n */\n rootComponentsDirectory?: string;\n\n /**\n * set the default structure of components in your project\n */\n vendor: VendorConfig;\n\n /**\n * All component extensions applied by default on all components in the workspace (except vendor components)\n */\n extensions: { [extensionsId: string]: string };\n\n /**\n * If set to\n * `true`, it allows the workspace to resolve scope's aspects from node modules\n * installed in the workspace's `node_modules` directory. If not set or set to `false`, aspects will only be resolved\n * from the scope aspects capsule.\n */\n resolveAspectsFromNodeModules?: boolean;\n\n /**\n * If set to `true`, it allows the workspace to resolve envs from node modules\n * installed in the workspace's `node_modules` directory.\n * the envs will be resolved from the node_modules of the env's root (workspace/node_modules/.bit_roots/{envId})\n * and if not found (usually when the env was hoisted to the root node_modules) then from the node_modules of the\n * workspace.\n * If not set or set to `false`, envs will only be resolved from the scope envs capsule.\n */\n resolveEnvsFromRoots?: boolean;\n\n /**\n * If set to `true`, bit will try to load aspects dependencies automatically.\n * even if the aspects dependencies are not configured in the workspace.jsonc root config.\n * for example having the aspect\n * main aspect\n * export class MainAspectMain {\n * ...\n * static dependencies = [MyDepAspect];\n * }\n * and the in the workspace.jsonc file:\n * {\n * ...\n * main-aspect: {}\n * }\n * when set to true, bit will try to load MyDepAspect automatically.\n */\n autoLoadAspectsDeps?: boolean;\n\n /**\n * If set to `true`, enables external package manager mode. When enabled:\n * - `bit install` will not install dependencies and will prompt the user to use their package manager.\n * - Other commands that trigger installation (e.g., `bit import`, `bit checkout`) will skip the installation and print a warning.\n * When this prop is set by bit to `true`, the following properties are automatically set to `false`:\n * - `rootComponent`.\n * - `enableWorkspaceConfigWrite`.\n */\n externalPackageManager?: boolean;\n\n /**\n * List of file patterns to ignore from all components in the workspace.\n * Uses gitignore syntax.\n * Example: [\"oxlint.config.json\", \"biome.json\", \"*.bak\"]\n */\n ignoredFiles?: string[];\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["interface VendorConfig {\n directory: string;\n}\n\nexport interface WorkspaceExtConfig {\n /**\n * name of the workspace.\n */\n name: string;\n\n /**\n * path to icon.\n */\n icon: string;\n\n /**\n * set the default scope when there is no matching for the component in the components array.\n */\n defaultScope: string;\n\n /**\n * set the default directory when there is no matching for the component in the components array.\n */\n defaultDirectory: string;\n\n /**\n * sets the location of the root components directory.\n * The location is a relative path to the workspace root and should use linux path separators (/).\n */\n rootComponentsDirectory?: string;\n\n /**\n * set the default structure of components in your project\n */\n vendor: VendorConfig;\n\n /**\n * All component extensions applied by default on all components in the workspace (except vendor components)\n */\n extensions: { [extensionsId: string]: string };\n\n /**\n * If set to\n * `true`, it allows the workspace to resolve scope's aspects from node modules\n * installed in the workspace's `node_modules` directory. If not set or set to `false`, aspects will only be resolved\n * from the scope aspects capsule.\n */\n resolveAspectsFromNodeModules?: boolean;\n\n /**\n * If set to `true`, it allows the workspace to resolve envs from node modules\n * installed in the workspace's `node_modules` directory.\n * the envs will be resolved from the node_modules of the env's root (workspace/node_modules/.bit_roots/{envId})\n * and if not found (usually when the env was hoisted to the root node_modules) then from the node_modules of the\n * workspace.\n * If not set or set to `false`, envs will only be resolved from the scope envs capsule.\n */\n resolveEnvsFromRoots?: boolean;\n\n /**\n * If set to `true`, bit will try to load aspects dependencies automatically.\n * even if the aspects dependencies are not configured in the workspace.jsonc root config.\n * for example having the aspect\n * main aspect\n * export class MainAspectMain {\n * ...\n * static dependencies = [MyDepAspect];\n * }\n * and the in the workspace.jsonc file:\n * {\n * ...\n * main-aspect: {}\n * }\n * when set to true, bit will try to load MyDepAspect automatically.\n */\n autoLoadAspectsDeps?: boolean;\n\n /**\n * If set to `true`, enables external package manager mode. When enabled:\n * - `bit install` will not install dependencies and will prompt the user to use their package manager.\n * - Other commands that trigger installation (e.g., `bit import`, `bit checkout`) will skip the installation and print a warning.\n * When this prop is set by bit to `true`, the following properties are automatically set to `false`:\n * - `rootComponent`.\n * - `enableWorkspaceConfigWrite`.\n */\n externalPackageManager?: boolean;\n\n /**\n * List of file patterns to ignore from all components in the workspace.\n * Uses gitignore syntax.\n * Example: [\"oxlint.config.json\", \"biome.json\", \"*.bak\"]\n */\n ignoredFiles?: string[];\n\n /**\n * If set to `true`, Bit auto-syncs the local `.bitmap` to the latest scope HEAD versions\n * whenever the git HEAD has moved since the last sync (sentinel-driven, runs once per\n * `git pull`). Designed for repos with strict branch-protection rules: combined with\n * `bit ci merge --no-bitmap-commit`, the CI never commits `.bitmap` to the default\n * branch — every developer's first Bit command after `git pull` reconciles their\n * local `.bitmap` with the latest exported scope versions automatically.\n *\n * The mechanism is a no-op when:\n * - the workspace is not inside a git repo,\n * - the workspace is on a lane (lanes have their own sync flow),\n * - git HEAD is unchanged since the last successful reconciliation.\n *\n * On a failed remote-scope fetch, the command continues with the cached `.bitmap`\n * state and the sentinel is NOT advanced, so the next command retries.\n */\n bitmapAutoSync?: boolean;\n}\n"],"mappings":"","ignoreList":[]}
@@ -439,6 +439,23 @@ export declare class Workspace implements ComponentFactory {
439
439
  */
440
440
  importCurrentObjects(compIds?: ComponentID[]): Promise<void>;
441
441
  importCurrentLaneIfMissing(): Promise<Lane | undefined>;
442
+ /**
443
+ * When `bitmapAutoSync` is enabled in workspace.jsonc, reconcile `.bitmap` with the
444
+ * latest scope HEAD versions on the first Bit command after `git pull`. The sentinel
445
+ * file at `.bit/last-pull-sync` makes subsequent commands at the same git HEAD a no-op.
446
+ *
447
+ * On a failed remote-scope fetch the sentinel is NOT advanced, so the next command
448
+ * retries.
449
+ */
450
+ reconcileBitmapWithScopeIfNeeded(): Promise<void>;
451
+ /**
452
+ * Resolve the current git HEAD. Tries a direct read of `.git/HEAD` (+ the loose ref
453
+ * file it points at) for the common case to avoid forking a process. Falls back to
454
+ * `git rev-parse HEAD` for packed-refs, git worktrees / submodules (where `.git` is
455
+ * a file pointing at the real gitdir, not a directory), and any other layout. Returns
456
+ * null when HEAD can't be resolved at all — the caller treats that as "skip auto-sync."
457
+ */
458
+ private readCurrentGitHead;
442
459
  use(aspectIdStr: string): Promise<string>;
443
460
  unuse(aspectIdStr: string): Promise<boolean>;
444
461
  write(component: Component, rootPath?: string): Promise<void>;
package/dist/workspace.js CHANGED
@@ -298,6 +298,27 @@ function _componentStatusLoader() {
298
298
  };
299
299
  return data;
300
300
  }
301
+ function _execa() {
302
+ const data = _interopRequireDefault(require("execa"));
303
+ _execa = function () {
304
+ return data;
305
+ };
306
+ return data;
307
+ }
308
+ function _gitModules() {
309
+ const data = require("@teambit/git.modules.git-executable");
310
+ _gitModules = function () {
311
+ return data;
312
+ };
313
+ return data;
314
+ }
315
+ function _objects() {
316
+ const data = require("@teambit/objects");
317
+ _objects = function () {
318
+ return data;
319
+ };
320
+ return data;
321
+ }
301
322
  function _autoTag() {
302
323
  const data = require("./auto-tag");
303
324
  _autoTag = function () {
@@ -1409,6 +1430,162 @@ the following envs are used in this workspace: ${(0, _lodash().uniq)(availableEn
1409
1430
  });
1410
1431
  return lane;
1411
1432
  }
1433
+
1434
+ /**
1435
+ * When `bitmapAutoSync` is enabled in workspace.jsonc, reconcile `.bitmap` with the
1436
+ * latest scope HEAD versions on the first Bit command after `git pull`. The sentinel
1437
+ * file at `.bit/last-pull-sync` makes subsequent commands at the same git HEAD a no-op.
1438
+ *
1439
+ * On a failed remote-scope fetch the sentinel is NOT advanced, so the next command
1440
+ * retries.
1441
+ */
1442
+ async reconcileBitmapWithScopeIfNeeded() {
1443
+ if (!this.config.bitmapAutoSync) return;
1444
+ if (!this.consumer.isOnMain()) return;
1445
+ if (!(await _fsExtra().default.pathExists(_path().default.join(this.path, '.git')))) return;
1446
+
1447
+ // Read the sentinel before resolving git HEAD — most invocations are cached and
1448
+ // exit cheaply here without forking a git subprocess. ENOENT (no sentinel yet) is
1449
+ // the normal first-run path; any other read error (EACCES, EIO, etc.) indicates
1450
+ // a genuine problem and should surface rather than be silently swallowed — masking
1451
+ // it would put every subsequent command on the slow full-fetch path with no signal.
1452
+ const sentinelPath = _path().default.join(this.path, '.bit', 'last-pull-sync');
1453
+ let sentinelHead;
1454
+ try {
1455
+ sentinelHead = (await _fsExtra().default.readFile(sentinelPath, 'utf-8')).trim();
1456
+ } catch (err) {
1457
+ if (err?.code !== 'ENOENT') throw err;
1458
+ }
1459
+ const currentGitHead = await this.readCurrentGitHead();
1460
+ if (!currentGitHead) return;
1461
+ if (sentinelHead === currentGitHead) return;
1462
+ this.logger.debug(`bitmapAutoSync: git HEAD changed since last sync, reconciling .bitmap with scope`);
1463
+
1464
+ // Resolve every bitmap entry to a "lookup id" that has scope filled in. New
1465
+ // components (added via `bit add` on a feature branch but tagged only by CI) have
1466
+ // an empty `scope` in the bitmap — only `defaultScope` is set. We must resolve
1467
+ // those before the import, because the scope import filters out IDs without scope
1468
+ // and the model lookup below needs scope to find the component.
1469
+ //
1470
+ // Related: `ComponentLoader._handleOutOfSyncScenarios` in
1471
+ // components/legacy/consumer-component/component-loader.ts uses the same primitives
1472
+ // (`getModelComponentIfExist`, `bitMap.updateComponentId`) to fix per-component
1473
+ // out-of-sync states during component loading. It does NOT handle:
1474
+ // - scope HEAD has advanced past bitmap's version when that version still exists
1475
+ // in scope (the main case `bitmapAutoSync` is built for),
1476
+ // - bitmap entries with empty `scope` field (it doesn't fall back to defaultScope),
1477
+ // - triggering the import itself with `includeUnexported: true` for components
1478
+ // not yet in local scope.
1479
+ // This reconcile fills those gaps; afterwards `_handleOutOfSyncScenarios` is a
1480
+ // no-op for the same components (bitmap is already aligned with scope).
1481
+ const lookupIds = [];
1482
+ for (const bitmapId of this.consumer.bitMap.getAllIdsAvailableOnLane()) {
1483
+ if (bitmapId.hasScope()) {
1484
+ lookupIds.push(bitmapId);
1485
+ continue;
1486
+ }
1487
+ const componentMap = this.consumer.bitMap.getComponentIfExist(bitmapId);
1488
+ const defaultScope = componentMap?.defaultScope;
1489
+ if (!defaultScope) continue;
1490
+ lookupIds.push(bitmapId.changeScope(defaultScope));
1491
+ }
1492
+
1493
+ // Fetch the latest scope HEADs from remote for all of these. Use the lower-level
1494
+ // scope importer with `includeUnexported: true` — `importCurrentObjects` filters
1495
+ // out IDs that aren't already in the local scope, which would skip brand-new
1496
+ // components introduced by another teammate's PR (they exist on remote but the
1497
+ // current workspace's local scope has never seen them).
1498
+ const scopeComponentsImporter = _legacy7().ScopeComponentsImporter.getInstance(this.scope.legacyScope);
1499
+ const idList = _componentId().ComponentIdList.fromArray(lookupIds);
1500
+ try {
1501
+ await scopeComponentsImporter.importWithoutDeps(idList.toVersionLatest(), {
1502
+ cache: false,
1503
+ includeVersionHistory: true,
1504
+ fetchHeadIfLocalIsBehind: true,
1505
+ ignoreMissingHead: true,
1506
+ includeUnexported: true,
1507
+ reason: 'bitmapAutoSync: latest scope HEAD'
1508
+ });
1509
+ await scopeComponentsImporter.importMany({
1510
+ ids: idList,
1511
+ ignoreMissingHead: true,
1512
+ preferDependencyGraph: true,
1513
+ reFetchUnBuiltVersion: true,
1514
+ throwForSeederNotFound: false,
1515
+ includeUnexported: true,
1516
+ reason: 'bitmapAutoSync: dependencies for sync'
1517
+ });
1518
+ } catch (err) {
1519
+ this.logger.warn(`bitmapAutoSync: failed to fetch latest scope objects: ${err?.message ?? err}. ` + `Continuing with the cached state. The next bit command will re-attempt the sync.`);
1520
+ // Sentinel stays at the previous HEAD so the next command retries the fetch.
1521
+ return;
1522
+ }
1523
+ const updatedIds = (0, _lodash().compact)(await (0, _pMap().default)(lookupIds, async lookupId => {
1524
+ const modelComponent = await this.scope.legacyScope.getModelComponentIfExist(lookupId.changeVersion(undefined));
1525
+ if (!modelComponent) return undefined;
1526
+ let scopeHead;
1527
+ try {
1528
+ scopeHead = modelComponent.getHeadRegardlessOfLaneAsTagOrHash(true);
1529
+ } catch {
1530
+ return undefined;
1531
+ }
1532
+ if (!scopeHead || scopeHead === _objects().VERSION_ZERO) return undefined;
1533
+ if (lookupId.version === scopeHead) return undefined;
1534
+ const newId = lookupId.changeVersion(scopeHead);
1535
+ this.consumer.bitMap.updateComponentId(newId);
1536
+ return newId;
1537
+ }, {
1538
+ concurrency: 30
1539
+ }));
1540
+ if (updatedIds.length > 0) {
1541
+ await this.bitMap.write('bitmapAutoSync: reconciled with scope HEAD');
1542
+ this.logger.console(`bitmapAutoSync: synced ${updatedIds.length} component(s) to scope HEAD`);
1543
+ }
1544
+
1545
+ // Let a sentinel write failure throw — `.bit/` was already written above (bitmap
1546
+ // and scope objects), so a permission failure here means something is genuinely
1547
+ // broken. Catching would silently leave the sentinel stale and force every later
1548
+ // command into the slow full-fetch path with no signal to the user.
1549
+ await _fsExtra().default.outputFile(sentinelPath, currentGitHead);
1550
+ }
1551
+
1552
+ /**
1553
+ * Resolve the current git HEAD. Tries a direct read of `.git/HEAD` (+ the loose ref
1554
+ * file it points at) for the common case to avoid forking a process. Falls back to
1555
+ * `git rev-parse HEAD` for packed-refs, git worktrees / submodules (where `.git` is
1556
+ * a file pointing at the real gitdir, not a directory), and any other layout. Returns
1557
+ * null when HEAD can't be resolved at all — the caller treats that as "skip auto-sync."
1558
+ */
1559
+ async readCurrentGitHead() {
1560
+ const gitPath = _path().default.join(this.path, '.git');
1561
+ let gitIsDir = false;
1562
+ try {
1563
+ gitIsDir = (await _fsExtra().default.stat(gitPath)).isDirectory();
1564
+ } catch {
1565
+ return null;
1566
+ }
1567
+ if (gitIsDir) {
1568
+ try {
1569
+ const headContent = (await _fsExtra().default.readFile(_path().default.join(gitPath, 'HEAD'), 'utf-8')).trim();
1570
+ if (!headContent.startsWith('ref: ')) return headContent; // detached HEAD
1571
+ const refName = headContent.slice('ref: '.length);
1572
+ return (await _fsExtra().default.readFile(_path().default.join(gitPath, refName), 'utf-8')).trim();
1573
+ } catch {
1574
+ // Loose ref missing (likely packed) or some other unusual state. Fall through.
1575
+ }
1576
+ }
1577
+
1578
+ // Fallback: invoke git itself. Handles worktrees (where `.git` is a file with
1579
+ // `gitdir:` indirection), packed-refs, submodules, and any other layout.
1580
+ try {
1581
+ const result = await (0, _execa().default)((0, _gitModules().getGitExecutablePath)(), ['rev-parse', 'HEAD'], {
1582
+ cwd: this.path
1583
+ });
1584
+ return result.stdout.trim();
1585
+ } catch {
1586
+ return null;
1587
+ }
1588
+ }
1412
1589
  async use(aspectIdStr) {
1413
1590
  const workspaceAspectsLoader = this.getWorkspaceAspectsLoader();
1414
1591
  return workspaceAspectsLoader.use(aspectIdStr);