A discussion around the shortcomings of template tags was started in this thread, which attempts to propose a native JSX syntax, and argues that template tags aren't good enough for this use case. In my mind, this is one major reason why template tags exist - to enable userland to basically embed languages within JavaScript for various reasons. The fact that we're not feeling that template tags are "good enough", and we need to create custom syntax for JSX tell me that there's some improvement that needs to be done in regards to template tags.
I'd like to move discussion around improving template tags to a separate thread, so that thread can just focus on native JSX syntax.
The proposal(s)
(Everything here is copy-pasted from a comment I left in the JSX thread, with only slight modifications).
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 with 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.
This means there's three major hurdles we'll have to overcome if we really want template tags to reach their full potential. (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 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.