angular-cli icon indicating copy to clipboard operation
angular-cli copied to clipboard

Schematic with multi select x-promt errors when using command line parameter: 'path ".foo" should be array'

Open d-koppenhagen opened this issue 6 years ago • 12 comments

🐞 Bug report

Command (mark with an x)

  • [ ] new
  • [ ] build
  • [ ] serve
  • [ ] test
  • [ ] e2e
  • [x] generate
  • [x] add
  • [ ] update
  • [ ] lint
  • [ ] xi18n
  • [ ] run
  • [ ] config
  • [ ] help
  • [ ] version
  • [ ] doc

Is this a regression?

No, it's not. Multi select prompt is just supported within angular cli > 9.0.0-rc.3.

Description

Creating an angular schematic that prompts the user for multiple options is possible since https://github.com/angular/angular-cli/issues/16104. The prompt works quite good now but using the command line for multi selections will cause an error:

Error: Schematic input does not validate against the Schema: {"name":"paul"}
Errors:

  Data path ".name" should be array.
...

At first I opened an issue for the docs because I thought it's just not documented: https://github.com/angular/angular/issues/33851 But after checking the implementation of the angular guard schematic which has --implements parameter (which works) and creating the minimal reproduction repo (see underneath), I think it's a bug somewhere in @angular-devkit/schematics-cli.

🔬 Minimal Reproduction

I created a very minimal repo for reproduction: https://github.com/d-koppenhagen/ngx-multi-select-schematic

npm i -g @angular-devkit/schematics-cli@next
git clone [email protected]:d-koppenhagen/ngx-multi-select-schematic.git
cd ngx-multi-select-schematic
npm i

✅ Using prompt works good:

schematics .:hello --debug=false
? Which interfaces would you like to implement? (Press <space> to select, <a> to toggle all, <
i> to invert selection)
❯◉ hans
 ◉ peter
 ◉ paul
schematics .:hello --debug=false --name="paul, peter"

❌Using command line parameter fails (event not with just a single name handed over:

schematics .:hello --debug=false --name="paul"
Error: Schematic input does not validate against the Schema: {"name":"paul"}
Errors:

  Data path ".name" should be array.
    at MapSubscriber.registry.compile.pipe.operators_1.map.result [as project] (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/schematics/tools/schema-option-transform.js:31:27)
    at MapSubscriber._next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/operators/map.js:49:35)
    at MapSubscriber.Subscriber.next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/Subscriber.js:66:18)
    at ThrowIfEmptySubscriber._next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/operators/throwIfEmpty.js:44:26)
    at ThrowIfEmptySubscriber.Subscriber.next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/Subscriber.js:66:18)
    at TakeSubscriber._next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/operators/take.js:54:30)
    at TakeSubscriber.Subscriber.next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/Subscriber.js:66:18)
    at MergeMapSubscriber.notifyNext (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/operators/mergeMap.js:92:26)
    at InnerSubscriber._next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/InnerSubscriber.js:28:21)
    at InnerSubscriber.Subscriber.next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/Subscriber.js:66:18)

🔥 Exception or Error

Error: Schematic input does not validate against the Schema: {"name":"paul"}
Errors:

  Data path ".name" should be array.
    at MapSubscriber.registry.compile.pipe.operators_1.map.result [as project] (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/schematics/tools/schema-option-transform.js:31:27)
    at MapSubscriber._next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/operators/map.js:49:35)
    at MapSubscriber.Subscriber.next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/Subscriber.js:66:18)
    at ThrowIfEmptySubscriber._next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/operators/throwIfEmpty.js:44:26)
    at ThrowIfEmptySubscriber.Subscriber.next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/Subscriber.js:66:18)
    at TakeSubscriber._next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/operators/take.js:54:30)
    at TakeSubscriber.Subscriber.next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/Subscriber.js:66:18)
    at MergeMapSubscriber.notifyNext (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/operators/mergeMap.js:92:26)
    at InnerSubscriber._next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/InnerSubscriber.js:28:21)
    at InnerSubscriber.Subscriber.next (/usr/local/lib/node_modules/@angular-devkit/schematics-cli/node_modules/rxjs/internal/Subscriber.js:66:18)

🌍 Your Environment


ng version
     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 9.0.0-rc.4
Node: 10.14.0
OS: darwin x64

Angular: undefined
... 
Ivy Workspace: Yes

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.900.0-rc.4 (cli-only)
@angular-devkit/core         9.0.0-rc.4
@angular-devkit/schematics   9.0.0-rc.4
@schematics/angular          9.0.0-rc.4 (cli-only)
@schematics/update           0.900.0-rc.4 (cli-only)
rxjs                         6.5.3
typescript                   3.6.4

d-koppenhagen avatar Nov 29 '19 17:11 d-koppenhagen

This is somewhat related to https://github.com/angular/angular-cli/issues/12150 but in this case it's for Array's

alan-agius4 avatar Dec 02 '19 07:12 alan-agius4

It seems that the cause of this error is somewhere in validateOptionsWithSchema : https://github.com/angular/angular-cli/blob/fd6bba38a008c6ff89584c3edf8ff5ab326f51d1/packages/angular_devkit/schematics/tools/schema-option-transform.ts#L43 InvalidInputOptions seems to be called with an object of {"name":"paul"} instead of: {"name":["paul"]}

d-koppenhagen avatar Dec 04 '19 19:12 d-koppenhagen

hey @alan-agius4 do you think this issue will be faced before releasing Angular 10? I tried to understand the implementation and would like to contribute but I didn't find out how to debug it until now ^^

d-koppenhagen avatar Jan 28 '20 20:01 d-koppenhagen

@alan-agius4 sorry, I meant Angular 9 in the above comment.

d-koppenhagen avatar Feb 11 '20 20:02 d-koppenhagen

@d-koppenhagen, currently this is not a top priority for the team.

alan-agius4 avatar Feb 11 '20 21:02 alan-agius4

@alan-agius4 I see. Meanwhile I dounf out how to debug and install the CLI locally. I found the place where the error occurs. I provided a PR (https://github.com/angular/angular-cli/pull/16985) to fix it.

d-koppenhagen avatar Feb 15 '20 15:02 d-koppenhagen

I don’t think that PR addresses the above issue. In the above issue you are using schematics CLI, however the fix is for Angular CLI.

alan-agius4 avatar Feb 15 '20 17:02 alan-agius4

Yes that’s true. But with the fix and building the CLI locally, the described issue doesn’t appear. So I think it will fix it indeed.

d-koppenhagen avatar Feb 15 '20 18:02 d-koppenhagen

In the issue I just use the schematics CLI during the schematics development. But in the end I will run the schematic with the Angular CLI using ng add.

d-koppenhagen avatar Feb 15 '20 18:02 d-koppenhagen

For anyone interested, the current workaround works for me:

schematics .:hello --debug=false --name=paul --name=peter

(tested with Angular CLI: 11.2.12)

Badisi avatar May 06 '21 17:05 Badisi

Same problem

(We're using Angular CLI v17.3.8)

We're having the same problem in our open source library Spartacus, when our users run our installation schematics (ng add) with a command line option --features that has a type: array. See the following screenshot:

image

Workaround: first npm-install; then ng-add

Interestingly, the error doesn't happen at all, if I first manually npm-install the pacakge and only then run the same ng add command (with array) option.

Why the problem seems to happen

The problem of mis-interpreting the command-line option as a string instead of array seems to occur, because the anticipated schema.json of our package (which informs that a flag --features is type: array) is not known at the time of parsing the command-line arguments.

As far as I could NodeJS-debug the whole process, AngularCLI command ng add seems to work in the following way:

  1. first it parses the command-line arguments early, without knowing the schema.json of the package that we're going to add
  2. then it npm-installs the package (which means also downloading its schema.json)
  3. then it validates lately if the parsed command-line arguments match the expected type defined in the downloaded schema.json

Above workaround doesn't work when passing the exact version ⚠️

The above mentioned workaround (i.e. first npm-installing the package before running ng add) doesn't work, when I pass the exact version of my package in the ng add command. See the following screenshot: image

Our plan to resign from type: array

We plan to resign from using type: array (as it seems to be a bug in Angular that we cannot fix ourselves) and use type: string with comma-separated values by convention, e.g. --features="Cart, Order, Checkout". Then in the logic of our installation schematics, we plan to split the strings by comas and therefore have an array effectively.

Idea for a fix in Angular?

@alan-agius4 Do you think AngularCLI could re-parse the command line arguments lately, i.e. after the added package is already npm-installed, so the schema.json of the added package (informing which params are type:array) is really taken into account?

Platonn avatar May 31 '24 12:05 Platonn

Hi @alan-agius4 If possible, I'd happy to provide a PR with a fix. My proposed approach is to re-parse the CLI arguments after the package is npm-installed so to we could use the package's precise schema.json for determining properly the types of arguments (e.g. array instead of string).

TBH I'd need a bit of an advice, how we could possibly workaround the limitations of the current architecture. Let me share my understanding of the current architecture:

  • there is an inversion of control: we plug our commands (in this case AddCommandModule) into the yargs tool (source) by exposing 2 "template methods": build() and run() in our AddCommandModule
  • yargs will invoke those 2 methods:
    • first it will invoke build() - it's the only time, when we can access the localYargs object and instruct it on how to parse the raw CLI arguments like ['ng', 'add', '@my/lib', '--features=foo', '--features=bar'] - especially by calling the convenient method localYargs.option(<optionName>, ...)
    • then it invokes run() (via handle() method) - then we no longer have access to the parser object localYargs, but we have access only to the already-parsed options object. There we npm-install the package (if needed) and then we execute its schematics with passing those already-parsed options object.

Do you have any idea of a workaround how we could re-parse the raw CLI arguments (i.e. invoke this.addSchemaOptionsToCommand() in the run() function, after npm-installing a package but before executing the schematics? Thanks to this, we could pass properly interpreted options object to the executed schematics (e.g. --features=foo --features=bar parsed to an array features: ['foo', 'bar'], instead of a string features: 'bar').

Platonn avatar Jun 04 '24 15:06 Platonn