Skip to content

Prop Spreading Support via @analogjs/angular-compiler #11

Description

@brandonroberts

Context

The @analogjs/angular-compiler introduces JSX-style prop spreading in Angular templates:

<app-child {...props} />

At build time, the compiler expands this into individual input bindings ([name]="props.name" [title]="props.title") using a component registry that maps selectors to their declared inputs. The Angular Language Service doesn't understand {...} syntax, so the extension needs to provide editor support for it.

Architecture

Component Registry

A persistent ComponentRegistry (Map<string, RegistryEntry>) built from two sources:

  1. Project source files — scan .ts files using scanFile() from @analogjs/angular-compiler. Extracts selectors, inputs, and outputs from @Component/@Directive decorators and signal APIs (input(), output(), model()).
  2. Installed packages — scan .d.ts files in node_modules using scanPackageDts(). Reads Ivy declaration types (ɵɵComponentDeclaration, etc.) to extract metadata from pre-compiled Angular packages.

Built on server startup and incrementally updated via file watchers.

Volar Service Plugin

A custom Volar LanguageServicePlugin named analog-spread providing three features:

Hover (provideHover)

When the cursor is over a {...identifier} expression, show:

  • The matched component selector and class name
  • The list of inputs that will be expanded (name, type, required/optional, signal-based)
  • Which inputs are skipped due to explicit bindings on the same element

Completions (provideCompletionItems)

When typing inside {...}:

  • Triggered by { character
  • Offer {... snippet when { is typed inside an opening tag
  • Suggest variables in scope that could be spread

Diagnostics (provideDiagnostics)

Scan the template for {...} patterns using scanSpreads() and report:

  • Unknown component: spread on an element whose selector isn't in the registry
  • Missing required inputs: spread identifier doesn't cover all required inputs
  • Multiple spreads: more than one spread on the same element

Files to Create/Modify

New files

  • packages/language-server/src/registry.ts — Wraps @analogjs/angular-compiler scanning APIs (initial scan, incremental updates, lookup by selector)
  • packages/language-server/src/plugins/spreadPlugin.ts — Volar LanguageServicePlugin with hover, completions, and diagnostics

Modified files

  • packages/language-server/package.json — Add dependency on @analogjs/angular-compiler
  • packages/language-server/src/index.ts — Import and register the spread plugin

Key Dependencies

  • @analogjs/angular-compilerscanFile(), scanDtsFile(), scanPackageDts(), collectImportedPackages(), scanSpreads()
    • Brings oxc-parser (Rust-based, fast) and magic-string
    • Peer deps @angular/compiler and @angular/compiler-cli are only needed for the compile() function, NOT for registry scanning

Verification

  • Add @analogjs/angular-compiler and verify build succeeds
  • Create a test component with input() signals
  • Verify hover on {...props} shows component inputs
  • Verify diagnostic on unknown element selector
  • Verify diagnostic on missing required inputs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions