lukeed / lukeed/tempura

Custom blocks w/ Children

Open
#3 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
528
Forks
10
PR merge metrics
No merged PRs in 30d

Description

There are two ways to do this:

Option A

Rely on the options.blocks key name(s) to decide whether or not a custom directive is "stateful" / will have a closing tag:

{{#section title="About Me"}}
  <p>Paragraph text</p>

  {{! inline, no-child directive }}
  {{#partial src="other.hbs" }}

  <p>Another paragraph</p>
{{/section}}
// usage
tempura.compile('...', {
  blocks: {
    // section opener
    '#section': args => {
      let txt = '<section>';
      if (args.title) txt += `<h2>${args.title}</h2>`;
      return txt;
    },

    // section closer
    '/section': () => '</section>',

    // does NOT expect children
    // ~> no "/partial" key
    '#partial': async args => {
      // ...
      return 'something';
    }
  }
});

With this approach, the tempura parse will infer whether or not it should expect a {{/NAME}} based on /NAME's existence in the options.block dictionary. In other words, defining a "/partial": args => {} in this example – without modifying the template – would throw:

Expected to close "partial" block; closed "section" instead

Option B

Rely on the template tag itself to determine if this block should be stateful.
Additionally, any stateful blocks would have to return string[] instead of string:

{{#section title="About Me"}}
  <p>Paragraph text</p>

  {{! inline, no-child directive }}
  {{#/partial src="other.hbs" }}

  <p>Another paragraph</p>
{{/section}}
// usage
tempura.compile('...', {
  blocks: {
    // #section block
    // must return string[]
    section(args) {
      let open = '<section>';
      if (args.title) open += `<h2>${args.title}</h2>`;

      return [
        open, //=> #section
       '</section>' //=> /section
      ];
    },

    // #partial block
    // ~> does NOT have children
    // must return string
    async partial(args) {
      // ...
      return 'something';
    }
  }
});

Here, the {{#/partial}} syntax means that it's self-closing, just like a JSX or HTML <input /> element.


I think Option B is a bit sleeker, but both approaches have a potential authoring and/or usage issue. They both require that the author & user of a block are aware of its type, affecting both how it must be defined and how it must be used.

I intentionally don't want a block to work both ways. The rigidity here is fine, but just thinking about which approach may feel better.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the tempura.compile entry point and inspect how custom blocks in options.blocks and the two template syntaxes are parsed. Compare Option A and Option B, then define which behavior and API should be implemented; the issue does not specify a final choice or tests for completion.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.