My editor components started with a base class and methods along these lines:

protected render() {
  return `<button data-ref="play">Play</button>`;
}

protected sass() {
  return `button { border-radius: .5rem; }`;
}

The base class did the useful housekeeping, creating the shadow root, adding styles and finding references. That part was manageable. Then I wanted to edit a button, and its markup was the return value of a JavaScript method, its styles were another string, and the file kept insisting my HTML and CSS were a class. What bothers me is having to read the class when I'm trying to change the button. Named regions would let me find the markup and styles. The browser still receives a custom element. It's the file I edit that's becoming a format.

A useful file to edit

Here's a simplified example of the format:

<template>
  <button data-ref="play">Play</button>
</template>

<style>
  button { border-radius: .5rem; }
</style>

<script>
  // Component behaviour belongs here.
</script>

The arrangement is familiar from single-file component formats such as Vue's. My tooling has to turn it into the plain component model I was using, so I can edit these regions together.

The browser end can stay fairly boring. Generate an element, attach its shadow root, register the tag. That's where I'd like to stop the invention.

The build now owns the translation. It has to preserve script meaning, keep styles attached to the right component and produce diagnostics that lead back to the source file. Saving some typing gives the compiler a surprising amount of clerical work. File locations, line numbers, which piece owns that style.

A recogniser has to know which region it's reading, because <style> can open a block at the top level while angle brackets can be content inside a quoted template attribute. Context matters.

Right, where does the script end?

That needs a rule before I choose an implementation. If an ordinary JavaScript string contains </script>, an HTML parser can close the script element there even though the JavaScript string hasn't ended. Should my format do the same? Or understand script strings and keep reading? Either answer changes what users may write.

I'd like to pick a recogniser and get on with the button, but choosing one also chooses how it reads that closing tag unless I've made the rule myself. A string with > and a string with </script> ask different questions. Three blocks found. Have I defined which strings their author may write?

The scanner can track the region and hand a script block to a script parser, while a template reader recognises tags, attributes and text. Those readers can live close together. Neither needs to understand both languages.

Now a change to component syntax has a place to live. A new template attribute doesn't have to become the script reader's problem too.

When an attribute gains meaning

Suppose an authoring format introduces a binding attribute. In plain HTML, an unfamiliar attribute is data on an element. In the component compiler, it can become an instruction to connect that element to a value.

Adding a pleasant little attribute has become a larger choice. When should its expression run? What tells the DOM it has changed? What would I tell someone whose expression is invalid? I need answers before the attribute can be useful, even if recognising its spelling is only a few lines.

Each convenience needs examples of its behaviour, including what happens when it goes wrong, because extracting three blocks correctly is a smaller promise than the one I'm making to their author.

So this has become PKC. A format, with a compiler stage to change when I add a template feature. The file has outgrown three strings in a coat.

I can get on with editing the button in a template now. The next convenience I add will come with homework, though. I'll have to decide what the compiler does with it and how to explain it when somebody gets it wrong.

This entry came over from blog.haddon.me. I’ve tidied up the grammar and flow, bringing it closer to how I write now. I used AI to help with the grammar, structure and flow. I have dyslexia, and my writing often comes out as a rambling scrawl, so it can be hard to see what I was actually working on at the time. I think the tidied versions are easier to digest and more interesting to read. The artefacts got the same tidy-up while I was at it.

  • Read next

    Putting up with storage

    Put, get and remove look simple. Checksums, failed uploads and shared readers give me rather more to work through.