Oxc linker rejects parenthesized resolveMetadata return in deferred class metadata

未关闭 适合新手
#34,129 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
76/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
angular, typescript

调研方向

使用 npm run build 运行最小复现,并将其与 npm run build:babel 进行比较。从 OxcAstHost.parseReturnValue() 和 PartialClassMetadataAsyncLinkerVersion1.linkPartialDeclaration 开始,使用 fixture/index.mjs 比较带括号的简洁 arrow body 与可正常工作的 block-body 形式。完成标准是:两种等价的 callback 形式都能通过默认的 Oxc linker 成功链接。

由索引模型根据 Issue 内容生成。

描述

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.

主要语言
TypeScript
星标
27k
派生
11.8k
平均合并
16 小时 21 分钟
30 天内合并 PR
170

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

angular/angular-cli 的其他 Issue

查看 angular/angular-cli 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。