Proposal: ESX as core JS feature

Template tags allow us to effectively embed sublanguages within JavaScript, which is awesome! It helps to enable userland to build all sorts of helpful tooling, not just related to XML/HMTML in JavaScript, but also CSS-in-JavaScript, or I've personally used it to allow you to provide TypeScript like syntax in a template tag, then use that for runtime type validation.

But, there are a couple of major issues, which has already been discussed to a degree, but I'll reiterate them.

  • Syntax highlighting and editor support is difficult within template tags, especially userland defined ones. This is because it can be difficult to statically analyze what tag you're using, and because there's really no way for a userland library to bundle editor tooling with their library.
  • You basically have to bundle an entire language parser with any library that supports this kind of thing, which isn't lightweight. It makes it hard to want to use these kinds of libraries when you know it's adding a fair size to your website's bundle.

I think the optimal solution would be to find ways to fix these issues. Having native support for JSX would be nice, but finding a way to improve tagged templates would be even better.

This means we'll need to solve three issues (not all of which can be solved by TC39).

How can we associate a template tag with certain editor features, like syntax highlighting, snippets, autocomplete, etc?

One option would be to somehow register metadata with a template tag. This could be done in a special comment before the template tag function, that points to, say, a metadata JSON file, like this:

// @@TemplateTagEditorMetadata: ../editor-metadata.json
function jsx(...) { ... }

This metadata file could contain recommended plugins to install (maybe even containing different sets of plugins depending on which editor you're using). Or, it could declaratively contain instructions on how to syntax highlight content that this tag is tagging. Maybe it can also contain useful snippets, or some basic auto-completion instructions. Since the metadata json file is entirely declarative, it should be safe for an editor to automatically follow these instructions.

2. How can we make template tags more statically analyzable?

One option would be to do nothing. If a template tag is exported in one place and imported and used in another, it should be easy enough for an editor to make an attempt at following the import. Maybe by using a @@TemplateTagEditorMetadata comment, you're consenting that you're not going to try to do anything funky with the template tag that would make it hard for an editor to follow a path backwards to the declaration. (Users using the tag would likewise have to not do funcky tricks with the tag, like putting it on the global scope, storing it in another object, etc).

3. Is there a way to reduce the weight of libraries that provide these sorts of template tags?

Here, I'm envisioning that a library could tell your bundling tool how it could go about optimizing template tags.

One possible example would be, perhaps, another metadata comment before your template tag definitions:

// @@TemplateTagCompiler: ../jsx-compiler.js
function jsx(templateTagParts, ...interpolatedValues) { ... }

The file the comment points to can export a function like this:

export function compile(templateTagParts) {
  return <json serialiable data>;
}

When a transpiler runs, whenever it runs into a user of a jsx template tag, it'll pass all of the string pieces of that template tag into the compile() function. The compile function will parse that template tag, look for syntax or semantic errors, and then output some piece of data (e.g. an AST tree). The transpiler can take this final piece of data and embed it inside the the original template tag function, so that the final tag definition would look something like this:

function jsx(templateTagParts, ...interpolatedValues) {
  // The transpiler auto-inserts a line that looks something like this.
  templateTagParts.dataFromCompileStep = { the json serialiable data };
  // Everything else is the same. The rest of this function
  // can access and use `templateTagParts.dataFromCompileStep`
  // if it's there. If not, then the transpiler hasn't run, and it's this
  // function's job to compile the passed-in templateTagParts itself.
  ...
}

What I'm proposing here is, I guess, two separate features. One to somehow standardize editor tooling support for third-party libraries, and one to somehow standardize tooling support to allow expensive and heavy parsing logic to be done once on a developer's machine. And, again, I acknowledge that it's probably outside of TC39's realm to handle this sort of thing. But, this would be my dream path of accomplishing the original feature request.

2 Likes