Skip to content

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

@brandonroberts

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
    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