From 7ea11b35f9864c76e9ba87ab7d7635e3a9b13aef Mon Sep 17 00:00:00 2001 From: Steven Prybylynskyi Date: Sun, 13 Oct 2024 22:42:02 +0200 Subject: [PATCH] refactor: switch from child_process to worker_threads - Remove unused import of `rewritePathsWithExposedFederatedModules` in `make-federated-types.ts` - Update `compileTypesAsync` function in `compileTypesAsync.ts` to accept a `loggerHint` parameter and use the `getLogger` function from `helpers` - Remove `compileWorker.js` file as it is no longer needed - Update `plugin.ts` to use the updated `compileTypesAsync` function and pass the `federationConfig` parameter --- package.json | 2 +- src/bin/make-federated-types.ts | 26 ++---------- src/compileTypes/compileTypes.ts | 6 +++ src/compileTypes/compileTypesAsync.ts | 57 +++++++++++++++++--------- src/compileTypes/compileTypesWorker.ts | 48 ++++++++++++++++++++++ src/compileTypes/compileWorker.ts | 5 --- src/plugin.ts | 34 ++++----------- 7 files changed, 106 insertions(+), 72 deletions(-) create mode 100644 src/compileTypes/compileTypesWorker.ts delete mode 100644 src/compileTypes/compileWorker.ts diff --git a/package.json b/package.json index 39235f0..3181231 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "build": "tsc", "watch": "tsc -w", "lint": "biome check --write --unsafe", - "lint:tsc": "tsc --noEmit", + "lint:ts": "tsc --noEmit", "test": "vitest run", "test:coverage": "vitest run --coverage", "test:watch": "vitest" diff --git a/src/bin/make-federated-types.ts b/src/bin/make-federated-types.ts index 70ca6b3..415d9b6 100644 --- a/src/bin/make-federated-types.ts +++ b/src/bin/make-federated-types.ts @@ -4,7 +4,6 @@ import path from 'node:path'; import parseArgs from 'minimist'; -import { rewritePathsWithExposedFederatedModules } from '../compileTypes'; import { compileTypesAsync } from '../compileTypes/compileTypesAsync'; import { DEFAULT_DIR_DIST, @@ -59,26 +58,9 @@ console.log(`Emitting types for ${exposedModules.length} exposed module(s)`); setLogger(console); compileTypesAsync({ - tsconfigPath, exposedModules, - outFile, + federationConfig, dirGlobalTypes, -}) - .then(({ isSuccess, typeDefinitions }) => { - if (!isSuccess) { - console.error('Failed to compile types'); - process.exit(1); - } - - console.log('Replacing paths with names of exposed federate modules in typings file:', outFile); - - 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); - }); + outFile, + tsconfigPath, +}); diff --git a/src/compileTypes/compileTypes.ts b/src/compileTypes/compileTypes.ts index 9a10b96..c3812e9 100644 --- a/src/compileTypes/compileTypes.ts +++ b/src/compileTypes/compileTypes.ts @@ -4,6 +4,7 @@ import ts from 'typescript'; import { getAllFilePaths, getLogger } from '../helpers'; +import type { FederationConfig } from '../models'; import { getTSConfigCompilerOptions, reportCompileDiagnostic } from './helpers'; export type CompileTypesParams = { @@ -11,6 +12,7 @@ export type CompileTypesParams = { exposedModules: string[]; outFile: string; dirGlobalTypes: string; + federationConfig: FederationConfig; }; export type CompileTypesResult = { @@ -63,6 +65,10 @@ export function compileTypes({ const { diagnostics, emitSkipped } = program.emit(); diagnostics.forEach(reportCompileDiagnostic); + if (emitSkipped) { + logger.log('[compileTypes]: TypeScript program emit skipped'); + } + return { isSuccess: !emitSkipped, typeDefinitions: fileContent, diff --git a/src/compileTypes/compileTypesAsync.ts b/src/compileTypes/compileTypesAsync.ts index edefd15..4c5671e 100644 --- a/src/compileTypes/compileTypesAsync.ts +++ b/src/compileTypes/compileTypesAsync.ts @@ -1,38 +1,57 @@ -import { type ChildProcess, fork } from 'node:child_process'; import path from 'node:path'; +import { Worker, parentPort } from 'node:worker_threads'; -import type { CompileTypesParams, CompileTypesResult } from './compileTypes'; +import { getLogger } from '../helpers'; +import type { CompileTypesParams } from './compileTypes'; +import type { CompileTypesWorkerMessage } from './compileTypesWorker'; -let currentWorker: ChildProcess | null = null; +let worker: Worker | null = null; + +export function compileTypesAsync(params: CompileTypesParams, loggerHint = ''): Promise { + const logger = getLogger(); -export function compileTypesAsync(params: CompileTypesParams): Promise { return new Promise((resolve, reject) => { - if (currentWorker) { - currentWorker.kill(); + if (worker) { + logger.log('Terminating existing worker process'); + worker.terminate(); } const workerPath = path.join(__dirname, 'compileWorker.js'); - currentWorker = fork(workerPath); - - currentWorker.on('message', (result: CompileTypesResult) => { - resolve(result); - currentWorker?.kill(); - currentWorker = null; + worker = new Worker(workerPath); + + worker.on('message', (result: CompileTypesWorkerMessage) => { + switch (result.status) { + case 'success': + resolve(); + break; + case 'failure': + logger.warn('[Worker]: Failed to compile types for exposed modules.', loggerHint); + reject(new Error('Failed to compile types for exposed modules.')); + break; + case 'error': + logger.warn('[Worker]: Error compiling types for exposed modules.', loggerHint); + reject(result.error); + break; + } + worker?.terminate(); + worker = null; }); - currentWorker.on('error', error => { + worker.on('error', error => { + logger.warn('[Worker]: Unexpected error.', loggerHint); + logger.log(error); reject(error); - currentWorker?.kill(); - currentWorker = null; + worker?.terminate(); + worker = null; }); - currentWorker.on('exit', code => { + worker.on('exit', code => { if (code !== 0 && code !== null) { - reject(new Error(`Worker process exited with code ${code}`)); + reject(new Error(`[Worker]: Process exited with code ${code}`)); } - currentWorker = null; + worker = null; }); - currentWorker.send(params); + parentPort?.postMessage({ ...params, logger }); }); } diff --git a/src/compileTypes/compileTypesWorker.ts b/src/compileTypes/compileTypesWorker.ts new file mode 100644 index 0000000..a53065f --- /dev/null +++ b/src/compileTypes/compileTypesWorker.ts @@ -0,0 +1,48 @@ +import { parentPort } from 'node:worker_threads'; + +import type { Compilation } from 'webpack'; +import { type CompileTypesParams, compileTypes } from './compileTypes'; +import { rewritePathsWithExposedFederatedModules } from './rewritePathsWithExposedFederatedModules'; + +type CompileTypesWorkerMessageError = { + status: 'error'; + error: Error; +}; + +export type CompileTypesWorkerMessage = + | { status: 'success' } + | { status: 'failure' } + | CompileTypesWorkerMessageError; + +parentPort?.on('message', (message: CompileTypesParams & { logger: Compilation['logger'] }) => { + const { logger, ...params } = message; + + try { + const startTime = performance.now(); + const { isSuccess, typeDefinitions } = compileTypes(params); + + if (isSuccess) { + const endTime = performance.now(); + const timeTakenInSeconds = (endTime - startTime) / 1000; + logger.log(`Types compilation completed in ${timeTakenInSeconds.toFixed(2)} seconds`); + + logger.log( + `Replacing paths with names of exposed federate modules in typings file: ${params.outFile}`, + ); + rewritePathsWithExposedFederatedModules( + params.federationConfig, + params.outFile, + typeDefinitions, + ); + + parentPort?.postMessage({ status: 'success' } satisfies CompileTypesWorkerMessage); + } else { + parentPort?.postMessage({ status: 'failure' } satisfies CompileTypesWorkerMessage); + } + } catch (error) { + parentPort?.postMessage({ + status: 'error', + error: error as Error, + } satisfies CompileTypesWorkerMessageError); + } +}); diff --git a/src/compileTypes/compileWorker.ts b/src/compileTypes/compileWorker.ts deleted file mode 100644 index 1f4b273..0000000 --- a/src/compileTypes/compileWorker.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { type CompileTypesParams, compileTypes } from './compileTypes'; - -process.on('message', (message: CompileTypesParams) => { - process.send?.(compileTypes(message)); -}); diff --git a/src/plugin.ts b/src/plugin.ts index 570c490..159465b 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import type { Compiler, WebpackPluginInstance } from 'webpack'; -import { compileTypesAsync, rewritePathsWithExposedFederatedModules } from './compileTypes'; +import { compileTypesAsync } from './compileTypes'; import { DEFAULT_DIR_DIST, DEFAULT_DIR_DOWNLOADED_TYPES, @@ -95,32 +95,16 @@ export class ModuleFederationTypesPlugin implements WebpackPluginInstance { // Create types for exposed modules const compileTypesAfterEmit = async () => { - try { - const startTime = performance.now(); - - const { isSuccess, typeDefinitions } = await compileTypesAsync({ + 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); - } + federationConfig: federationPluginOptions as FederationConfig, + }, + getLoggerHint(compiler), + ); }; // Import types from remote modules @@ -178,11 +162,11 @@ export class ModuleFederationTypesPlugin implements WebpackPluginInstance { if (exposes && !isCompilationDisabled) { compiler.hooks.afterEmit.tap(PLUGIN_NAME, () => { if (shouldSyncContinuously) { - logger.log('Compiling types on afterEmit event'); + logger.log('Asynchronously compiling types on afterEmit event'); compileTypesContinuouslyAfterEmit(); } else if (!isCompiledOnce) { - isCompiledOnce = true; logger.log('Compile types on startup only'); + isCompiledOnce = true; compileTypesAfterEmit(); } });