asyncapi / shape-up-process

This repo contains pitches and the current cycle bets. More info about the Shape Up process: https://basecamp.com/shapeup
https://shapeup.asyncapi.io
11 stars 8 forks source link

React as a Generator engine #1

Closed fmvilas closed 3 years ago

fmvilas commented 3 years ago

On This Page

Summary

Make Generator support React as a template engine, along with Nunjucks.

Problem

The current templating system —based on Nunjucks— has some really important drawbacks:

  1. It's hard (or impossible) to create reusable "partials" across templates. Ultimately, this leads to repetition, especially those of the same kind. For instance, POJO (Plain Old Java Objects) definitions are common across different templates. And this is something that's going to happen more and more as we create different templates for the same programming language.
  2. It gets really painful to debug a template when an error happens inside a macro. Even with the --debug flag turned on. That's a problem of Nunjucks itself and has been there for quite some time now.
  3. Even though Nunjucks provides lots of built-in filters, it's very common to find ourselves having to create a custom filter for basic stuff like checking the type of a variable, e.g., typeof variable === 'object' or running basic JS logic.
  4. It can get really hard to read a complex template. See this example.
  5. All the points above become super important if we want to increase the number of templates. Template authoring needs to be a smooth process, not a painful one.

Solution

In general, the solution goes through adding React as a template engine to the Generator. When the template declares that it's using React as its template engine, the Generator will render files using React instead of Nunjucks.

There a slight change in the "rendering paradigm" though. In the case of React, we don't "render the file" but instead we "import" it, "process" it, and get the result as a string that has to be written to the destination file. This will allow us to have top-level metadata on each file, like in the following example:

// File: $$schema$$.java

...
import { generateCamelCaseSchemaName } from '@asyncapi/generator/sdk'

export default function SchemaFile({ schema }) {
  return (
    <File name={generateCamelCaseSchemaName(schema)}>
      ... (your Java code here)
    </File>
  )
}

In the example above, we have a top-level <File> component that allows us to add meta information about the file itself, like its name, permissions, and more. Even more, we can tell Generator not to render this file by simply returning null in the SchemaFile function:

// File: $$schema$$.java

...
import { generateCamelCaseSchemaName } from '@asyncapi/generator/sdk'

export default function SchemaFile({ schema }) {
  if (schema.type !== 'object') return null // <== NOTICE THIS LINE

  return (
    <File name={generateCamelCaseSchemaName(schema)}>
      ... (your Java code here)
    </File>
  )
}

In the example above, this file will only be rendered if the type of the schema is object. This will allow us to eventually get rid of file name template syntax like $$schema$$, $$everySchema$$, and $$objectSchema$$. Also, the usage of <File name="..."> will eventually allow us to get rid of all the file name templates.

Solution elements

Rabbit holes & challenges

  1. Compiling React JSX and ES7 import syntax may be challenging. Watch out. We may have to introduce a step to precompile ES7 and JSX syntax before the renderer requires the files.
  2. We may have to create our own React renderer with its own DOM model and reconciler. Here's a great source of inspiration: https://github.com/chentsulin/awesome-react-renderer. This tool can also be helpful: https://www.npmjs.com/package/esinstall.

Out of bounds

  1. Removing the usage of file name templates.
  2. Creating an SDK for the Generator.
magicmatatjahu commented 3 years ago

We should also consider as followup Folder (or Directory) component to generate structure of generated template. Something like:

<Folder name="someFolder">
  <Folder name="folderInsideSomeFolder">
    <File name="deepFile.js">
      ... content
    </File>
  </Folder>
  <File name="someFile.js">
    ... content
  </File>
</Folder>

and then in output we will have:

someFolder
  folderInsideSomeFolder
    deepFile.js
  someFile.js
fmvilas commented 3 years ago

In my opinion, this should not even be considered. Nobody ever asked to have this feature in the template language and its functionality is something we already provide to the user in a different way. Having multiple ways of doing the same thing will only make the tool more complicated to use and maintain.

If at some point we get rid of Nunjucks, then it's something we can happily consider but this will change how the whole Generator works.

jonaslagoni commented 3 years ago

@fmvilas what is the procedure for finishing a cycle bet? 🤔

fmvilas commented 3 years ago

Closing this issue :)

jonaslagoni commented 3 years ago

Bet implemented in the react-sdk and integrated into the generator here. Template for templates was also re-written to only support React along with the markdown template.

Closing bet 🎉