Skip to content

Commit

Permalink
fix #2211 and #2308, when exporting to multiple scopes, make sure to …
Browse files Browse the repository at this point in the history
…not export their dependencies when these dependencies themselves are not export pending
  • Loading branch information
davidfirst committed Feb 6, 2020
1 parent 642f6d4 commit 174e408
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [unreleased]

- [#2211](https://github.com/teambit/bit/issues/2211) fix bit export to not export non-staged dependencies
- [#2308](https://github.com/teambit/bit/issues/2308) fix "Cannot read property 'scope' of undefined" error on bit export

## [[14.7.4] - 2020-02-06](https://github.com/teambit/bit/releases/tag/v14.7.4)

- [#2300](https://github.com/teambit/bit/issues/2300) improve `bit export` performance by pushing new tags only
Expand Down
33 changes: 33 additions & 0 deletions e2e/commands/export.e2e.1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,39 @@ describe('bit export command', function() {
expect(output).to.have.string(`${anotherRemote}/foo2`);
});
});
// fixes https://github.com/teambit/bit/issues/2308
// here, the component foo1 has a new dependency "bar", this dependency has been exported
// already, so we expect "bit export" to not attempt to export it.
describe('export with no ids, no remote and no flags when a dependency is from another collection', () => {
let output;
before(() => {
helper.scopeHelper.getClonedLocalScope(localScopeBefore);
helper.scopeHelper.getClonedRemoteScope(remoteScopeBefore);
helper.scopeHelper.getClonedScope(anotherRemoteScopeBefore, anotherRemotePath);
const { scopeName, scopePath } = helper.scopeHelper.getNewBareScope();
helper.scopeHelper.addRemoteScope(scopePath);
helper.scopeHelper.addRemoteScope(scopePath, helper.scopes.remotePath);
helper.fs.outputFile('bar.js', '');
helper.command.addComponent('bar.js');
helper.fs.outputFile('foo1.js', 'require("./bar");');
helper.command.tagAllComponents();
helper.command.exportComponent('bar', scopeName);
output = helper.command.runCmd('bit export');
});
it('should export successfully all ids, each to its own remote', () => {
const remoteList = helper.command.listRemoteScopeParsed();
expect(remoteList).to.have.lengthOf(1);
expect(remoteList[0].id).to.have.string('foo1');

const anotherRemoteListJson = helper.command.runCmd(`bit list ${anotherRemote} --json`);
const anotherRemoteList = JSON.parse(anotherRemoteListJson);
expect(anotherRemoteList).to.have.lengthOf(1);
expect(anotherRemoteList[0].id).to.have.string('foo2');
});
it('should not export the dependency that was not intended to be exported', () => {
expect(output).to.not.have.string('bar');
});
});
describe('export with ids, no remote and the flag --last-scope', () => {
let output;
before(() => {
Expand Down
24 changes: 22 additions & 2 deletions src/scope/component-ops/export-scope-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,15 @@ export async function exportMany({
}
const remotes: Remotes = await getScopeRemotes(scope);
if (remoteName) {
logger.debugAndAddBreadCrumb('export-scope-components', 'export all ids to one remote');
return exportIntoRemote(remoteName, ids);
}
logger.debugAndAddBreadCrumb('export-scope-components', 'export ids to multiple remotes');
const groupedByScope = await sortAndGroupByScope();
const groupedByScopeString = groupedByScope
.map(item => `scope "${item.scopeName}": ${item.ids.toString()}`)
.join(', ');
logger.debug(`export-scope-components, export to the following scopes ${groupedByScopeString}`);
const results = await pMapSeries(groupedByScope, result => exportIntoRemote(result.scopeName, result.ids));
return {
exported: BitIds.uniqFromArray(R.flatten(results.map(r => r.exported))),
Expand Down Expand Up @@ -189,6 +195,15 @@ export async function exportMany({
* 3) it's possible to have circle dependencies inside the same scope, and non-circle
* dependencies between the different scopes. in this case, the toposort should be done after
* removing the ids participated in the circular.
*
* once the sort is done, it returns an array of { scopeName: string; ids: BitIds }.
* keep in mind that this array might have multiple items with the same scopeName, that's totally
* valid and it will cause multiple round-trip to the same scope. there is no other way around
* it.
* the sort is done after eliminating circles, so it's possible to execute topsort. once the
* components are topological sorted, they are added one by one to the results array. If the last
* item in the array has the same scope as the currently inserted component, it can be added to
* the same scope group. otherwise, a new item needs to be added to the array with the new scope.
*/
async function sortAndGroupByScope(): Promise<{ scopeName: string; ids: BitIds }[]> {
const grouped = ids.toGroupByScopeName(idsWithFutureScope);
Expand All @@ -209,8 +224,13 @@ export async function exportMany({
return;
}
}
const idWithFutureScope = idsWithFutureScope.searchWithoutScopeAndVersion(id) as BitId;
groupedArraySorted.push({ scopeName: idWithFutureScope.scope as string, ids: new BitIds(id) });
const idWithFutureScope = idsWithFutureScope.searchWithoutScopeAndVersion(id);
if (idWithFutureScope) {
groupedArraySorted.push({ scopeName: idWithFutureScope.scope as string, ids: new BitIds(id) });
}
// otherwise, it's in the graph, but not in the idWithFutureScope array. this is probably just a
// dependency of one of the pending-export ids, and that dependency is not supposed to be
// export, so just ignore it.
};
if (cycles.length) {
const cyclesWithMultipleScopes = cycles.filter(cycle => {
Expand Down

0 comments on commit 174e408

Please sign in to comment.