moon icon indicating copy to clipboard operation
moon copied to clipboard

[feature] Improvements to code generation templates

Open svallory opened this issue 1 year ago • 8 comments

Is your feature request related to a problem? Please describe.

Just some suggestions for the template creation DX

Describe the solution you'd like

I've come across a lot of things I'm wanted to do in the templates I'm writing, so I'm leaving the suggestions here. I'll try to keep them low-level and precise.

  • [x] Sort prompts Some template questions have a logical order. Like asking if you want to generate A before asking for details for A generation. This can be achieved by making variables an array or adding a separate prompts array definition
  • [ ] Conditional prompts / variables Some prompts don't make sense depending on the answer to other prompts. Right now, the only way around this is
  • [x] Internal variables Allow template so specify variables cannot be changed from the CLI. This is useful for passing variables to the next templates in the chainto add "only used if X == Y" to the prompt text
  • [ ] Dynamic template chain / conditional template extension It would be really nice to allow an extended template to only be executed under certain conditions. Right now this can be achieved by defining a variable in the first one that gets propagate to the next one. The next template then uses that variable to determine the value of skip, but that's a highly tight coupling. This could be implemented by allowing extends to accept an object with skip (or condition) condition that works like the one in template files frontmatter
  • [x] Automatic template help This can be as simple as listing the template variables or showing the entire yaml on moon generate TEMPLATE --help It would be nice to a decription or docs property to variables
  • [ ] Shared mutable memory properties Some kind of shared in-memory object would help a lot. We don't need to make the entire context mutable, a cache key-value map in the Tera context that can be modified by any template would be enough I'm currently using extends to make a value available to all template files without copying and pasting code (imports do not work, but I think that is a Tera bug)
  • [x] Ability to iterate template variables This would allow base templates to pattern match the variables which may be very handy (think babel-plugin-*)

Minor things

skip vs condition

I find it odd to set when a file should be skipped instead of when it should run. To me, condition: {{ debug }} is more straightforward than skip: {{ not debug }} and I think it gets worse for more complex rules. Maybe it's just me, but I think there's a double negation hidden in there that always makes me write conditions wrong at the first time.

Idk, it seems to me that there are less checking to be done when I can say "do this if X" then "skip if X" because (in my mind) that unfolds to "do not generate if X" and then X becomes more complicated because if it is true I need to return false because I'm deciding if I should not do the thing... Idk, maybe I'm crazy lol

svallory avatar Mar 17 '24 03:03 svallory

All great ideas. A few comments:

  • Sort prompts - Would an order config for each prompt be enough for this?
  • Conditional prompts - This seems tricky, but maybe doable if other things land first.
  • Internal variables - Easy enough.
  • Dynamic template chain - Would have to look into.
  • Automatic template help - We'll be adding template related commands in the future.
  • Shared mutable memory properties - Would have to look into.
  • Iterate variables - I think you can use __tera_context for this: https://keats.github.io/tera/docs/#variables

milesj avatar Mar 17 '24 06:03 milesj

@milesj great to hear there are! I see moon as the tool that will be always there, so the greater its template support the better! 🥳

svallory avatar Mar 18 '24 22:03 svallory

Landing some of this here: https://github.com/moonrepo/moon/pull/1398

milesj avatar Mar 22 '24 00:03 milesj

Looks like I forgot to actually send the detailed response:

  • Sort prompts - Would an order config for each prompt be enough for this?

For sorting, yes. But if you ever want to support prompt flows, maybe it is better to move it to an array of objects.

  • Conditional prompts - This seems tricky, but maybe doable if other things land first.

There's a way you can add features little by little and get conditional, and a more flexible prompt flow, way better than sorted

  1. Make it an array which allows ordering
  2. Make it a tree by add a prompts property to a prompt. Order moves to a depth-first tree walk
  3. add a guard|when property to the prompt, and only go down the tree if the parent value matches the guard

It could look something like this (I'm sure you'll pick better names):

variables:
  - name: color
    type: 'enum'
    values: ['red', 'green']
    default: 'red'
    prompt: 'Favorite color?'
    
    next: 
      # only one will execute
      # but that can be decided by the tree visitor
      # there's no need to pre-evaluate the flow
      - case: 'red' 
        name: question_1
        type: string
        prompt: 'Name 3 red fruits'

        next: # both will be executed
          # no case here
          - name: question_2
            type: string
            prompt: 'Another red question'

          # neither here
          - name: question_3
            type: string
            prompt: 'Another red question'

      - case: 'green'
        name: question_1
        type: string
        prompt: 'Name 3 mostly green country flags'
        next:
          - ...
  • Iterate variables - I think you can use __tera_context for this: https://keats.github.io/tera/docs/#variables

I tried but it doesn't work. As they say, it is a magical... I guess it is magical because you can't touch it, only print it lol

svallory avatar Mar 24 '24 23:03 svallory

Landed some of these in v1.23.

milesj avatar Mar 25 '24 23:03 milesj