Oxc linker rejects parenthesized resolveMetadata return in deferred class metadata
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 76/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- angular, typescript
- Área
- build-system, tooling
Línea de trabajo
Ejecuta la reproducción mínima con npm run build y compárala con npm run build:babel. Comienza en OxcAstHost.parseReturnValue() y PartialClassMetadataAsyncLinkerVersion1.linkPartialDeclaration, usando fixture/index.mjs para comparar el cuerpo de arrow conciso entre paréntesis con la forma funcional con cuerpo de bloque. Se considera completado cuando ambas formas equivalentes de callback se enlazan correctamente con el enlazador Oxc predeterminado.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- TypeScript
- Estrellas
- 27k
- Forks
- 11.8k
- Merge medio
- 16 h 21 min
- PR fusionados (30 d)
- 170
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de angular/angular-cli
-
Support aube as package manager Abiertoarea: @angular/cli gemini-triaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
angular/angular-cli#34057 ·
-
Can't use an array of hostnames in --allowedHosts cli parameter in @angular/build:dev-server Abiertoarea: @angular/build gemini-triaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
angular/angular-cli#33955 ·
-
area: @angular/cli gemini-triaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
angular/angular-cli#33055 · 1 comentario · 3 reacciones ·
-
angular/build:library area: @angular/build gemini-triaged
angular/angular-cli#34131 · 1 asignado ·
-
angular/build:library area: @angular/build gemini-triaged
angular/angular-cli#34130 · 1 asignado ·
Todos los issues de angular/angular-cli
Issues similares
-
Type/Bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
OpenNSW/nsw-srilanka#497 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
0xMiden/bridge-portal#132 ·
-
react-doctor severity:warning tech-debt
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
digidem/comapeo-cloud-app#403 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100