Angular Modernizer
    Preparing search index...

    Module @angular-modernizer/worker - v1.2.0

    Worker thread script for parallel analysis with the Angular Modernizer.

    The WorkerPool and ParallelExecutor of @angular-modernizer/core start Node.js worker threads from a script path. The script lives in this package because it loads all analysis plugins, and the plugins depend on core. getWorkerScriptPath returns that path. WorkerTask and WorkerResult are the messages exchanged with a worker.

    @angular-modernizer/worker

    Worker thread script for parallel analysis in the Angular Modernization Platform.

    @angular-modernizer/core can run analysis rules in Node.js worker threads (WorkerPool, ParallelExecutor). Each worker is started from a script file. That script lives in this package, and getWorkerScriptPath() returns its path.

    The package exports:

    • getWorkerScriptPath() - absolute path to the compiled worker script (dist/analysis-worker.js)
    • WorkerTask, WorkerResult - message types, re-exported from @angular-modernizer/core

    The script itself is also published as the subpath export @angular-modernizer/worker/analysis-worker. It can only run as a worker thread; importing it on the main thread throws.

    Worker threads run in isolated V8 contexts, so the worker script has to load all analysis plugins itself. The plugins depend on core, and core contains the WorkerPool. Placing the script in core would create a circular dependency.

    This package resolves that by:

    1. Depending on all plugin packages (it builds after them)
    2. Exporting a helper that returns the worker script path
    3. Being used by both programmatic API users and the MCP adapter (scan-architecture passes the path to the parallel executor)
    pnpm add @angular-modernizer/worker @angular-modernizer/core
    

    The worker script is loaded from dist/, so in the monorepo all workspace packages must be built first (pnpm build from the root).

    ParallelExecutor decides on its own whether to use worker threads (see Mode Selection). Pass the script path, the tsconfig the workers load and the plugin IDs whose rules the workers run:

    import { getWorkerScriptPath } from '@angular-modernizer/worker';
    import { ParallelExecutor } from '@angular-modernizer/core';

    const executor = new ParallelExecutor({ maxWorkers: 4 });

    const results = await executor.executeParallel(sourceFiles, rules, {
    tsConfigPath: '/path/to/tsconfig.json',
    workerScriptPath: getWorkerScriptPath(),
    pluginIds: ['architecture', 'solid'],
    });

    await executor.shutdown();
    import { getWorkerScriptPath } from '@angular-modernizer/worker';
    import { WorkerPool } from '@angular-modernizer/core';

    const pool = new WorkerPool({
    tsConfigPath: '/path/to/tsconfig.json',
    workerScriptPath: getWorkerScriptPath(),
    maxWorkers: 4,
    });

    const result = await pool.executeTask({
    id: 'task-1',
    type: 'analyze-rule',
    filePath: '/path/to/file.ts',
    ruleIds: [], // empty: all rules of the listed plugins
    pluginIds: ['architecture', 'solid'],
    });

    if (result.error) {
    console.error(result.error);
    }
    console.log(result.results);

    await pool.terminate();

    WorkerPool throws when workerScriptPath is missing.

    Each worker started from the script:

    1. Reads workerId and tsConfigPath from workerData and loads the whole project from that tsconfig into its own ts-morph Project. Rules need cross-file context (inheritance, imports), so a worker does not load single files.
    2. Creates its own PublicApi and posts { type: 'ready', workerId }. If loading fails, it posts { type: 'init-error', workerId, error } and exits. The pool only hands out tasks to workers that sent ready.
    3. Receives WorkerTask messages and answers each with a WorkerResult.

    For a task the worker:

    • loads the analysis rules of the plugins in pluginIds; rules are cached per plugin ID for later tasks
    • keeps only the rules listed in ruleIds when that list is not empty
    • builds the context with ContextFactory.createAnalysisContext() and runs the rules on filePath
    • returns the results with the severity warning (plugin results carry no severity)

    Accepted plugin IDs:

    Plugin ID Package
    architecture @angular-modernizer/plugin-architecture
    solid @angular-modernizer/plugin-solid
    angular @angular-modernizer/plugin-angular
    analyzer @angular-modernizer/plugin-analyzer
    standalone @angular-modernizer/plugin-standalone (no analysis rules)

    The full package name works as well. Unknown IDs are logged and ignored. With no matching rules the task returns an empty result list, so pass pluginIds: the ParallelExecutor sends an empty list when pluginIds is not set.

    Errors:

    • A rule that throws is logged and skipped; the other rules still run.
    • A task fails (empty results, error set) when filePath is not part of the loaded project or the worker is not initialized.
    • An uncaught exception or unhandled rejection in a worker posts init-error and ends the worker.

    Workers log to the console with the prefix [Worker <id>].

    ParallelExecutor.executeParallel() chooses between Promise-based parallelism on the main thread and worker threads. With autoSelectMode (default) and no useWorkerThreads, it computes a complexity score from 0 to 100:

    Factor Weight
    File count (2000 files = 100) 30%
    Estimated lines of code (1M = 100) 35%
    Large files over 50 KB 20%
    Current heap usage above 70% 15%
    Score Mode
    below 35 Promise mode
    35 to below 50 Promise mode with memory monitoring; aborts when heap usage reaches 85% and falls back to workers if workerScriptPath is set
    50 and above Worker threads

    Overrides:

    • useWorkerThreads: true uses workers when there are at least minFilesForWorkers files (default 200), otherwise Promise mode
    • useWorkerThreads: false always uses Promise mode
    • autoSelectMode: false without useWorkerThreads uses Promise mode

    If worker execution fails, the executor terminates the pool and falls back to Promise mode.

    Details: docs/architecture/parallelism.md.

    Setting Default
    Workers (WorkerPool) CPU cores - 1, at least 1
    Heap per worker (WorkerPool, workerHeapLimit) 512 MB old generation; ParallelExecutor sets 4096 MB
    Task timeout (defaultTimeout / timeout) 30000 ms, counted from the moment a worker is ready
    Wait for the first ready worker 120 s

    Each worker loads the full project, so memory grows with the number of workers. A worker that hits its heap limit is ended by Node.js; lower maxWorkers or raise workerHeapLimit for large projects. Workers do not report their heap usage, so the pool cannot recycle them based on real memory use.

    Data crossing the thread boundary is copied with the structured clone algorithm. Use plain objects, primitives and arrays: functions and Symbols cause a DataCloneError, class instances arrive as plain objects without their methods. WorkerTask.config must follow this rule.

    This package builds after all plugins in the monorepo:

    1. core
    2. plugin-system
    3. api
    4. plugin-analyzer, plugin-solid, plugin-angular, plugin-standalone, plugin-architecture, orchestration
    5. worker (this package)
    6. adapter-mcp

    The package has no tests of its own. The worker pool tests live in @angular-modernizer/core and are skipped unless ENABLE_WORKER_TESTS=true is set and the worker script is built:

    pnpm run build
    ENABLE_WORKER_TESTS=true pnpm --filter @angular-modernizer/core test
    • @angular-modernizer/core - WorkerTask, WorkerResult
    • @angular-modernizer/api - createPublicApi for each worker
    • @angular-modernizer/plugin-system - ContextFactory, rule contracts
    • @angular-modernizer/plugin-analyzer, plugin-solid, plugin-angular, plugin-standalone, plugin-architecture - rules run in workers
    • ts-morph - project loading

    Interfaces

    WorkerResult
    WorkerTask

    Functions

    getWorkerScriptPath