applyPostTransforms should not be applied for all builder schemas

未关闭
#14,827 2 条评论 3 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
35/100
Issue 类型
功能
描述清晰度
基本清楚
活跃度
停滞
技术栈
typescript
领域
tooling

调研方向

从 packages/angular_devkit/core/src/json/schema/registry.ts 开始,尤其检查 applyPostTransforms 选项,然后查看 packages/angular_devkit/architect/src/architect.ts,其中调用 validation 时没有传入第二个参数。复现 issue 中描述的 builder schema 行为,并确定特定 builder 如何禁用 addUndefinedDefaults,同时不更改其他 schema 的现有 defaults。

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

描述

area: @angular-devkit/architect feature feature: insufficient votes

🚀 Feature request

Command (mark with an x)
- [ ] new
- [ ] build
- [ ] serve
- [ ] test
- [ ] e2e
- [ ] generate
- [ ] add
- [ ] update
- [ ] lint
- [ ] xi18n
- [x] run
- [ ] config
- [ ] help
- [ ] version
- [ ] doc
Description

I am currently writing a builder and the appropriate JSON schema to validate its options.
The builder will be used in different targets, but all of these targets usually share some base options which is why I added some target reference support, similar to the browserTarget
option of the @angular-devkit/build-angular:dev-server builder.
An example config could look like this:

"target1": {
    "builder": "mybuilder:f":
    "options": {
        "a": "foo",
        "b": {
            "asd": "wer"
        }
    }
},
"target2": {
    "builder": "mybuilder:f":
    "options": {
        "targetRef": "myproject:target1", // References the options of target1 above
        "a": "bar"
    }
},

So, at the end the options of target2 should be evaluated to the following:

{
    "a": "bar",
    "b": {
        "asd": "wer"
    }
}

This should be as simple as using some Object.assign code:

const target2Options = {  "a": "bar"  };
const target1Options = {  "a": "foo", "b": { "asd": "wer" }  };
const result = Object.assign({}, target1Options, target2Options);

But the problem is, that Angular modifies the incoming options-object. It passes in an empty object for the b property of target2's options: { "a": "bar", "b": {} }. Thus, result.b will be {} instead of { "asd": "wer" }.
This is done by the Angular's postTransform addUndefinedDefaults which is always added to the CoreSchemaRegistry. In theory postTransforms can be disabled as seen here using applyPostTransforms: false. However, it is not possible to override this value when the builder's schema is compiled and the validator is used, see here (no second argument is passed to validation).

Of course, I could add some code to my builder that checks for that {} and ignores it. But actually, it should be possible to explicitly set b: {} to override the inherited value, e.g.:

"target3": {
    "builder": "mybuilder:f":
    "options": {
        "targetRef": "myproject:target1",
        "b": {}
    }
},

So I really need to distinguish between the {} set by the user and the {} set by the postTransform.

NOTE: All of this affects options that are set to type object or array (in which case [] is used as default) in the schema.

Describe the solution you'd like

It would be nice to be able to disable postTransforms for a specific builder, for example in the builder code itself.

Actually I do not know why the addUndefinedDefaults transform is used at all, but there is probably a reason for that. Is it? 😛

Describe alternatives you've considered

I cannot imagine any workarounds. Angular modifies the options passed to a builder where it should not, IMHO. In the above case, the user does not specified a b property, but Angular passes in an empty object for b. And currently there is no way to change that behavior, as far as I can see.

EDIT:
I did find a workaround, but it is an ugly hack as it relies on the internals of addUndefinedDefault and thus could easily break when Angular is updated.

Instead of this schema:

"b": {
    "type": "object",
    "additionalProperties": {
        "type": "string"
    }
}

One can use this schema:

"b": {
    "oneOf": [
        {
            "type": "null"
        },
        {
            "type": "object",
            "additionalProperties": {
                "type": "string"
            }
        }
    ]
}

This works, because Angular detects that two types are possible for b: objectand null. This causes Angular to not override the value.
Of course, null is now a valid value for b, too, but that may not be a problem in some cases.

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

贡献指南

打开贡献指南

从这里开始

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

angular/angular-cli 的其他 Issue

查看 angular/angular-cli 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

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