Oxc linker rejects parenthesized resolveMetadata return in deferred class metadata

Aperta Adatta ai principianti
#34,129 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
76/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
angular, typescript

Direzione di ricerca

Esegui la riproduzione minima con npm run build e confrontala con npm run build:babel. Parti da OxcAstHost.parseReturnValue() e PartialClassMetadataAsyncLinkerVersion1.linkPartialDeclaration, usando fixture/index.mjs per confrontare il corpo conciso di arrow tra parentesi con la forma funzionante con corpo a blocco. Il lavoro è completato quando entrambe le forme equivalenti di callback vengono collegate correttamente con il linker Oxc predefinito.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

area: @angular/build gemini-triaged
Command

build

Description

With @angular/build@22.2.0-rc.0, the default Oxc linker fails to process a partial ɵɵngDeclareClassMetadataAsync declaration whose resolveMetadata callback returns a parenthesized object literal. The same input builds successfully with NG_BUILD_BABEL_LINKER=1.

The fixture below is hand-reduced from Angular 22.1.7 output for a library component containing @defer; it removes the original application's dependencies and UI. It exercises the real ng build pipeline, not a mock linker.

Is this a regression?

The original library artifact passes our stable Angular 20/21/22 CI matrix and fails the 22.2.0-rc.0 canary. I have not bisected the first affected version using this reduced reproduction.

Minimal Reproduction

Create an empty directory with the following files, then run:

npm install
npm run build
# Fails: Unsupported syntax, expected an object literal.

npm run build:babel
# Succeeds: same input, Babel linker.

The build cache is disabled in this reproduction.

Complete minimal project files

package.json

{
  "name": "angular-oxc-deferred-metadata-repro",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "ng build",
    "build:babel": "NG_BUILD_BABEL_LINKER=1 ng build"
  },
  "dependencies": {
    "@angular/core": "22.2.0-rc.0",
    "@angular/common": "22.2.0-rc.0",
    "@angular/compiler": "22.2.0-rc.0",
    "@angular/platform-browser": "22.2.0-rc.0",
    "rxjs": "7.8.2",
    "tslib": "2.8.1",
    "deferred-metadata-fixture": "file:./fixture"
  },
  "devDependencies": {
    "@angular/build": "22.2.0-rc.0",
    "@angular/cli": "22.2.0-rc.0",
    "@angular/compiler-cli": "22.2.0-rc.0",
    "typescript": "6.0.3"
  }
}

angular.json

{
  "version": 1,
  "cli": {
    "cache": {
      "enabled": false
    },
    "analytics": false
  },
  "projects": {
    "repro": {
      "projectType": "application",
      "root": "",
      "architect": {
        "build": {
          "builder": "@angular/build:application",
          "options": {
            "browser": "src/main.ts",
            "tsConfig": "tsconfig.json",
            "index": "src/index.html",
            "outputPath": "dist",
            "optimization": false
          }
        }
      }
    }
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "preserve",
    "moduleResolution": "bundler",
    "experimentalDecorators": true,
    "skipLibCheck": true,
    "strict": true,
    "lib": [
      "ES2022",
      "DOM"
    ]
  },
  "files": [
    "src/main.ts"
  ]
}

src/main.ts

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { DeferredFixture } from 'deferred-metadata-fixture';

@Component({ selector: 'app-root', template: 'Linker reproduction' })
class App {
  constructor() { console.log(DeferredFixture); }
}

bootstrapApplication(App).catch(console.error);

src/index.html

<!doctype html><html><head><title>Reproduction</title><base href="/"></head><body><app-root></app-root></body></html>

fixture/package.json

{
  "name": "deferred-metadata-fixture",
  "version": "1.0.0",
  "type": "module",
  "main": "index.mjs",
  "types": "index.d.ts"
}

fixture/index.mjs

import * as i0 from '@angular/core';

export class DeferredFixture {}

// Reduced from Angular 22.1.7 output for a component containing @defer.
// The parenthesized object returned by resolveMetadata triggers the failure.
i0.ɵɵngDeclareClassMetadataAsync({
  minVersion: '18.0.0',
  version: '22.1.7',
  ngImport: i0,
  type: DeferredFixture,
  resolveDeferredDeps: () => [],
  resolveMetadata: () => ({
    decorators: [],
    ctorParameters: null,
    propDecorators: null,
  }),
});

fixture/index.d.ts

export declare class DeferredFixture {}

Additional control: in fixture/index.mjs, replace resolveMetadata: () => ({ ... }) with the equivalent resolveMetadata: () => { return { ... }; }. Running npm run build then succeeds with the default Oxc linker. This was verified locally, in addition to the Babel comparison.

Expected: both equivalent callback forms link successfully with the default builder.

Actual: only the parenthesized expression form fails.

Exception or Error
Unsupported syntax, expected an object literal. [plugin angular-compiler]

at OxcAstHost.parseObjectLiteral (.../oxc-ast-host.js:95:19)
at _AstObject.parse
at _AstValue.getObject
at PartialClassMetadataAsyncLinkerVersion1.linkPartialDeclaration
at FileLinker.linkPartialDeclaration
at OxcLinker.linkCallExpression
Your Environment
Angular CLI: 22.2.0-rc.0
Angular: 22.2.0-rc.0
@angular/build: 22.2.0-rc.0
@angular/compiler-cli: 22.2.0-rc.0
TypeScript: 6.0.3
Node.js: 26.5.0
npm: 11.17.0
OS: darwin arm64

The original failure also occurs on Ubuntu CI with Node 24.17.0.

Anything else relevant?

The parser represents the concise arrow body as ParenthesizedExpression(ObjectExpression). OxcAstHost.parseReturnValue() returns the body unchanged; the subsequent object-literal check only accepts ObjectExpression. Unwrapping the parenthesized return expression before interpreting the metadata appears to be the required fix.

Related PR #33769 addresses parentheses when emitting JavaScript, whereas this failure occurs when reading the callback's return value.

Lingua principale
TypeScript
Stelle
27k
Fork
11.8k
Merge medio
16h 21m
PR unite (30g)
170

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di angular/angular-cli

Tutte le issue di angular/angular-cli

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.