Skip to content

bug(vite-plugin-angular): useAngularCompilationAPI duplicates diagnostics across all emitted files #2317

@tomer953

Description

@tomer953

Please provide the environment you discovered this bug in.

Angular: 21.2.10
@analogjs/vite-plugin-angular: 2.3.1
Vite: 7.3.1 (rolldown-vite)
Node: 22.x

Which area/package is the issue in?

vite-plugin-angular

Description

Hi, I'm copy-paste my AI 🤖 diagnostic to the issue, so not sure about the recommendation and solution, but I can confirm the problem is exists.
Confirmed — the warnings do not include file paths. This is a second bug in the same code.

When using experimental: { useAngularCompilationAPI: true }, the diagnostic handling in performAngularCompilation() has two issues:

Bug 1: Diagnostics duplicated across all emitted files (N×M explosion)

In performAngularCompilation() (angular-vite-plugin.ts), the global diagnostics array is attached to every emitted file:

const diagnostics = await compilation.diagnoseFiles(/* ... */);
const errors = diagnostics.errors?.length ? diagnostics.errors : [];
const warnings = diagnostics.warnings?.length ? diagnostics.warnings : [];

for (const file of await compilation.emitAffectedFiles()) {
    outputFiles.set(file.filename, {
        content: file.contents,
        dependencies: [],
        errors: errors.map((error) => error.text || ''),
        warnings: warnings.map((warning) => warning.text || ''),
    });
}

Then in the transform hook, each file emits its attached warnings via this.warn():

const typescriptResult = fileEmitter(id);
if (typescriptResult?.warnings?.length > 0) {
    this.warn(`${typescriptResult.warnings.join('\n')}`);
}

Result: In our project with 485 unique warnings and 85 emitted component files: 485 × 85 = 41,087 total warnings in the build log (4.1 MB of output).

Bug 2: File location info is discarded

The diagnostic messages from @angular/build's compilation.diagnoseFiles() are esbuild-style PartialMessage objects with rich location info:

// What @angular/build returns:
{
  text: "NG8113: AddAssetGroupModalComponent is not used within the template of AssetsGroupsComponent",
  location: {
    file: "src/app/views/assets/assets-groups.component.ts",
    line: 42,
    column: 5,
    length: 30,
    lineText: "  AddAssetGroupModalComponent,"
  },
  notes: [/* related info */]
}

But the plugin only keeps warning.text, discarding location and notes:

warnings: warnings.map((warning) => warning.text || ''),  // ← location lost!

Result: Warnings show only the message text with no file path, line number, or column — making it impossible to locate the issue without searching the codebase.

Expected Behavior

  1. Each diagnostic should be emitted once (not per-file)
  2. Diagnostics should include the file path and line number from location

Suggested Fix

async function performAngularCompilation(config) {
    // ... compilation setup ...
    const diagnostics = await compilation.diagnoseFiles(/* ... */);

    // Emit diagnostics once, with location info
    for (const warning of diagnostics.warnings || []) {
        const loc = warning.location;
        const prefix = loc ? `${loc.file}:${loc.line}:${loc.column} - ` : '';
        globalWarnings.push(`${prefix}${warning.text}`);
    }

    // Don't attach diagnostics to individual files
    for (const file of await compilation.emitAffectedFiles()) {
        outputFiles.set(file.filename, {
            content: file.contents,
            dependencies: [],
            errors: [],
            warnings: [],
        });
    }
}

// Emit all warnings once in buildStart or closeBundle
closeBundle() {
    if (globalWarnings.length > 0) {
        this.warn(globalWarnings.join('\n'));
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions