From 25e11a0ba6faff2dc426e4eba7a0156b69b1f4b4 Mon Sep 17 00:00:00 2001 From: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com> Date: Sat, 10 Aug 2024 23:19:19 +0200 Subject: [PATCH 1/8] Add worker --- src/stages/scan-files.ts | 160 +++++++++++++------------------- src/stages/workers/scan-file.ts | 123 ++++++++++++++++++++++++ 2 files changed, 187 insertions(+), 96 deletions(-) create mode 100644 src/stages/workers/scan-file.ts diff --git a/src/stages/scan-files.ts b/src/stages/scan-files.ts index bd619b8..62910d7 100644 --- a/src/stages/scan-files.ts +++ b/src/stages/scan-files.ts @@ -1,112 +1,80 @@ import * as modReplacements from 'module-replacements'; -import {ts as sg} from '@ast-grep/napi'; -import {readFile} from 'node:fs/promises'; -import dedent from 'dedent'; -import pc from 'picocolors'; import * as cl from '@clack/prompts'; import {type FileReplacement} from '../shared-types.js'; -import {suggestReplacement} from '../suggest-replacement.js'; +import {cpuUsage} from 'node:process'; +import {cpus, availableParallelism} from 'node:os'; +import {Worker} from 'node:worker_threads'; +import {fork} from 'node:child_process'; +import Events from 'node:events'; +const __dirname = import.meta.dirname; -async function scanFile( - filePath: string, - contents: string, - lines: string[], - replacements: modReplacements.ModuleReplacement[] -): Promise { - const ast = sg.parse(contents); - const root = ast.root(); - const matches: modReplacements.ModuleReplacement[] = []; - - for (const replacement of replacements) { - const imports = root.findAll({ - rule: { - any: [ - { - pattern: { - context: `import $NAME from '${replacement.moduleName}'`, - strictness: 'relaxed' - } - }, - { - pattern: { - context: `require('${replacement.moduleName}')`, - strictness: 'relaxed' - } - } - ] - } - }); - - if (imports.length > 0) { - matches.push(replacement); - } - - for (const node of imports) { - const range = node.range(); - let snippet: string = ''; - - const prevLine = lines[range.start.line - 1]; - const line = lines[range.start.line]; - const nextLine = lines[range.start.line + 1]; - - if (prevLine) { - snippet += `${range.start.line} | ${prevLine}\n`; - } - - snippet += `${range.start.line + 1} | ${pc.red(line)}\n`; +const events = new Events(); - if (nextLine) { - snippet += `${range.start.line + 2} | ${nextLine}\n`; - } +let tasks = 0; - suggestReplacement(replacement, { - type: 'file', - path: filePath, - line: range.start.line, - column: range.start.column, - snippet - }); +export async function _scanFile( + file: string, + replacements: modReplacements.ModuleReplacement[] +): Promise { + // return new Promise((resolve, reject) => { + const worker = fork(`${__dirname}/workers/scan-file.js`); + worker.on('error', (error) => reject(error)); + worker.once('message', (message) => { + if (message?.type === 'result') { + // resolve(message.value); + events.emit('file-scan-worker-result', message.value); + } else { + console.error(message.value); + + // reject(message.value); } - } - - if (matches.length === 0) { - return null; - } - - return { - path: filePath, - contents, - replacements: matches - }; + events.emit('file-scan-worker-done'); + worker.kill(); + }); + worker.send({file, replacements}); + // }); } -export async function scanFiles( +export function scanFiles( files: string[], replacements: modReplacements.ModuleReplacement[], - spinner: ReturnType + spinner: ReturnType, + results: FileReplacement[] ): Promise { - const results: FileReplacement[] = []; - - for (const file of files) { - try { - const contents = await readFile(file, 'utf8'); - const lines = contents.split('\n'); - - spinner.message(`Scanning ${file}`); - - const scanResult = await scanFile(file, contents, lines, replacements); + return new Promise((resolve, reject) => { + if (!results) results = []; + let i = 0; + const filesLength = files.length; + + const runJob = () => { + const available = availableParallelism(); + + let maxThreads; + if (!tasks) maxThreads = available; + else maxThreads = available - tasks; + + tasks = tasks + maxThreads; + const targets = files.splice( + 0, + files.length < maxThreads ? files.length : maxThreads + ); + + spinner.message(`Scanning files: ${targets.join(', ')}`); + targets.forEach((file) => _scanFile(file, replacements)); + }; + events.on('file-scan-worker-done', () => { + if (files.length > 0) runJob(); + tasks -= 1; + }); - if (scanResult) { - results.push(scanResult); + events.on('file-scan-worker-result', (result) => { + results.push(result); + i += 1; + if (i === filesLength) { + resolve(results); } - } catch (err) { - cl.log.error(dedent` - Could not read file ${file}: - - ${String(err)} - `); - } - } + }); - return results; + runJob(); + }); } diff --git a/src/stages/workers/scan-file.ts b/src/stages/workers/scan-file.ts new file mode 100644 index 0000000..26ce070 --- /dev/null +++ b/src/stages/workers/scan-file.ts @@ -0,0 +1,123 @@ +import * as modReplacements from 'module-replacements'; +import {ts as sg} from '@ast-grep/napi'; +import {readFile} from 'node:fs/promises'; +import dedent from 'dedent'; +import pc from 'picocolors'; +import {type FileReplacement} from './../../shared-types.js'; +import {suggestReplacement} from './../../suggest-replacement.js'; + +async function scanFile( + filePath: string, + contents: string, + lines: string[], + replacements: modReplacements.ModuleReplacement[] +): Promise { + const ast = sg.parse(contents); + const root = ast.root(); + const matches: modReplacements.ModuleReplacement[] = []; + + for (const replacement of replacements) { + const imports = root.findAll({ + rule: { + any: [ + { + pattern: { + context: `import $NAME from '${replacement.moduleName}'`, + strictness: 'relaxed' + } + }, + { + pattern: { + context: `require('${replacement.moduleName}')`, + strictness: 'relaxed' + } + } + ] + } + }); + + if (imports.length > 0) { + matches.push(replacement); + } + + for (const node of imports) { + const range = node.range(); + let snippet: string = ''; + + const prevLine = lines[range.start.line - 1]; + const line = lines[range.start.line]; + const nextLine = lines[range.start.line + 1]; + + if (prevLine) { + snippet += `${range.start.line} | ${prevLine}\n`; + } + + snippet += `${range.start.line + 1} | ${pc.red(line)}\n`; + + if (nextLine) { + snippet += `${range.start.line + 2} | ${nextLine}\n`; + } + + suggestReplacement(replacement, { + type: 'file', + path: filePath, + line: range.start.line, + column: range.start.column, + snippet + }); + } + } + + if (matches.length === 0) { + return null; + } + + return { + path: filePath, + contents, + replacements: matches + }; +} + +async function scanTask( + file: string, + replacements: modReplacements.ModuleReplacement[] +) { + try { + const contents = await readFile(file, 'utf8'); + const lines = contents.split('\n'); + + const scanResult = await scanFile(file, contents, lines, replacements); + + // if (scanResult) { + // @ts-ignore + process.send({type: 'result', value: scanResult}); + // } else { + // process.exit(0); + // } + } catch (err) { + process.send({ + type: 'error', + value: dedent` + Could not read file ${file}: + + ${String(err)} + ` + }); + // cl.log.error(dedent` + // Could not read file ${file}: + + // ${String(err)} + // `); + } +} + +process.on( + 'message', + (message: { + file: string; + replacements: modReplacements.ModuleReplacement[]; + }) => { + scanTask(message.file, message.replacements); + } +); From 4135793cfa8f738271600694af83a1a34c2c15e6 Mon Sep 17 00:00:00 2001 From: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com> Date: Sun, 11 Aug 2024 16:51:16 +0200 Subject: [PATCH 2/8] scan-file: switch to real worker and cleanup --- src/stages/scan-files.ts | 104 ++++++++++++-------------------- src/stages/workers/scan-file.ts | 18 ++---- 2 files changed, 43 insertions(+), 79 deletions(-) diff --git a/src/stages/scan-files.ts b/src/stages/scan-files.ts index 62910d7..f3d2a94 100644 --- a/src/stages/scan-files.ts +++ b/src/stages/scan-files.ts @@ -1,80 +1,54 @@ import * as modReplacements from 'module-replacements'; import * as cl from '@clack/prompts'; import {type FileReplacement} from '../shared-types.js'; -import {cpuUsage} from 'node:process'; -import {cpus, availableParallelism} from 'node:os'; +import {availableParallelism} from 'node:os'; import {Worker} from 'node:worker_threads'; -import {fork} from 'node:child_process'; -import Events from 'node:events'; -const __dirname = import.meta.dirname; +import path from 'path'; +import {fileURLToPath} from 'url'; -const events = new Events(); - -let tasks = 0; - -export async function _scanFile( - file: string, - replacements: modReplacements.ModuleReplacement[] -): Promise { - // return new Promise((resolve, reject) => { - const worker = fork(`${__dirname}/workers/scan-file.js`); - worker.on('error', (error) => reject(error)); - worker.once('message', (message) => { - if (message?.type === 'result') { - // resolve(message.value); - events.emit('file-scan-worker-result', message.value); - } else { - console.error(message.value); - - // reject(message.value); - } - events.emit('file-scan-worker-done'); - worker.kill(); - }); - worker.send({file, replacements}); - // }); -} +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const available = availableParallelism(); export function scanFiles( files: string[], replacements: modReplacements.ModuleReplacement[], - spinner: ReturnType, - results: FileReplacement[] + spinner: ReturnType ): Promise { return new Promise((resolve, reject) => { - if (!results) results = []; let i = 0; + let tasks = 0; const filesLength = files.length; - - const runJob = () => { - const available = availableParallelism(); - - let maxThreads; - if (!tasks) maxThreads = available; - else maxThreads = available - tasks; - - tasks = tasks + maxThreads; - const targets = files.splice( - 0, - files.length < maxThreads ? files.length : maxThreads - ); - - spinner.message(`Scanning files: ${targets.join(', ')}`); - targets.forEach((file) => _scanFile(file, replacements)); - }; - events.on('file-scan-worker-done', () => { - if (files.length > 0) runJob(); - tasks -= 1; - }); - - events.on('file-scan-worker-result', (result) => { - results.push(result); - i += 1; - if (i === filesLength) { - resolve(results); - } - }); - - runJob(); + const results: FileReplacement[] = []; + + for (const file of files.splice(0, available)) { + const worker = new Worker(`${__dirname}/workers/scan-file.js`); + // todo, what todo with the errors? + worker.on('error', (error) => reject(error.message)); + worker.on('message', (message) => { + if (message?.type === 'result') { + results.push(message.value); + i += 1; + if (i === filesLength) { + resolve(results); + } + } else { + reject(message.value); + } + if (files.length > 0) { + if (available >= tasks) { + const file = files.shift(); + spinner.message(`Scanning file: ${file}`); + worker.postMessage({file, replacements}); + } else { + tasks -= 1; + } + } else { + worker.terminate(); + } + }); + spinner.message(`Scanning file: ${file}`); + worker.postMessage({file, replacements}); + } }); } diff --git a/src/stages/workers/scan-file.ts b/src/stages/workers/scan-file.ts index 26ce070..013a936 100644 --- a/src/stages/workers/scan-file.ts +++ b/src/stages/workers/scan-file.ts @@ -5,6 +5,7 @@ import dedent from 'dedent'; import pc from 'picocolors'; import {type FileReplacement} from './../../shared-types.js'; import {suggestReplacement} from './../../suggest-replacement.js'; +import {parentPort} from 'node:worker_threads'; async function scanFile( filePath: string, @@ -86,17 +87,11 @@ async function scanTask( try { const contents = await readFile(file, 'utf8'); const lines = contents.split('\n'); - const scanResult = await scanFile(file, contents, lines, replacements); - // if (scanResult) { - // @ts-ignore - process.send({type: 'result', value: scanResult}); - // } else { - // process.exit(0); - // } + parentPort?.postMessage({type: 'result', value: scanResult}); } catch (err) { - process.send({ + parentPort?.postMessage({ type: 'error', value: dedent` Could not read file ${file}: @@ -104,15 +99,10 @@ async function scanTask( ${String(err)} ` }); - // cl.log.error(dedent` - // Could not read file ${file}: - - // ${String(err)} - // `); } } -process.on( +parentPort?.on( 'message', (message: { file: string; From 88dab328660d2f3705f1bda113dd549d91160af0 Mon Sep 17 00:00:00 2001 From: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com> Date: Sun, 11 Aug 2024 16:53:19 +0200 Subject: [PATCH 3/8] main: try catch scan errors --- src/main.ts | 64 ++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/main.ts b/src/main.ts index 056def5..2e9c819 100644 --- a/src/main.ts +++ b/src/main.ts @@ -196,43 +196,47 @@ async function runModuleReplacements(): Promise { scanSpinner.message('Scanning files'); - const files = await traverseFiles(options.filesDir); + try { + const files = await traverseFiles(options.filesDir); - const scanFilesResult = await scanFiles( - files, - manifestReplacements, - scanSpinner - ); + const scanFilesResult = await scanFiles( + files, + manifestReplacements, + scanSpinner + ); - if (scanFilesResult.length > 0) { - dependenciesFound = true; - } + if (scanFilesResult.length > 0) { + dependenciesFound = true; + } - if (dependenciesFound) { - scanSpinner.stop('Replaceable modules found.', 2); - } else { - scanSpinner.stop('No replaceable modules found.'); - } + if (dependenciesFound) { + scanSpinner.stop('Replaceable modules found.', 2); + } else { + scanSpinner.stop('No replaceable modules found.'); + } - if ( - options.autoUninstall && - (dependenciesToRemove.length > 0 || devDependenciesToRemove.length > 0) - ) { - const npmSpinner = cl.spinner(); + if ( + options.autoUninstall && + (dependenciesToRemove.length > 0 || devDependenciesToRemove.length > 0) + ) { + const npmSpinner = cl.spinner(); - npmSpinner.start('Removing npm dependencies'); + npmSpinner.start('Removing npm dependencies'); - if (dependenciesToRemove.length > 0) { - await x('npm', ['rm', '-S', ...dependenciesToRemove]); - } - if (devDependenciesToRemove.length > 0) { - await x('npm', ['rm', '-D', ...devDependenciesToRemove]); - } + if (dependenciesToRemove.length > 0) { + await x('npm', ['rm', '-S', ...dependenciesToRemove]); + } + if (devDependenciesToRemove.length > 0) { + await x('npm', ['rm', '-D', ...devDependenciesToRemove]); + } - npmSpinner.stop('npm dependencies removed'); - } + npmSpinner.stop('npm dependencies removed'); + } - if (options.fix) { - await fixFiles(scanFilesResult); + if (options.fix) { + await fixFiles(scanFilesResult); + } + } catch (error) { + scanSpinner.stop(error as string, 1); } } From 8f31de4ad4a8ebe33400b56f7c85cad7b7156410 Mon Sep 17 00:00:00 2001 From: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com> Date: Sun, 11 Aug 2024 17:58:17 +0200 Subject: [PATCH 4/8] workers/scan-file: readFile -> open --- src/stages/workers/scan-file.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stages/workers/scan-file.ts b/src/stages/workers/scan-file.ts index 013a936..b9670dd 100644 --- a/src/stages/workers/scan-file.ts +++ b/src/stages/workers/scan-file.ts @@ -1,11 +1,11 @@ import * as modReplacements from 'module-replacements'; import {ts as sg} from '@ast-grep/napi'; -import {readFile} from 'node:fs/promises'; import dedent from 'dedent'; import pc from 'picocolors'; import {type FileReplacement} from './../../shared-types.js'; import {suggestReplacement} from './../../suggest-replacement.js'; import {parentPort} from 'node:worker_threads'; +import {open} from 'node:fs/promises'; async function scanFile( filePath: string, @@ -85,9 +85,11 @@ async function scanTask( replacements: modReplacements.ModuleReplacement[] ) { try { - const contents = await readFile(file, 'utf8'); + const fd = await open(file); + const contents = await fd.readFile({encoding: 'utf-8'}); const lines = contents.split('\n'); const scanResult = await scanFile(file, contents, lines, replacements); + await fd.close(); parentPort?.postMessage({type: 'result', value: scanResult}); } catch (err) { From 57bb3a0df36f012632b188e186a89177b405d1ac Mon Sep 17 00:00:00 2001 From: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com> Date: Sun, 11 Aug 2024 19:50:06 +0200 Subject: [PATCH 5/8] Add advanced flag & scanSpeed option --- README.md | 10 +++++++ src/main.ts | 60 +++++++++++++++++++++++++++++++++++++++- src/stages/scan-files.ts | 3 +- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de1e0c5..d259df6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,16 @@ The interactive CLI can guide you through the following steps: npx esperf ``` +## Advanced Usage + +The interactive CLI will show more steps: + +- Set scan speed + +```sh +npx esperf --advanced +``` + ## License MIT diff --git a/src/main.ts b/src/main.ts index 2e9c819..273c353 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,6 +8,28 @@ import {scanFiles} from './stages/scan-files.js'; import {fixFiles} from './stages/fix-files.js'; import {traverseFiles} from './stages/traverse-files.js'; import {scanDependencies} from './stages/scan-dependencies.js'; +import {availableParallelism} from 'node:os'; + +let advanced = false; + +for (let i = 0; i < process.argv.length; ++i) { + if (process.argv[i] === '--advanced') advanced = true; +} + +function getWantedThreads(scanSpeed: string): number { + const threads = availableParallelism(); + switch (scanSpeed) { + case 'slow': + return threads * 0.25; + case 'medium': + return threads * 0.5; + case 'fast': + return threads * 0.75; + case 'fastest': + return threads; + } + return 1; +} const availableManifests: Record = { native: modReplacements.nativeReplacements, @@ -118,7 +140,41 @@ async function runModuleReplacements(): Promise { cl.confirm({ message: 'Automatically uninstall packages?', initialValue: false - }) + }), + scanSpeed: () => + advanced + ? cl.select({ + message: 'Preferred scan speed', + options: [ + { + value: 'fastest', + label: 'Fastest', + hint: 'uses all the threads, pushes cpu to 100%' + }, + { + value: 'fast', + label: 'Fast', + hint: '75% of available threads' + }, + { + value: 'medium', + label: 'Medium', + hint: '50% of available threads' + }, + { + value: 'slow', + label: 'Slow', + hint: '25% of available threads' + }, + { + value: 'slowest', + label: 'Slowest', + hint: 'disables parallelism, 1 thread' + } + ], + initialValue: 'medium' + }) + : Promise.resolve('medium') }, { onCancel: () => { @@ -198,10 +254,12 @@ async function runModuleReplacements(): Promise { try { const files = await traverseFiles(options.filesDir); + const threads = getWantedThreads(options.scanSpeed); const scanFilesResult = await scanFiles( files, manifestReplacements, + threads, scanSpinner ); diff --git a/src/stages/scan-files.ts b/src/stages/scan-files.ts index f3d2a94..e4ab066 100644 --- a/src/stages/scan-files.ts +++ b/src/stages/scan-files.ts @@ -13,6 +13,7 @@ const available = availableParallelism(); export function scanFiles( files: string[], replacements: modReplacements.ModuleReplacement[], + threads: number, spinner: ReturnType ): Promise { return new Promise((resolve, reject) => { @@ -21,7 +22,7 @@ export function scanFiles( const filesLength = files.length; const results: FileReplacement[] = []; - for (const file of files.splice(0, available)) { + for (const file of files.splice(0, threads)) { const worker = new Worker(`${__dirname}/workers/scan-file.js`); // todo, what todo with the errors? worker.on('error', (error) => reject(error.message)); From 1ee5e47cc6e09408d8f931279364503b73ef8d0e Mon Sep 17 00:00:00 2001 From: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:30:35 +0200 Subject: [PATCH 6/8] --advanced -> --parallelism & remove from interactive cli Resolves 57bb3a0df36f012632b188e186a89177b405d1ac#r1714034609 --- README.md | 10 ++++---- src/main.ts | 67 +++++++++++------------------------------------------ 2 files changed, 19 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index d259df6..9eec797 100644 --- a/README.md +++ b/README.md @@ -28,14 +28,14 @@ The interactive CLI can guide you through the following steps: npx esperf ``` -## Advanced Usage +### Flags -The interactive CLI will show more steps: +#### parallelism -- Set scan speed +Amount of threads to use. -```sh -npx esperf --advanced +``` +npx esperf --parallelism 4 ``` ## License diff --git a/src/main.ts b/src/main.ts index 273c353..fd966cc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,25 +10,21 @@ import {traverseFiles} from './stages/traverse-files.js'; import {scanDependencies} from './stages/scan-dependencies.js'; import {availableParallelism} from 'node:os'; -let advanced = false; +const maxThreads = availableParallelism(); -for (let i = 0; i < process.argv.length; ++i) { - if (process.argv[i] === '--advanced') advanced = true; -} +const params: { + parallelism: number; +} = { + parallelism: maxThreads * 0.5 +}; -function getWantedThreads(scanSpeed: string): number { - const threads = availableParallelism(); - switch (scanSpeed) { - case 'slow': - return threads * 0.25; - case 'medium': - return threads * 0.5; - case 'fast': - return threads * 0.75; - case 'fastest': - return threads; +for (let i = 0; i < process.argv.length; ++i) { + if (process.argv[i] === '--parallelism ') { + params.parallelism = Math.min( + Math.max(Number(process.argv[i]) + 1, 1), + maxThreads + ); } - return 1; } const availableManifests: Record = { @@ -140,41 +136,7 @@ async function runModuleReplacements(): Promise { cl.confirm({ message: 'Automatically uninstall packages?', initialValue: false - }), - scanSpeed: () => - advanced - ? cl.select({ - message: 'Preferred scan speed', - options: [ - { - value: 'fastest', - label: 'Fastest', - hint: 'uses all the threads, pushes cpu to 100%' - }, - { - value: 'fast', - label: 'Fast', - hint: '75% of available threads' - }, - { - value: 'medium', - label: 'Medium', - hint: '50% of available threads' - }, - { - value: 'slow', - label: 'Slow', - hint: '25% of available threads' - }, - { - value: 'slowest', - label: 'Slowest', - hint: 'disables parallelism, 1 thread' - } - ], - initialValue: 'medium' - }) - : Promise.resolve('medium') + }) }, { onCancel: () => { @@ -254,12 +216,11 @@ async function runModuleReplacements(): Promise { try { const files = await traverseFiles(options.filesDir); - const threads = getWantedThreads(options.scanSpeed); const scanFilesResult = await scanFiles( files, manifestReplacements, - threads, + params.parallelism, scanSpinner ); From 630533db301e54ebc3af9b766117a6a0520f0b2e Mon Sep 17 00:00:00 2001 From: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:47:25 +0200 Subject: [PATCH 7/8] Fix scanFilesResult returning null[] --- src/stages/scan-files.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stages/scan-files.ts b/src/stages/scan-files.ts index e4ab066..38eeb25 100644 --- a/src/stages/scan-files.ts +++ b/src/stages/scan-files.ts @@ -28,7 +28,7 @@ export function scanFiles( worker.on('error', (error) => reject(error.message)); worker.on('message', (message) => { if (message?.type === 'result') { - results.push(message.value); + if (message.value) results.push(message.value); i += 1; if (i === filesLength) { resolve(results); From b28830eeacc7cd590a3881474b22be1942402207 Mon Sep 17 00:00:00 2001 From: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:48:06 +0200 Subject: [PATCH 8/8] round threads --- src/main.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main.ts b/src/main.ts index fd966cc..40e3e93 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,14 +15,13 @@ const maxThreads = availableParallelism(); const params: { parallelism: number; } = { - parallelism: maxThreads * 0.5 + parallelism: Math.round(maxThreads * 0.5) }; for (let i = 0; i < process.argv.length; ++i) { if (process.argv[i] === '--parallelism ') { - params.parallelism = Math.min( - Math.max(Number(process.argv[i]) + 1, 1), - maxThreads + params.parallelism = Math.round( + Math.min(Math.max(Number(process.argv[i]) + 1, 1), maxThreads) ); } }