Skip to content

Commit

Permalink
feat!: NGU-2227 async compilation
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
- Added worker to handle compilation
  • Loading branch information
steven-pribilinskiy committed Oct 14, 2024
1 parent fab0d77 commit 9e8f204
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 38 deletions.
33 changes: 20 additions & 13 deletions src/bin/make-federated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ import parseArgs from 'minimist';
import {
DEFAULT_DIR_DIST, DEFAULT_DIR_EMITTED_TYPES, DEFAULT_DIR_GLOBAL_TYPES, TS_CONFIG_FILE,
} from '../constants';
import {
compileTypes, rewritePathsWithExposedFederatedModules,
} from '../compileTypes';
import { rewritePathsWithExposedFederatedModules } from '../compileTypes';
import { setLogger } from '../helpers';
import { FederationConfig } from '../models';
import { compileTypesAsync } from '../compileTypes/compileTypesAsync';

import {
assertRunningFromRoot, getFederationConfig, getOptionsFromWebpackConfig, getWebpackConfigPathFromArgs,
Expand Down Expand Up @@ -44,27 +43,35 @@ if (argv['federation-config']) {
federationConfig = getOptionsFromWebpackConfig(webpackConfigPath).mfPluginOptions as FederationConfig;
}

const compileFiles = Object.values(federationConfig.exposes);
const exposedModules = Object.values(federationConfig.exposes);
const outDir = argv['output-types-folder'] || path.join(DEFAULT_DIR_DIST, DEFAULT_DIR_EMITTED_TYPES);
const outFile = path.join(outDir, 'index.d.ts');
const dirGlobalTypes = argv['global-types'] || DEFAULT_DIR_GLOBAL_TYPES;
const tsconfigPath = argv.tsconfig || TS_CONFIG_FILE;

console.log(`Emitting types for ${compileFiles.length} exposed module(s)`);
console.log(`Emitting types for ${exposedModules.length} exposed module(s)`);

setLogger(console);

const { isSuccess, typeDefinitions } = compileTypes(
compileTypesAsync({
tsconfigPath,
compileFiles,
exposedModules,
outFile,
dirGlobalTypes,
);
})
.then(({ isSuccess, typeDefinitions }) => {
if (!isSuccess) {
console.error('Failed to compile types');
process.exit(1);
}

if (!isSuccess) {
process.exit(1);
}
console.log('Replacing paths with names of exposed federate modules in typings file:', outFile);

console.log('Replacing paths with names of exposed federate modules in typings file:', outFile);
rewritePathsWithExposedFederatedModules(federationConfig, outFile, typeDefinitions);

rewritePathsWithExposedFederatedModules(federationConfig, outFile, typeDefinitions);
console.log(`Asynchronous types compilation completed successfully in ${process.uptime()} seconds`);
})
.catch(error => {
console.error('Error during type compilation:', error);
process.exit(1);
});
16 changes: 12 additions & 4 deletions src/compileTypes/compileTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import fs from 'fs';

import ts from 'typescript';

import { CompileTypesResult } from '../models';
import {
getAllFilePaths, getLogger,
} from '../helpers';
Expand All @@ -11,15 +10,24 @@ import {
getTSConfigCompilerOptions, reportCompileDiagnostic,
} from './helpers';

export function compileTypes(
export type CompileTypesParams = {
tsconfigPath: string,
exposedComponents: string[],
exposedModules: string[],
outFile: string,
dirGlobalTypes: string,
};

export type CompileTypesResult = {
isSuccess: boolean,
typeDefinitions: string,
};

export function compileTypes(
{ tsconfigPath, exposedModules, outFile, dirGlobalTypes }: CompileTypesParams,
): CompileTypesResult {
const logger = getLogger();

const exposedFileNames = Object.values(exposedComponents);
const exposedFileNames = Object.values(exposedModules);
const { moduleResolution, ...compilerOptions } = getTSConfigCompilerOptions(tsconfigPath);

Object.assign(compilerOptions, {
Expand Down
42 changes: 42 additions & 0 deletions src/compileTypes/compileTypesAsync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {
fork, ChildProcess,
} from 'child_process';
import path from 'path';

import {
CompileTypesParams, CompileTypesResult,
} from './compileTypes';

let currentWorker: ChildProcess | null = null;

export function compileTypesAsync(params: CompileTypesParams): Promise<CompileTypesResult> {
return new Promise((resolve, reject) => {
if (currentWorker) {
currentWorker.kill();
}

const workerPath = path.join(__dirname, 'compileWorker.js');
currentWorker = fork(workerPath);

currentWorker.on('message', (result: CompileTypesResult) => {
resolve(result);
currentWorker?.kill();
currentWorker = null;
});

currentWorker.on('error', error => {
reject(error);
currentWorker?.kill();
currentWorker = null;
});

currentWorker.on('exit', code => {
if (code !== 0 && code !== null) {
reject(new Error(`Worker process exited with code ${code}`));
}
currentWorker = null;
});

currentWorker.send(params);
});
}
7 changes: 7 additions & 0 deletions src/compileTypes/compileWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {
compileTypes, CompileTypesParams,
} from './compileTypes';

process.on('message', (message: CompileTypesParams) => {
process.send?.(compileTypes(message));
});
2 changes: 1 addition & 1 deletion src/compileTypes/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './compileTypes';
export * from './compileTypesAsync';
export * from './rewritePathsWithExposedFederatedModules';
4 changes: 0 additions & 4 deletions src/models/CompileTypesResult.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './CompileTypesResult';
export * from './FederationConfig';
export * from './ModuleFederationPluginOptions';
export * from './ModuleFederationTypesPluginOptions';
Expand Down
46 changes: 31 additions & 15 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import path from 'path';
import {
Compiler, WebpackPluginInstance,
} from 'webpack';
import {
Configuration as DevServerConfiguration, Static,
} from 'webpack-dev-server';

import {
DEFAULT_DIR_DIST,
Expand All @@ -13,7 +16,7 @@ import {
TS_CONFIG_FILE,
} from './constants';
import {
compileTypes, rewritePathsWithExposedFederatedModules,
rewritePathsWithExposedFederatedModules, compileTypesAsync,
} from './compileTypes';
import {
downloadTypes, getRemoteManifestUrls,
Expand Down Expand Up @@ -85,28 +88,41 @@ export class ModuleFederationTypesPlugin implements WebpackPluginInstance {
// Define path for the emitted typings file
const { exposes, remotes } = federationPluginOptions;

const dirDist = compiler.options.devServer?.static?.directory
const dirDist = ((compiler.options.devServer as DevServerConfiguration)?.static as Static)?.directory
|| compiler.options.output?.path
|| DEFAULT_DIR_DIST;
const dirEmittedTypes = this.options?.dirEmittedTypes || DEFAULT_DIR_EMITTED_TYPES;
const dirGlobalTypes = this.options?.dirGlobalTypes || DEFAULT_DIR_GLOBAL_TYPES;
const dirDownloadedTypes = this.options?.dirDownloadedTypes || DEFAULT_DIR_DOWNLOADED_TYPES;
const tsconfig = TS_CONFIG_FILE;
const outFile = path.join(dirDist, dirEmittedTypes, 'index.d.ts');

// Create types for exposed modules
const compileTypesAfterEmit = () => {
const { isSuccess, typeDefinitions } = compileTypes(
tsconfig,
exposes as string[],
outFile,
dirGlobalTypes,
);

if (isSuccess) {
rewritePathsWithExposedFederatedModules(federationPluginOptions as FederationConfig, outFile, typeDefinitions);
} else {
logger.warn('Failed to compile types for exposed modules.', getLoggerHint(compiler));
const compileTypesAfterEmit = async () => {
try {
const startTime = performance.now();

const { isSuccess, typeDefinitions } = await compileTypesAsync({
tsconfigPath: TS_CONFIG_FILE,
exposedModules: exposes as string[],
outFile,
dirGlobalTypes,
});

if (isSuccess) {
const endTime = performance.now();
const timeTakenInSeconds = (endTime - startTime) / 1000;
logger.log(`Types compilation completed in ${timeTakenInSeconds.toFixed(2)} seconds`);

rewritePathsWithExposedFederatedModules(
federationPluginOptions as FederationConfig,
outFile,
typeDefinitions,
);
} else {
logger.warn('Failed to compile types for exposed modules.', getLoggerHint(compiler));
}
} catch (error) {
logger.error('Error compiling types asynchronously:', error);
}
};

Expand Down

0 comments on commit 9e8f204

Please sign in to comment.