Skip to content

Commit

Permalink
fix Capsule.component to be Component and not ConsumerComponent (#2858)
Browse files Browse the repository at this point in the history
* fix Capsule.component to be Component and not ConsumerComponent

* move capsule commands from isolate-ext to workspace-ext
  • Loading branch information
davidfirst committed Jul 18, 2020
1 parent caeddeb commit 6c882b6
Show file tree
Hide file tree
Showing 15 changed files with 91 additions and 117 deletions.
2 changes: 1 addition & 1 deletion src/extensions/builder/builder.service.ts
Expand Up @@ -35,7 +35,7 @@ export class BuilderService implements EnvService {
);

const buildContext = Object.assign(context, {
capsuleGraph: await this.isolator.createNetworkFromConsumer(
capsuleGraph: await this.workspace.createNetwork(
context.components.map((component) => component.id.toString()),
this.workspace.consumer
),
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/builder/run.cmd.tsx
Expand Up @@ -41,7 +41,7 @@ export class BuilderCmd implements Command {
// @todo: decide about the output
results.forEach((
result // eslint-disable-next-line no-console
) => console.log('result', `Env: ${result.env}\nResult: ${JSON.stringify(result.res, undefined, 2)}`));
) => console.log('result', `Env: ${result.env}\nResult: success`));
this.reporter.end();

return `compiled ${results.length} components successfully`;
Expand Down
3 changes: 1 addition & 2 deletions src/extensions/flows/cache.ts
Expand Up @@ -16,8 +16,7 @@ export class ExecutionCache {

hash(capsule: Capsule, name: string) {
const configString = JSON.stringify(path(['component', 'extensions'], capsule));
// for some reason in this point i get consumerComponent and not a component
const consumerComponent = (capsule.component as any) as ConsumerComponent;
const consumerComponent = capsule.component.state._consumer;
const { files, packageJsonFile } = consumerComponent;
const vinylFiles: AbstractVinyl[] = [...files];
if (packageJsonFile) vinylFiles.push(packageJsonFile.toVinylFile());
Expand Down
14 changes: 5 additions & 9 deletions src/extensions/isolator/capsule/capsule.ts
Expand Up @@ -74,16 +74,12 @@ export default class Capsule extends CapsuleTemplate<Exec, NodeFS> {
return this.container.symlink(src, dest);
}

static async createFromComponent(component: Component, baseDir: string, opts?: {}): Promise<Capsule> {
static async createFromComponent(
component: Component,
baseDir: string,
config: { alwaysNew?: boolean; name?: string } = {}
): Promise<Capsule> {
// TODO: make this a static method and combine with ComponentCapsule
const config = Object.assign(
{
alwaysNew: false,
name: undefined,
},
opts
);

const capsuleDirName = config.name || filenamify(component.id.toString(), { replacement: '_' });
const wrkDir = path.join(baseDir, config.alwaysNew ? `${capsuleDirName}_${v4()}` : capsuleDirName);
const container = new FsContainer(wrkDir);
Expand Down
75 changes: 14 additions & 61 deletions src/extensions/isolator/isolator.extension.ts
@@ -1,27 +1,21 @@
import path from 'path';
import hash from 'object-hash';
import fs from 'fs-extra';
import { flatten, filter, uniq, concat, map, equals } from 'ramda';
import { map, equals } from 'ramda';
import { CACHE_ROOT, PACKAGE_JSON } from '../../constants';
import { Component, ComponentID } from '../component';
import { Component } from '../component';
import ConsumerComponent from '../../consumer/component';
import { DependencyResolverExtension } from '../dependency-resolver';
import { Capsule } from './capsule';
import writeComponentsToCapsules from './write-components-to-capsules';
import Consumer from '../../consumer/consumer';
import CapsuleList from './capsule-list';
import { CapsuleListCmd } from './capsule-list.cmd';
import { CapsuleCreateCmd } from './capsule-create.cmd';
import Graph from '../../scope/graph/graph'; // TODO: use graph extension?
import { BitId, BitIds } from '../../bit-id';
import { buildOneGraphForComponents } from '../../scope/graph/components-graph';
import PackageJsonFile from '../../consumer/component/package-json-file';
import componentIdToPackageName from '../../utils/bit/component-id-to-package-name';
import { symlinkDependenciesToCapsules } from './symlink-dependencies-to-capsules';
import logger from '../../logger/logger';
import { CLIExtension } from '../cli';
import { DEPENDENCIES_FIELDS } from '../../constants';
import { Network } from './network';

const CAPSULES_BASE_DIR = path.join(CACHE_ROOT, 'capsules'); // TODO: move elsewhere

Expand All @@ -31,7 +25,7 @@ export type ListResults = {
capsules: string[];
};

async function createCapsulesFromComponents(components: any[], baseDir, orchOptions): Promise<Capsule[]> {
async function createCapsulesFromComponents(components: Component[], baseDir: string, orchOptions): Promise<Capsule[]> {
const capsules: Capsule[] = await Promise.all(
map((component: Component) => {
return Capsule.createFromComponent(component, baseDir, orchOptions);
Expand All @@ -40,48 +34,17 @@ async function createCapsulesFromComponents(components: any[], baseDir, orchOpti
return capsules;
}

function findSuccessorsInGraph(graph: Graph, seeders: string[]) {
const dependenciesFromAllIds = flatten(seeders.map((bitId) => graph.getSuccessorsByEdgeTypeRecursively(bitId)));
const components: ConsumerComponent[] = filter(
(val) => val,
uniq(concat(dependenciesFromAllIds, seeders)).map((id: string) => graph.node(id))
);
return components;
}

export class IsolatorExtension {
static id = '@teambit/isolator';
static dependencies = [DependencyResolverExtension, CLIExtension];
static dependencies = [DependencyResolverExtension];
static defaultConfig = {};
static async provide([dependencyResolver, cli]: IsolatorDeps) {
static async provide([dependencyResolver]: IsolatorDeps) {
const isolator = new IsolatorExtension(dependencyResolver);
const capsuleListCmd = new CapsuleListCmd(isolator);
const capsuleCreateCmd = new CapsuleCreateCmd(isolator);
cli.register(capsuleListCmd);
cli.register(capsuleCreateCmd);
return isolator;
}
constructor(private dependencyResolver: DependencyResolverExtension) {}

async createNetworkFromConsumer(seeders: string[], consumer: Consumer, opts?: {}): Promise<Network> {
logger.debug(`isolatorExt, createNetworkFromConsumer ${seeders.join(', ')}`);
const seedersIds = seeders.map((seeder) => consumer.getParsedId(seeder));
const graph = await buildOneGraphForComponents(seedersIds, consumer);
const baseDir = path.join(CAPSULES_BASE_DIR, hash(consumer.projectPath)); // TODO: move this logic elsewhere
opts = Object.assign(opts || {}, { consumer });
return this.createNetwork(seedersIds, graph, baseDir, opts);
}
private getBitIdsIncludeVersionsFromGraph(seedersIds: BitId[], graph: Graph): BitId[] {
const components: ConsumerComponent[] = graph.nodes().map((n) => graph.node(n));
return seedersIds.map((seederId) => {
const component = components.find((c) => c.id.isEqual(seederId) || c.id.isEqualWithoutVersion(seederId));
if (!component) throw new Error(`unable to find ${seederId.toString()} in the graph`);
return component.id;
});
}
private async createNetwork(seedersIds: BitId[], graph: Graph, baseDir, opts?: {}) {
const seederIds = this.getBitIdsIncludeVersionsFromGraph(seedersIds, graph);
const seeders = seederIds.map((s) => s.toString());
public async isolateComponents(baseDir: string, components: Component[], opts?: {}): Promise<CapsuleList> {
const config = Object.assign(
{},
{
Expand All @@ -90,25 +53,19 @@ export class IsolatorExtension {
},
opts
);
const compsAndDeps = findSuccessorsInGraph(graph, seeders);
const filterNonWorkspaceComponents = () => {
// @ts-ignore @todo: fix this opts to have types
const consumer: Consumer = opts?.consumer;
if (!consumer) return compsAndDeps;
return compsAndDeps.filter((c) => consumer.bitMap.getComponentIfExist(c.id, { ignoreVersion: true }));
};
const components = filterNonWorkspaceComponents();
const capsules = await createCapsulesFromComponents(components, baseDir, config);
const capsulesDir = path.join(CAPSULES_BASE_DIR, hash(baseDir)); // TODO: move this logic elsewhere
const capsules = await createCapsulesFromComponents(components, capsulesDir, config);

const capsuleList = new CapsuleList(
...capsules.map((c) => {
const id = c.component.id instanceof BitId ? new ComponentID(c.component.id) : c.component.id;
const id = c.component.id;
return { id, capsule: c };
})
);
const capsulesWithPackagesData = await getCapsulesPreviousPackageJson(capsules);

await writeComponentsToCapsules(components, capsuleList);
const consumerComponents = components.map((c) => c.state._consumer);
await writeComponentsToCapsules(consumerComponents, capsuleList);
updateWithCurrentPackageJsonData(capsulesWithPackagesData, capsules);
if (config.installPackages) {
const capsulesToInstall: Capsule[] = capsulesWithPackagesData
Expand All @@ -132,12 +89,9 @@ export class IsolatorExtension {
);
});

return new Network(
capsuleList,
graph,
seederIds.map((s) => new ComponentID(s))
);
return capsuleList;
}

async list(consumer: Consumer): Promise<ListResults> {
const workspacePath = consumer.getPath();
try {
Expand Down Expand Up @@ -191,8 +145,7 @@ async function getCapsulesPreviousPackageJson(capsules: Capsule[]): Promise<Caps

function updateWithCurrentPackageJsonData(capsulesWithPackagesData: CapsulePackageJsonData[], capsules: Capsule[]) {
capsules.forEach((capsule) => {
// @ts-ignore
const component: ConsumerComponent = capsule.component as ConsumerComponent;
const component: ConsumerComponent = capsule.component.state._consumer;
const packageJson = getCurrentPackageJson(component, capsule);
const found = capsulesWithPackagesData.find((c) => c.capsule.component.id.isEqual(capsule.component.id));
if (!found) throw new Error(`updateWithCurrentPackageJsonData unable to find ${capsule.component.id}`);
Expand Down
12 changes: 3 additions & 9 deletions src/extensions/isolator/readme.md
Expand Up @@ -23,12 +23,6 @@ const Network = {
The exact particularities of this API are beyond the scope of this readme. Instead, this document opts to
provide a general description of the methods of this API and their purpose.

### Isolator.createNetworkFromConsumer
This method receives a list of seeder component IDs and a consumer including them.
It returns a Network including the seeder components as well as any of their dependencies.

### Isolator.createNetworkFromScope
Like createNetworkFromConsumer but receives a Scope instead.

### Isolator.createNetwork
Like createNetworkFromConsumer and createNetworkFromScope, only this method receives a graph directly. This method is also used internally by the above two methods, but is provided in the API as a convenience.
### Isolator.isolateComponents
This method receives a list of components, create capsule for the components and write the components data into the capsule.
It returns a list of capsules.
6 changes: 2 additions & 4 deletions src/extensions/isolator/symlink-dependencies-to-capsules.ts
Expand Up @@ -11,8 +11,7 @@ import logger from '../../logger/logger';
export async function symlinkDependenciesToCapsules(capsules: Capsule[], capsuleList: CapsuleList) {
await Promise.all(
capsules.map((capsule) => {
// @ts-ignore
return symlinkComponent(capsule.component, capsuleList);
return symlinkComponent(capsule.component.state._consumer, capsuleList);
})
);
}
Expand All @@ -30,8 +29,7 @@ async function symlinkComponent(component: ConsumerComponent, capsuleList: Capsu
);
return null;
}
// @ts-ignore fix once the capsule has the correct component. change to devCapsule.component.state._consumer
const packageName = componentIdToPackageName(devCapsule.component as ConsumerComponent);
const packageName = componentIdToPackageName(devCapsule.component.state._consumer);
const devCapsulePath = devCapsule.wrkDir;
// @todo: this is a hack, the capsule should be the one responsible to symlink, this works only for FS capsules.
const dest = path.join(componentCapsule.wrkDir, 'node_modules', packageName);
Expand Down
3 changes: 2 additions & 1 deletion src/extensions/pkg/pack.ts
Expand Up @@ -70,7 +70,8 @@ export class Packer {
if (!bitId.hasScope()) {
throw new GeneralError(`unable to find "${componentId}" in the scope, make sure the component is tagged first`);
}
const network = await this.isolator.createNetworkFromConsumer([componentId], consumer);
if (!this.workspace) throw new Error('packUsingCapsule expect to have workspace');
const network = await this.workspace.createNetwork([componentId]);
const capsule = network.capsules.getCapsuleIgnoreVersion(new ComponentID(bitId));
if (!capsule) throw new Error(`capsule not found for ${componentId}`);
return this.runNpmPack(capsule.wrkDir);
Expand Down
3 changes: 1 addition & 2 deletions src/extensions/pkg/publish-dry-run.task.ts
Expand Up @@ -11,8 +11,7 @@ export class PublishDryRunTask implements BuildTask {
async execute(context: BuildContext): Promise<BuildResults> {
this.publisher.options.dryRun = true;
const capsules = context.capsuleGraph.capsules.getAllCapsules();
// @ts-ignore todo: fix once the component here is not ConsumerComponent, just change to c.component.config.extensions
const capsulesToPublish = capsules.filter((c) => this.publisher.shouldPublish(c.component.extensions));
const capsulesToPublish = capsules.filter((c) => this.publisher.shouldPublish(c.component.config.extensions));
const results = await this.publisher.publishMultipleCapsules(capsulesToPublish);
return {
components: results,
Expand Down
5 changes: 2 additions & 3 deletions src/extensions/pkg/publisher.ts
Expand Up @@ -68,8 +68,7 @@ export class Publisher {
if (err.stderr) this.logger.error(componentIdStr, err.stderr);
errors.push(`${errorMsg}\n${err.stderr}`);
}
// @ts-ignore fix once capsule has the right Component object
const id = new ComponentID(capsule.component.id as BitId);
const id = capsule.component.id;
return { id, data, errors };
}

Expand All @@ -80,7 +79,7 @@ export class Publisher {
return [];
}
const idsToPublish = await this.getIdsToPublish(componentIds);
const network = await this.isolator.createNetworkFromConsumer(idsToPublish, consumer);
const network = await this.workspace.createNetwork(idsToPublish);
return network.seedersCapsules;
}

Expand Down
@@ -1,9 +1,8 @@
import chalk from 'chalk';
import _ from 'lodash';
import { Command, CommandOptions } from '../cli';
import { IsolatorExtension } from './isolator.extension';
import { loadConsumer } from '../../consumer';
import CapsuleList from './capsule-list';
import { Workspace } from '.';
import CapsuleList from '../isolator/capsule-list';

type CreateOpts = {
baseDir?: string;
Expand All @@ -27,18 +26,16 @@ export class CapsuleCreateCmd implements Command {
['d', 'installPackages', 'install packages in capsule with npm'],
] as CommandOptions;

constructor(private isolator: IsolatorExtension) {}
constructor(private workspace: Workspace) {}

async create(
[componentIds]: [string[]],
{ baseDir, alwaysNew = false, id, installPackages = false }: CreateOpts
): Promise<CapsuleList> {
// @todo: why it is not an array?
if (componentIds && !Array.isArray(componentIds)) componentIds = [componentIds];
// TODO: remove this consumer loading from here. it shouldn't be here
const consumer = await loadConsumer();
const capsuleOptions = _.omitBy({ baseDir, installPackages, alwaysNew, name: id }, _.isNil);
const isolatedEnvironment = await this.isolator.createNetworkFromConsumer(componentIds, consumer, capsuleOptions);
const isolatedEnvironment = await this.workspace.createNetwork(componentIds, capsuleOptions);
const capsules = isolatedEnvironment.capsules;
return capsules;
}
Expand Down
@@ -1,9 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import React from 'react';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Color } from 'ink';
import chalk from 'chalk';
import { Command, CommandOptions } from '../cli';
import { IsolatorExtension, ListResults } from './isolator.extension';
import { IsolatorExtension, ListResults } from '../isolator/isolator.extension';
import { loadConsumerIfExist } from '../../consumer';

export class CapsuleListCmd implements Command {
Expand All @@ -26,16 +23,10 @@ export class CapsuleListCmd implements Command {
return results;
}

// TODO: remove this ts-ignore
// @ts-ignore
async render() {
async report() {
const list = await this.getList();
// TODO: improve output
return (
<Color green>
found {list.capsules.length} capsule(s) for workspace {list.workspace}
</Color>
);
return chalk.green(`found ${list.capsules.length} capsule(s) for workspace ${list.workspace}`);
}

async json() {
Expand Down
7 changes: 7 additions & 0 deletions src/extensions/workspace/workspace.provider.ts
Expand Up @@ -14,6 +14,8 @@ import getWorkspaceSchema from './workspace.graphql';
import InstallCmd from './install.cmd';
import { CLIExtension } from '../cli';
import EjectConfCmd from './eject-conf.cmd';
import { CapsuleListCmd } from './capsule-list.cmd';
import { CapsuleCreateCmd } from './capsule-create.cmd';

export type WorkspaceDeps = [
CLIExtension,
Expand Down Expand Up @@ -88,6 +90,11 @@ export default async function provideWorkspace(
cli.register(new InstallCmd(workspace));
cli.register(new EjectConfCmd(workspace));

const capsuleListCmd = new CapsuleListCmd(isolator);
const capsuleCreateCmd = new CapsuleCreateCmd(workspace);
cli.register(capsuleListCmd);
cli.register(capsuleCreateCmd);

return workspace;
}

Expand Down

0 comments on commit 6c882b6

Please sign in to comment.