angular / angular/angular-cli

applyPostTransforms should not be applied for all builder schemas

Offen
#14,827 2 Kommentare 3 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
area: @angular-devkit/architect feature feature: insufficient votes
Vorherrschende Sprache
TypeScript
Sterne
27k
Forks
11.8k
Ø Merge
14 Std. 23 Min.
Gemergte PRs (30 T.)
162

Beschreibung

# 🚀 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](https://github.com/angular/angular-cli/blob/48e6060e02792cd614a1f80ae8b87109df88c893/packages/angular_devkit/core/src/json/schema/registry.ts#L337L339) 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](https://github.com/angular/angular-cli/blob/master/packages/angular_devkit/architect/src/architect.ts#L63) (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`: `object`and `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.

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Beginne mit packages/angular_devkit/core/src/json/schema/registry.ts, insbesondere mit der Option applyPostTransforms, und untersuche anschließend packages/angular_devkit/architect/src/architect.ts, wo die Validierung ohne ein zweites Argument aufgerufen wird. Reproduziere das im Issue beschriebene Verhalten des Builder-Schemas und ermittle, wie ein bestimmter Builder addUndefinedDefaults deaktivieren kann, ohne die bestehenden Defaults für andere Schemas zu ändern.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
typescript
Bereich
tooling
Issue-Typ
Feature
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
35/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.