# Inline Macros

**URL:** <https://es.discourse.group/t/inline-macros/2455>\
**Category:** 💡 Ideas\
**Tags:** proposal\
**Created:** [October 25, 2025, 12:39am UTC](https://es.discourse.group/t/inline-macros/2455 "2025-10-25T00:39:07Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![alshdavid](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alshdavid/32/915_2.png) [@alshdavid](https://es.discourse.group/u/alshdavid)\
**Post date:** [October 25, 2025, 12:39am UTC](https://es.discourse.group/t/inline-macros/2455/1 "2025-10-25T00:39:08Z")

</div>

Disclaimer: No AI was used in the making of this post

# What are Macros?

_Note: the syntax semantics are only an example for demonstration_

Macros are a pre-processing step that allows developers to use templating-like syntax to describe code generation.

For example, an inline macro:

```typescript
let result = add!(1, 1);

```

Prior to evaluation, would expand into:

```typescript
let result = 2;

```

# Why is this needed?

## The Current Landscape

Almost every single non-trivial JavaScript and TypeScript application _already_ uses "macros" (or at least the concept of). These macros are hard coded into the tools they use OR are added into custom compilers;

### React

React, Preact, Solid, or any library that depends on JSX uses a built-in macro available in their transpiler

```typescript
import { createElement } from 'react'
export App = () => <div>Hello World</div>

```

Expands into

```typescript
import { createElement } from 'react'
export App = () => createElement('div', {}, 'Hello World')

```

Due to slight differences in implementation, there was a need to add configuration options like `jsxFactory`.

However, frameworks that want to take a different approach or wish to experiment with alternative approaches must use custom compilers.

### Angular, Vue and Svelte

Angular uses a custom templating system powered by a [custom compiler](https://github.com/angular/angular/tree/main/packages/compiler) and a custom LSP.

Vue and Svelte use their own custom templating systems powered by custom compilers. Both require custom LSPs, and due to requiring a dedicated file extension, require additional tooling to support TypeScript, testing, etc

### Styling Libraries

Styling libraries often use template literals or a combination of custom compilers and template literals to create programmatically defined controlled styling directives.

## How do Macros Help?

### Custom Language Syntax Becomes a Library Concern

Rather than tools like TypeScript's `tsc`, `babel`, `swc`, `oxc`, etc implementing expansion of jsx syntax and all other custom syntax formats (angular, vue, svelte, styling libs) requiring an additional bundler layer configured with custom compilers/loaders - the transformations for these libraries could be defined and exported _by the libraries themselves_

For example jsx could be described _inside a normal `.js` file_ as:

```javascript
import {jsx} from 'react'
const App = () => jsx!{<div>Hello World</div>}

```

Other frameworks/libraries could also leverage the same capability within `.js` files as:

```javascript
import {vue} from 'vue'

export const MyComponent = function () {
  return {
    template: vue.html!{ 
      <div>{this.data.value}</div> 
    },
    data: { 
      value: 'Hello World' 
    },
  }
}

```

This would _drastically_ reduce the amount of tooling required to create a dynamic web application and allow the tools to distribute their custom transformations directly.

### Custom Compilers and Build Tools have Short Shelf Lives

Many of the tools re-implement solutions to problems solved by other tools. The tools have high maintenance burdens and, unless they have large budgets, often have short lifespans. This means developers have to frequently hop from tool to tool without any material benefit to their workflow.

### Running Code in the Browser and/or Runtime Directly

Currently, none of these custom implementations can be run directly in a JavaScript runtime without first requiring the pre-processing step.

### Examples of the need for Macros

There are efforts to incorporate these custom transformations using tagged template literals, various `eval` hacks or baking external tooling into the runtime:

- [GitHub - developit/htm: Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support.](https://github.com/developit/htm)
  - Great for prototyping, however the code cannot be reused for production and will need to be rewritten when production use cases are targeted

- [JSX Support · Issue #56822 · nodejs/node · GitHub](https://github.com/nodejs/node/issues/56822)
  - While JSX support in Nodejs would help, anyone using additional tools (like styling libraries) would inevitably fall back to needing an external preprocessor
  - This code could not run symmetrically in the browser

- [GitHub - vuejs/petite-vue: 6kb subset of Vue optimized for progressive enhancement](https://github.com/vuejs/petite-vue)
  - Compiles Vue templates in the browser however requires the use of unsafe `eval` and any code written to target this would need to be rewritten to target production

- [GitHub - tc39/proposal-decorators: Decorators for ES6 classes](https://github.com/tc39/proposal-decorators)
  - Decorators aim to solve a similar problem however they are incomplete, difficult to use, cannot be compiled AOT and are only available on classes and class properties.

These approaches reimplement existing tools and are either unsafe or not-reusable/non-portable.

Having first class support in the EcmaScript specification for inline macros would consolidate _all_ of these efforts

### Compile Time Versus Run Time Evaluation

Macros are typically thought of as a pre-processing step done Ahead-Of-Time by a compiler. EcmaScript is different in that it's an evaluated language.

Having macros defined within the EcmaScript specification allows these pre-processing steps to be evaluated at runtime OR expanded/optimized by compilers in a build step prior to runtime.

This has the advantage of allowing source code to be use symmetrically at both run time and during the development loop.

### Examples of Macros used for These Exact Use Cases

Here are some examples where the use of inline macros enabled the rapid experimentation and development of GUI frameworks and utilities _without_ the need for an external transpiler.

- [1] Moved to comment due to links limit
  - Rust wasm framework, similar to Vue, that adds html support into the language _without_ an external transpiler

- [2]
  - Rust wasm framework, similar to React, that adds jsx-like support to the language _without_ an external transpiler

- [3]
  - Rust native desktop framework that has a Flutter-like syntax to define elements/styles _without_ an external transpiler

- [4]
  - Rust library that adds inline json support to the language _without_ an external transpiler

- [5]
  - Rust library that adds inline xml support to the language _without_ an external transpiler

- ...etc

If a developer wanted to experiment with a new/novel framework - they could just write it within the language and would not face the cliff of needing to build, document and maintain an external transpiler, LSP and integration tooling.

This lack of friction empowers developers/tool makers and fosters competition within the ecosystem which is highly valuable.

## Macro Syntax

I won't dive into this now as I'd like to discuss the inclusion of macros into EcmaScript before getting into the technicals.

That said, there are a few key points.

- Macros must be expandable statically AOT by native transpilers (like `swc` `oxc`) and therefore should not use ES syntax in their definition
- Macros must not conflict with existing ES syntax

Below is a example of a macro syntax that meets point 1 (I am aware of the conflict of using `!`)

This is just an example based on Rust's inline macro templating system. If progressed, something similar would be defined in a more ES idiomatic way.

This is an inline macro that adds two numbers at at expand-time.

```typescript
macro_rules! add {
    ($a:expr, $b:expr) => {
        $a + $b
    };
}

const result = add!(1, 1)

```

**... That looks complicated**

It is, but so is maintaining a transpiler, LSP and multiple bundler plugin/loaders for that custom transpiler - which is what we do today any way.

Ultimately, macros are intended to be used by framework/library maintainers/builders as a means to reduce complexity for consumers of those libraries. Outside of edge cases, it's not something most developers would actually write themselves.

## Procedural Macros vs Inline Macros

There would perhaps need to be two proposals to incorporate both of these elements.

### Inline Macros

Inline macros are simple "pure" templates that take arguments and are replaced by the result of their macro syntax

For example, I could write a library that does this conversion:

Users write:

```typescript
import {css} from 'magic-styles';

const className = css!{ color: red; };
document.querySelector('#my-elm').classList.add(className);

```

Expands into:

```typescript
import {css} from 'magic-styles'; // Can be stripped by dead-code elimination

const className = (() => {
  const className = "xyz123"
  const elm = document.createElement('style')
  elm.innerHtml = `.${className} { color: red; }` 
  document.head.appendChild(elm)
  return className
})();

document.querySelector('#my-elm').classList.add(className);

```

### Procedural Macros (A Later Discussion)

I don't want to dive too deeply into this in this discussion, but I wanted to share as it's part of the larger vision.

Procedural Macros are similar to decorators, but can be applied to variable declarations, functions, classes, class members, etc, and augment the behavior of the target.

For example; Frameworks like Vue, Angular, Mobx, etc currently use complex combinations of compiler tricks and/or runtime libraries like signals to give users ergonomic access to detect state mutations and automatically trigger rendering calculations.

Procedural macros would grant both tool makers and developers alternative and/or simpler approaches.

For example, a procedural macro that in-place converts class properties into getters/setters;

```typescript
import {reactive, subscribe} from 'reactive'

#[reactive.class]
class Foo {
  #[reactive.push]
  bar = 'Initial Value'

  constructor() {
    // This is difficult (or impossible?) to replicate with decorators
    setTimeout(() => this.bar = 'Updated', 1000)
  }
}

const foo = new Foo()
subscribe(foo, () => console.log('updated'))

```

Would expand into something like

```typescript
import {subscribe} from 'reactive'

class Foo {
  [Symbol.for('reactive-state')] = {
    emitter: new EventTarget(),
    bar: 'Initial Value',
  }

  get bar() { return this[Symbol.for('reactive-state')].bar}
  set bar(update) { 
    this[Symbol.for('reactive-state')].bar = update
    this[Symbol.for('reactive-state')].emitter
      .dispatchEvent(new CustomEvent('reactive:update'))
  }

  constructor() {
    setTimeout(() => this.bar = 'Updated', 1000)
  }
}

const foo = new Foo()
subscribe(foo, () => console.log('updated'))

```

There are many other use cases, like simplified serialization/deserialization and creating RPC wrappers for instances located in Worker threads or external processes - however the key is that these implementation are done in user-land and can be compiled/optimized AOT by bundlers

# Alternatives Considered

- Using an external macro compiler
  - [6]
  - [7]

- Adding this into TypeScript
  - [8]
  - [9]

The TypeScript team reject the idea of adding macros unless it's an EcmaScript feature (fair enough, it makes sense given the project goals).

Support for custom TypeScript transformers is going away with ts-go (see tool's have short shelf lives).

An external macro-aware compiler requires a custom LSP as well as documenting and maintaining plugins/loaders for bundlers.

---

<div class="post-metadata">

**Author:** ![alshdavid](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alshdavid/32/915_2.png) [@alshdavid](https://es.discourse.group/u/alshdavid)\
**Post date:** [October 25, 2025, 12:40am UTC](https://es.discourse.group/t/inline-macros/2455/2 "2025-10-25T00:40:13Z")

</div>

Missing links due to link post limit for new users:

- [1] [yew/examples/counter/src/main.rs at 71e24b7e811cde10974f6f35f9743df93f173459 · yewstack/yew · GitHub](https://github.com/yewstack/yew/blob/71e24b7e811cde10974f6f35f9743df93f173459/examples/counter/src/main.rs#L39)
- [2] [leptos/examples/counter/src/lib.rs at 0edbd9b3b5e2745d9bea9949c11ea056e9dc83b3 · leptos-rs/leptos · GitHub](https://github.com/leptos-rs/leptos/blob/0edbd9b3b5e2745d9bea9949c11ea056e9dc83b3/examples/counter/src/lib.rs#L15)
- [3] [Relm4/examples/data\_binding.rs at 710da58a939c1e6f11dac666028e9f5feb5a4211 · Relm4/Relm4 · GitHub](https://github.com/Relm4/Relm4/blob/710da58a939c1e6f11dac666028e9f5feb5a4211/examples/data_binding.rs#L27)
- [4] [json in serde\_json - Rust](https://docs.rs/serde_json/1.0.145/serde_json/macro.json.html)
- [5] [inline\_xml - Rust](https://docs.rs/inline-xml/latest/inline_xml/)

---

<div class="post-metadata">

**Author:** ![alshdavid](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alshdavid/32/915_2.png) [@alshdavid](https://es.discourse.group/u/alshdavid)\
**Post date:** [October 25, 2025, 1:09am UTC](https://es.discourse.group/t/inline-macros/2455/3 "2025-10-25T01:09:26Z")

</div>

- [6] [GitHub - GoogleFeud/ts-macros: A typescript transformer / plugin that allows you to write macros for typescript!](https://github.com/GoogleFeud/ts-macros)
- [7] [Macros](https://parceljs.org/features/macros/)
- [8] [A minimal custom transformer plugin proposal · Issue #54276 · microsoft/TypeScript · GitHub](https://github.com/microsoft/TypeScript/issues/54276)
- [9] [[Feature Request] Rust-Like Macros that generate TypeScript (again) · Issue #60645 · microsoft/TypeScript · GitHub](https://github.com/microsoft/TypeScript/issues/60645)

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [October 25, 2025, 1:05pm UTC](https://es.discourse.group/t/inline-macros/2455/4 "2025-10-25T13:05:24Z")

</div>

Hi! I'm the macro guy here, I think. I have a very detailed proposal about how to bring macros into JavaScript.

You mention a number of real problems with maintenance burden, and I agree wholeheartedly that maintenance burdens in our ecosystem have, in aggregate, spiralled out of control. It's becoming a problem especially rapidly now with multiple major tooling forks in Rust and Go, meaning that where before JSX was just tool factoring debt that existed in one set of tools, now many sets of tool maintainers have copied the debt and the ecosystem is bearing highly multiplied maintenance costs.

JSX is everywhere already so I think that to be able to clean up the mess we already made we have to find a way to be able to describe the reality of how things are already working. For me that means that i prefer to think of what's needed as language extension instead of macros, because JSX is, in practice, a custom language extension.

The debt that we owe is that tools now consider it pseudo-standard, and implement it as part of their core definition of JavaScript. They do this because there's is no other mechanism for them to use to support that extension. There could be such a mechanism in the future though! That's why I've chosen to focus my work on extensible parsers like [this es3 parser](https://github.com/bablr-lang/language-en-es3/blob/09064392594b79a97ff6919ab6ddd42fff3c36c4/lib/grammar.js) which we later [extend to es5](https://github.com/bablr-lang/language-en-es5/blob/814ea3f65121a8f8b1a53cf0accf0317a51f29a6/lib/grammar.js) and [then to es6](https://github.com/bablr-lang/language-en-es6/blob/db2cbc83b573099a0a37fe99ff0b6fd7830346f1/lib/grammar.js) and beyond. We [already support JSX as a higher-order language](https://github.com/bablr-lang/language_enhancer-en-jsx/blob/46cffcbe10f694e67a35dfbac98f013ea070aee1/lib/grammar.js)!

---

<div class="post-metadata">

**Author:** ![alshdavid](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alshdavid/32/915_2.png) [@alshdavid](https://es.discourse.group/u/alshdavid)\
**Post date:** [October 26, 2025, 10:34pm UTC](https://es.discourse.group/t/inline-macros/2455/5 "2025-10-26T22:34:31Z")

</div>

> JSX is everywhere already so I think that to be able to clean up the mess we already made we have to find a way to be able to describe the reality of how things are already working.

The benefit of my proposed macros approach is that it provides library makers a backwards compatible means to migrate their implementations to macros without breaking support for existing bundler configurations.

Macros can also coexist so you can have React, Preact and Vue in the same project _without_ a transpiler

```typescript
import {jsx} from 'react/macros'
import {jsx as pjsx} from 'preact/macros'

const ReactApp = jsx!(<div>Hello World</div>)
const PreactApp = pjsx!(<div>Hello World</div>)

```

Realistically, if a macro system was integrated into ES, I'm certain a JSX macro would be developed within hours. Such a macro would be compatible with React _without changes to React_.

It's also worth mentioning that JSX debt only affects React and React-like libraries. There are many other libraries (and prototypes that couldn't get off the ground) which would be able to adopt the new capability afforded by macros immediately - as well as non-gui use cases (e.g. a comlink-fork, serialization/deserialization libraries, and any library that uses decorators)

> JSX is everywhere already so I think that to be able to clean up the mess we already made we have to find a way to be able to describe the reality of how things are already working

Personally, I'd caution against doubling down on the existing debt with a closed approach that is tailored to _one specific GUI library_.

> i prefer to think of what's needed as language extension instead of macros

I'm assuming you mean there is an API exposed to allow extending the language parser at runtime (otherwise it would just be adding jsx to the language specification)

Something like:

```typescript
import {JSX} from 'react/parser'
globalThis.engine.extendParser(JSX.parser({ pragma: "createElement" }))

```

The biggest issue I see with an extensible parser is the complexity and performance impacts associated with running it.

To run in the browser, you'd need to supply the parser extension to the browser. To run in a bundler, you'd need to supply the parser extension to webpack, rspack, parcel, rollup, swc, tsc, oxc - which begs the question of what language the parser extension will be written in.

If a parser extension is written in JavaScript - then native transpilers (ts-go, swc, oxc) will not be able to use it. swc and oxc might be able to embed a v8 runtime to consume the parser extension, but then they would need expensive mapping logic. ts-go on the other hand simply couldn't consume extensions written in JavaScript.

Native transpilers also need to identify the custom parser before using it. Which begs the question of control flow. When do you declare a custom parser? What happens before it's declared? What happens inside of Workers or iframes?

By contrast, static macro syntax can be implemented in v8 as native code - which means it can be highly efficient as it's a simple static language expansion (basically handlebars). Extensions supplied at runtime have to be in JavaScript, which would be slow (important for Nodejs targets that would use them during testing).

Additionally, what happens if you need to use multiple custom parsers? For example, you are writing Vue and need templates and the ability to modify class properties to be getters/setters - but you also use React because you're migrating. Or perhaps you are using React and migrating to Preact.

The benefit of macros are they are a generic method to open the language to any extension which would evolve at the pace of the implementer (rather than TC39). This is evidenced in how Rust, despite being a systems language, has support for a React-like GUI library and a Vue-like GUI library without the use of a transpiler. The design of their macro system opened the language up to creativity of library implementers and amazing solutions were developed without the need to involve their standards committee or use any external tooling.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [October 27, 2025, 1:33pm UTC](https://es.discourse.group/t/inline-macros/2455/6 "2025-10-27T13:33:45Z")

</div>

To be clear, I'm not against macros. I'm both a user and one of the maintainers for `babel-plugin-macros`, so I'm pretty well aware of how nice it is to be able to scope transformations to particular syntax, and to make the macro transformations explicit and discoverable in the process.

I reiterate what I said however: the focus should be on _being able to describe the code we have already written._ We've probably got hundreds of millions of files out there using non-macro JSX as well as other nonstandard syntactic extensions like Typescript types or CSS-in-JS imports. To me that's a major debt: a lot of code that belongs to our ecosystem that we just don't comprehend well enough to be able to run. People wish they could just run their code!

I suspect we'll eventually support scoped macros like `jsx!()` and your example is a succinct display of a real purpose they might be put to, but the vast majority of JSX users aren't going to want to write `jsx!()` around every single JSX tag in their application. They're not going to want to do it because it would cost them something (ease of reading and writing their code) and they wouldn't get anything in return except for what they already had. For most real applications writing `jsx!()` repeatedly would only really be visual noise making it harder to scan the code to quickly grok its purpose.

You mentioned:

> I'm assuming you mean there is an API exposed to allow extending the language parser at runtime (otherwise it would just be adding jsx to the language specification)

You assume correctly. Wouldn't your idea also require a mechanism like this though? After all, you used nonstandard JSX syntax in the source file, so now as far as I can tell you still need a custom parser to be able to parse the source file. There are nearly no limitations on what kind of crazy syntax can go between JSX tags, including arbitrary JS expressions which may be interpolated. If you don't give the `jsx` macro control of the runtime's parser, you're going to have to make draconian restictions like "the character `)` is reserved inside macros" as that would be the only way the parser could tell when the expandable macro is over.

I agree with most of your analysis of the difficulties involved in parser extension, I just think that that work will be absolutely necessary both for macros and to be able to support the code we've already written.

---

<div class="post-metadata">

**Author:** ![alshdavid](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alshdavid/32/915_2.png) [@alshdavid](https://es.discourse.group/u/alshdavid)\
**Post date:** [October 27, 2025, 10:18pm UTC](https://es.discourse.group/t/inline-macros/2455/7 "2025-10-27T22:18:37Z")

</div>

Thanks for the response! Hope I'm not overly interrogating your ideas, I appreciate the sparring as you've obviously thought deeply about the subject for a lot longer than I have.

> the focus should be on _being able to describe the code we have already written._ We've probably got hundreds of millions of files out there using non-macro JSX.

That's a fair point. I can't back this up but my gut tells me to trust in the adaptability and competency of developers using the language. Plus moving jsx to an inline parameterized macro is pretty easy to automate via codemod - and the benefits are tangible enough to justify the migration.

> as well as other nonstandard syntactic extensions like Typescript types or CSS-in-JS imports

That's true. Macros can't solve css-in-js ES imports. It targets use cases like css-in-js (like styled-components).

In Rust, they do have a mechanism for macros to work with files

```javascript
let foo = css!("./path/to/file.css"); // Internally calls fs.read_to_string(path)
// Or
let bar = css!{ color: blue; };

```

But that would mean macros need access to `fetch` (not in the spec) to load the css file as text. Then you need to worry about macros fetching remote files and how transpilers would work with file loading.

> For most real applications writing `jsx!()` repeatedly would only really be visual noise making it harder to scan the code to quickly grok its purpose.

I can see this point, my gut tells me that it's too subjective to quantify. This doesn't really occur in the Rust world as macros overall reduce the verbosity of code.

I can't speak for all but personally I value visual traceability, having `import {jsx} from 'react/jsx'` in scope is unambiguous - by comparison a `.jsx` or `.tsx` file requires me to interrogate the `tsconfig` and the bundler configuration (which might be out of sync).

That said, macros wouldn't replace bundlers, they'd simply reduce the demands on the pre-processors as much of the transformations can be described within the libraries themselves.

Also, it's not that ugly 😂 Here's jsx in rust:

```javascript
fn render_greeting(name: &str) -> DOMNode {
  let stylesheet = css!("example.css");

  return rsx! {
    <text style={stylesheet.take(".text")}>
      { greeting_str(name) }
    </text>
  };
}

```

> Wouldn't your idea also require a mechanism like this though?

Hmm.. great point. You're right, they totally would.

While we'd have the ability to use a simple templating syntax for some simple inline macros - non-trivial macros (like [css, jsx, html](https://github.com/victorporof/rsx?tab=readme-ov-file#how-to-use), etc) would need to be expanded programmatically which necessitates using JavaScript to parse and reorganize tokens.

That alleviates the issue of how/where to "register" a parser (as the implementation is just imported at the call site) - but it does present new challenges;

1. Macros can't have access to globals like `localStorage` or anything set by the user, etc.
2. Native transpilers need to identify macros and run them inside of a JavaScript runtime.

So I guess that naturally leads us to requiring the isolation of macros (I believe parsers would share this challenge) to running within their own lightweight restricted contexts, which then requires the addition of some kind of sandboxed `ParserWorker`/`MacroWorker`.

So library authors would need to write something like

```javascript
// react/jsx.js
export const jsx = MacroRegistry.registerCallable('./react_jsx.js')

// app.js
import {jsx} from 'react/jsx' 
const App = () => jsx!(<div>Hello World</div>)

```

The good news is that, if the isolated environment _only_ features ES syntax, native transpilers can essentially use an unmodified [quickjs](https://github.com/quickjs-ng/quickjs) - which has bindings for Go and Rust.

The bad news it that it's difficult for transpilers to statically identify usage.

Tricky tricky.

Perhaps, similarly to `import`, it's enough for the ES spec to define how to register a macro and how to call a macro, but not how the macro is evaluated. The runtime can then determine it wants to spawn worker threads, give them `fetch` and load balance between them

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [November 6, 2025, 7:21pm UTC](https://es.discourse.group/t/inline-macros/2455/8 "2025-11-06T19:21:18Z")

</div>

I don't quite understand, why are tagged template literals not a sufficient solution for the presented use cases?

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [November 6, 2025, 9:51pm UTC](https://es.discourse.group/t/inline-macros/2455/9 "2025-11-06T21:51:49Z")

</div>

I can't speak for anyone else but I'm here to advocate for something much richer than just macros, which is a method for parser extension.

A method for parser extension would allow existing JSX and Typescript code to become valid according to the standards again, healing a tremendous wound in the ecosystem caused when a plurality of people started writing code in `.ts` files which are completely opaque to TC39's standards and so to any standard interpreter.

I don't think it's my job to second-guess that people want to be able to extend the parser. They've demonstrated overwhelmingly that they want to extend it -- that they will extend it.

What I will advocate fiercely for is to create a level playing ground for technologies. I object to the idea that runtimes like Deno or Bun should be baking in specific support for `.ts` files and JSX. In practice that makes these syntax extensions have the weight and force of standards, while new or competing syntax extensions are nearly impossible to make since they involve adding support to every single parser -- tens if not hundreds. If it's inevitable that people are going to extend the parser, there should be a standard mechanism that ensures that access to the opportunity is equitable and can't be used as leverage to take control away from TC39 and can't be used as a cudgel by entrenched technologies to freeze out upstarts.

---

<div class="post-metadata">

**Author:** ![alshdavid](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alshdavid/32/915_2.png) [@alshdavid](https://es.discourse.group/u/alshdavid)\
**Post date:** [December 4, 2025, 3:54am UTC](https://es.discourse.group/t/inline-macros/2455/10 "2025-12-04T03:54:06Z")

</div>

> I don't quite understand, why are tagged template literals not a sufficient solution for the presented use cases?

Tagged template literals often depend on `eval` which disqualifies them for the majority of non-trivial web applications.

For instance, imagine this Vue-like framework;

```javascript
import html from 'framework'

const MyComponent = {
  template: html`
    <div>{{ self.i }}</div>
    <button on:click="(self) => self.increment()">
      Increment
    </button>
  `,
  data: () => ({
    i: 0
  }),
  increment(self) {
    self.i += 1
  }
}

```

While the AST of the template can be resolved, the `on:click` directive must be `eval`'d. Handling the template binding would also likely require `eval`.

A solution could be to pass in values like this:

```javascript
import html from 'framework'

const MyComponent = {
  template: html`
    <div>${(self) => self.i}</div>
    <button on:click="${(self) => self.increment()}">
      Increment
    </button>
  `,
  data: () => ({
    i: 0
  }),
  increment(self) {
    self.i += 1
  }
}

```

However the ergonomics of the framework suffer, as well as the complexity associated with compiling the template literal AOT (for performance) and the implementation of things like directives.

Additionally, rather than using macros to transform properties into getters/setters for reactivity, we must rely on mutation of data properties at runtime, which is inefficient.

> I can't speak for anyone else but I'm here to advocate for something much richer than just macros, which is a method for parser extension.

My confusion with this approach is that it's essentially allowing _any_ syntax to be interpreted by the browser - making ecmascript, itself, a runtime for other languages.

For instance, you could write a parser for;

- TypeScript
- Vue
- Rust
- Dotnet
- etc

At that point, what are we achieving? Running other languages in ecmascript? Why not just put effort into advocating for wasm, where the same objective would be achieved more naturally.

By contrast, the concept of procedural and inline macros essentially expands upon the existing concept of decorators, doesn't fundamentally change ecmascript, and is a pattern validated by successful usage in another language.

It's about giving JavaScript the ability for code generation - which can be used symmetrically on the client and AOT by tooling - allowing for greater competition in the web development tooling space.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [December 4, 2025, 1:56pm UTC](https://es.discourse.group/t/inline-macros/2455/11 "2025-12-04T13:56:29Z")

</div>

Ecmascript _is already a runtime for other languages_. Think of that what you will, but it is not within your power to change nor mine. People have voted with their feet: they write Typescript. They extend the syntax of the language.

I cannot bring myself to find any kind of existential panic in the idea that many different syntaxes are written and all run on the JS runtime when that's just a level-headed description of the current reality.

I also refuse to solve this problem in a way that only brings 1 or 2 of those projects in from the cold while leaving the others frozen out. I believe that is my responsibility and the responsibility of anyone who safeguards the neutrality of the standards. The list of compile-to-JS languages [is long](https://github.com/jashkenas/coffeescript/wiki/List-of-languages-that-compile-to-JS) and only ever getting longer.

There's one last crazy thing, which is that I don't need TC39 at all to make this happen. I already wrote the code and designed the APIs. I'm just polishing everything up to standards quality, something which the standards committee has shown 0 interest in. I mention this to say: the only person you need to convince of anything is me. TC39 has no more power to stop me than they had power to stop Typescript or JSX taking off.

---

<div class="post-metadata">

**Author:** ![alshdavid](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alshdavid/32/915_2.png) [@alshdavid](https://es.discourse.group/u/alshdavid)\
**Post date:** [December 4, 2025, 9:46pm UTC](https://es.discourse.group/t/inline-macros/2455/12 "2025-12-04T21:46:08Z")

</div>

> Ecmascript _is already a runtime for other languages_ .

IMO this is a symptom that non-trivial web applications require more capabilities than Ecmascript can provide. To me, that's not an indication that the language needs a runtime hook to support the ability to (essentially) evaluate other languages.

In the context of the web, if Ecmascript is destined to be a compiler target - then the web should implement a natural compiler target (which was the original goal of wasm almost a decade ago).

TBH, given the state of wasm today, it would probably just require a half decent browser FFI ([basically, just include this in the browser by default](https://github.com/devongovett/napi-wasm/blob/bbde4dc54b23d56889e4ca8a23704424df7ae05e/index.mjs)) + a few additions like threads support and a better way to bootstrap modules.

That said, this is a discussion on an Ecmascript language feature that enhances the language to solve common use cases through a pattern validated by and used extensively in other languages.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [December 4, 2025, 11:28pm UTC](https://es.discourse.group/t/inline-macros/2455/13 "2025-12-04T23:28:18Z")

</div>

This is the model **our** ecosystem has already a) validated and b) overwhelmingly chosen...

Healing the rift that JSX and TS brought to the JS ecosystem (so they can run their code without compiling it) is measurably the single thing people want most

Here's the kicker: even if you wanted to shift 10 trillion lines of TS and JSX code to whatever totally new thing you come up with, you'd need an automated way to rewrite 10 trillion lines of code from nonstandard JS syntax down to basic/standard syntax, which is to say you'd have to be able to parse nonstandard syntax from within the standards environment.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [December 4, 2025, 11:34pm UTC](https://es.discourse.group/t/inline-macros/2455/14 "2025-12-04T23:34:49Z")

</div>

I don't want to hijack the ESX thread to be about generic parser extension. In that thread you put up this example:

```javascript
import {yaml} from 'yaml'

const myYaml = yaml! {
  items:
    - "This is yaml"
    - "It is in Ecmascript" 
}

```

You continue to make proposals that can only work if the JS parser can be extended at runtime (like this example!), and then you argue with me, the person making that possible, saying it shouldn't be possible. I am left scratching my head.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [December 4, 2025, 11:40pm UTC](https://es.discourse.group/t/inline-macros/2455/15 "2025-12-04T23:40:49Z")

</div>

To use your own exact argument against you:

> this is a symptom that non-trivial web applications require more capabilities than Ecmascript can provide

How does this not apply exactly as much to your macro implementation of JSX and YAML? It seems to me that by your logic any need to extend the parser (as your examples do) would be requiring "more capabilities than Ecmascript can provide," which you have indicated you see as grounds for non-consideration
