Skip to content

Commit

Permalink
first working version of component level bundling
Browse files Browse the repository at this point in the history
  • Loading branch information
GiladShoham committed May 25, 2021
1 parent eb8b917 commit 95ac060
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 117 deletions.
10 changes: 10 additions & 0 deletions scopes/compilation/bundler/dev-server-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ export type Target = {
* output path of the target
*/
outputPath: string;

/**
* module federation namespace name
*/
mfName?: string;

/**
* module federation exposed module
*/
mfExposes?: Record<string, string>;
};

export interface BundlerContext extends BuildContext {
Expand Down
15 changes: 15 additions & 0 deletions scopes/compilation/webpack/webpack.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { WebpackDevServer } from './webpack.dev-server';

export type WebpackConfigTransformContext = {
mode: BundlerMode;
target?: Target;
};
export type WebpackConfigTransformer = (
config: WebpackConfigMutator,
Expand Down Expand Up @@ -93,6 +94,20 @@ export class WebpackMain {
return new WebpackBundler(context.targets, mutatedConfigs, this.logger);
}

createComponentsBundler(context: BundlerContext, transformers: WebpackConfigTransformer[] = []) {
const mutatedConfigs = context.targets.map((target) => {
const baseConfig = previewConfigFactory(target.entries, target.outputPath);
const transformerContext: WebpackConfigTransformContext = {
mode: 'prod',
target,
};
const configMutator = new WebpackConfigMutator(baseConfig);
const afterMutation = runTransformersWithContext(configMutator.clone(), transformers, transformerContext);
return afterMutation.raw;
});
return new WebpackBundler(context.targets, mutatedConfigs, this.logger);
}

private createPreviewConfig(targets: Target[]) {
return targets.map((target) => {
return previewConfigFactory(target.entries, target.outputPath);
Expand Down
1 change: 1 addition & 0 deletions scopes/preview/preview/preview.main.runtime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export class PreviewMain {
private execContexts = new Map<string, ExecutionContext>();
private componentsByAspect = new Map<string, RuntimeComponents>();

// TODO: consolidate code duplication with the env-strategy computePaths logic
private async getPreviewTarget(
/** execution context (of the specific env) */
context: ExecutionContext
Expand Down
47 changes: 2 additions & 45 deletions scopes/preview/preview/preview.task.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import { resolve, join } from 'path';
import { resolve } from 'path';
import { ExecutionContext } from '@teambit/envs';
import { BuildContext, BuiltTaskResult, BuildTask, TaskLocation } from '@teambit/builder';
import { Bundler, BundlerContext, BundlerMain, Target } from '@teambit/bundler';
import { Compiler } from '@teambit/compiler';
import { ComponentMap } from '@teambit/component';
import { Capsule } from '@teambit/isolator';
import { AbstractVinyl } from '@teambit/legacy/dist/consumer/component/sources';
import { flatten } from 'lodash';
import { PreviewDefinition } from './preview-definition';
import { PreviewMain } from './preview.main.runtime';

export class PreviewTask implements BuildTask {
Expand Down Expand Up @@ -41,51 +35,14 @@ export class PreviewTask implements BuildTask {
rootPath: url,
});

const bundler: Bundler = await context.env.getBundler(bundlerContext);
const bundler: Bundler = await context.env.getComponentBundler(bundlerContext);
const bundlerResults = await bundler.run();

return bundlingStrategy.computeResults(bundlerContext, bundlerResults, this);
}

async computePaths(capsule: Capsule, defs: PreviewDefinition[], context: BuildContext): Promise<string[]> {
const previewMain = await this.preview.writePreviewRuntime();

const moduleMapsPromise = defs.map(async (previewDef) => {
const moduleMap = await previewDef.getModuleMap([capsule.component]);
const paths = this.getPathsFromMap(capsule, moduleMap, context);
const template = previewDef.renderTemplatePath ? await previewDef.renderTemplatePath(context) : 'undefined';

const link = this.preview.writeLink(
previewDef.prefix,
paths,
previewDef.renderTemplatePath ? await previewDef.renderTemplatePath(context) : undefined,
capsule.path
);

const files = flatten(paths.toArray().map(([, file]) => file)).concat([link]);

if (template) return files.concat([template]);
return files;
});

const moduleMaps = await Promise.all(moduleMapsPromise);

return flatten(moduleMaps.concat([previewMain]));
}

getPreviewDirectory(context: ExecutionContext) {
const outputPath = resolve(`${context.id}/public`);
return outputPath;
}

getPathsFromMap(
capsule: Capsule,
moduleMap: ComponentMap<AbstractVinyl[]>,
context: BuildContext
): ComponentMap<string[]> {
const compiler: Compiler = context.env.getCompiler(context);
return moduleMap.map((files) => {
return files.map((file) => join(capsule.path, compiler.getDistPathBySrcPath(file.relative)));
});
}
}
86 changes: 82 additions & 4 deletions scopes/preview/preview/strategies/component-strategy.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,75 @@
import { Compiler } from '@teambit/compiler';
import { ComponentMap } from '@teambit/component';
import { Capsule } from '@teambit/isolator';
import { AbstractVinyl } from '@teambit/legacy/dist/consumer/component/sources';
import { join } from 'path';
import { BuildContext } from '@teambit/builder';
import { Target, BundlerResult, BundlerContext } from '@teambit/bundler';
import { camelCase } from 'lodash';
import fs from 'fs-extra';
import { BundlingStrategy } from '../bundling-strategy';
import { PreviewDefinition } from '../preview-definition';
import { PreviewTask } from '../preview.task';

export class ComponentBundlingStrategy implements BundlingStrategy {
name = 'component';

computeTargets(context: BuildContext, previewDefs: PreviewDefinition[], previewTask: PreviewTask): Promise<Target[]> {
computeTargets(context: BuildContext, previewDefs: PreviewDefinition[]): Promise<Target[]> {
return Promise.all(
context.capsuleNetwork.graphCapsules.map(async (capsule) => {
context.capsuleNetwork.seedersCapsules.map(async (capsule) => {
const component = capsule.component;
const entry = await this.writeEmptyEntryFile(capsule);
const exposes = await this.computeExposes(capsule, previewDefs, context);
return {
entries: await previewTask.computePaths(capsule, previewDefs, context),
components: [capsule.component],
entries: [entry],
mfName: normalizeMfName(component.id.fullName),
mfExposes: exposes,
components: [component],
outputPath: capsule.path,
};
})
);
}

async computeExposes(
capsule: Capsule,
defs: PreviewDefinition[],
context: BuildContext
): Promise<Record<string, string>> {
const compIdCamel = normalizeMfName(capsule.component.id.fullName);
const compiler: Compiler = context.env.getCompiler(context);
const mainFile = capsule.component.state._consumer.mainFile;
const mainFilePath = join(capsule.path, compiler.getDistPathBySrcPath(mainFile));
const exposes = {
[`./${compIdCamel}`]: mainFilePath,
};

const moduleMapsPromise = defs.map(async (previewDef) => {
const moduleMap = await previewDef.getModuleMap([capsule.component]);
const paths = this.getPathsFromMap(capsule, moduleMap, context);
paths.toArray().map(([, files], index) => {
files.map((filePath) => {
Object.assign(exposes, {
[`./${compIdCamel}_${previewDef.prefix}_${index}`]: filePath,
});
return undefined;
});
return undefined;
});
});

await Promise.all(moduleMapsPromise);
return exposes;
}

async writeEmptyEntryFile(capsule: Capsule): Promise<string> {
const tempFolder = join(capsule.path, '__temp');
await fs.ensureDir(tempFolder);
const filePath = join(tempFolder, 'emptyFile.js');
await fs.writeFile(filePath, '');
return filePath;
}

async computeResults(context: BundlerContext, results: BundlerResult[], previewTask: PreviewTask) {
return {
componentsResults: results.map((result) => {
Expand All @@ -31,4 +82,31 @@ export class ComponentBundlingStrategy implements BundlingStrategy {
artifacts: [{ name: 'preview', globPatterns: [previewTask.getPreviewDirectory(context)] }],
};
}

getPathsFromMap(
capsule: Capsule,
moduleMap: ComponentMap<AbstractVinyl[]>,
context: BuildContext
): ComponentMap<string[]> {
const compiler: Compiler = context.env.getCompiler(context);
return moduleMap.map((files) => {
return files.map((file) => join(capsule.path, compiler.getDistPathBySrcPath(file.relative)));
});
}
}

// link-file.js
// new webpack.container.ModuleFederationPlugin({
// exposes: {
// // TODO: take the dist file programmatically
// [`./${buttonId}`]: '/Users/giladshoham/Library/Caches/Bit/capsules/d3522af33785e04e8b1199864b9f46951ea3c008/my-scope_ui_button/dist/button.js',
// [`./${buttonId}_composition_1`]: '/Users/giladshoham/Library/Caches/Bit/capsules/d3522af33785e04e8b1199864b9f46951ea3c008/my-scope_ui_button/dist/button.composition.js',
// [`./${buttonId}_docs`]: '/Users/giladshoham/Library/Caches/Bit/capsules/d3522af33785e04e8b1199864b9f46951ea3c008/my-scope_ui_button/dist/button.docs.js',
// },
// defaultEposes: './index'
// import ('uiButton')
// }),

function normalizeMfName(componentId: string): string {
return camelCase(componentId);
}
6 changes: 4 additions & 2 deletions scopes/preview/preview/strategies/env-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ export class EnvBundlingStrategy implements BundlingStrategy {

async computeTargets(context: BuildContext, previewDefs: PreviewDefinition[]) {
const outputPath = this.getOutputPath(context);
console.log(outputPath);
console.log('computeTargets');
console.log('outputPath', outputPath);
if (!existsSync(outputPath)) mkdirpSync(outputPath);
const entries = await this.computePaths(outputPath, previewDefs, context);

return [
{
entries: await this.computePaths(outputPath, previewDefs, context),
entries,
components: context.components,
outputPath,
},
Expand Down
28 changes: 24 additions & 4 deletions scopes/react/react/react.env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import { outputFileSync } from 'fs-extra';
import { Configuration } from 'webpack';
import { ReactMainConfig } from './react.main.runtime';
import devPreviewConfigFactory from './webpack/webpack.config.preview.dev';
import previewConfigFactory from './webpack/webpack.config.preview';
import basePreviewConfigFactory from './webpack/webpack.config.base.preview';
import envPreviewConfigFactory from './webpack/webpack.config.env.preview';
import componentPreviewConfigFactory from './webpack/webpack.config.component.preview';
import { eslintConfig } from './eslint/eslintrc';
import { ReactAspect } from './react.aspect';

Expand Down Expand Up @@ -185,16 +187,34 @@ export class ReactEnv implements Environment {
return this.webpack.createDevServer(context, [defaultTransformer, ...transformers]);
}

async getBundler(context: BundlerContext, transformers: WebpackConfigTransformer[] = []): Promise<Bundler> {
const path = this.writeFileMap(context.components);
const defaultConfig = previewConfigFactory(path);
async getEnvBundler(context: BundlerContext, transformers: WebpackConfigTransformer[] = []): Promise<Bundler> {
const defaultConfig = envPreviewConfigFactory();
const defaultTransformer: WebpackConfigTransformer = (configMutator) => {
return configMutator.merge([defaultConfig]);
};

return this.webpack.createBundler(context, [defaultTransformer, ...transformers]);
}

async getComponentBundler(context: BundlerContext, transformers: WebpackConfigTransformer[] = []): Promise<Bundler> {
const fileMapPath = this.writeFileMap(context.components);
const baseConfig = basePreviewConfigFactory();
const defaultTransformer: WebpackConfigTransformer = (configMutator, mutatorContext) => {
if (!mutatorContext.target?.mfName) {
throw new Error(`missing module federation name for ${mutatorContext.target?.components[0].id.toString()}`);
}
const componentConfig = componentPreviewConfigFactory(
mutatorContext.target?.mfName,
mutatorContext.target?.mfExposes,
fileMapPath
);

return configMutator.merge([baseConfig, componentConfig]);
};

return this.webpack.createComponentsBundler(context, [defaultTransformer, ...transformers]);
}

private getEntriesFromWebpackConfig(config?: Configuration): string[] {
if (!config || !config.entry) {
return [];
Expand Down
Loading

0 comments on commit 95ac060

Please sign in to comment.