Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for TC39 Explicit Resource Management (using/await using syntax) #1528

Open
aidenlx opened this issue Mar 21, 2025 · 7 comments
Open
Labels
upstream Issue related to an upstream library

Comments

@aidenlx
Copy link

aidenlx commented Mar 21, 2025

Feature Request

Please support the (await) using syntax from the TC39 Explicit Resource Management proposal. Currently, attempting to use this syntax results in the error: This experimental syntax requires enabling the parser plugin: 'explicitResourceManagement'.

Is your feature request related to a bug?

N/A

What are the alternatives?

Alternative solutions I've considered:

  1. Manually configuring Babel with the @babel/plugin-syntax-explicit-resource-management plugin, but I'm unsure how to properly integrate this with the wxt framework.
  2. Using traditional try/finally blocks or other resource management patterns, but these are more verbose and less readable than the new syntax.

Additional context

The Explicit Resource Management proposal (https://github.com/tc39/proposal-explicit-resource-management) is gaining significant adoption.

Current implementation status (from tc39/proposal-explicit-resource-management#242):

  • TypeScript has supported it since version 5.2
  • Babel has supported it since approximately version 7.20.0
  • esbuild (which powers Vite) specifically added support in version 0.18.7
  • V8 has added support in Chrome 134
  • Mozilla's SpiderMonkey engine is actively working on implementation
  • Moddable XS implemented both sync and async versions in 2023
  • Webkit has a meta issue but has seen no movement and is currently unassigned (as of Feb 2025)

As this syntax is becoming standard across the JavaScript ecosystem, it would be valuable for wxt to support this modern language feature that improves resource management patterns.

@aklinker1
Copy link
Collaborator

Can you share a reproduction? I would have thought that this already works.

@aidenlx
Copy link
Author

aidenlx commented Mar 21, 2025

here is the repro: https://github.com/aidenlx/using-test
I've included disposable in both backgroud script and content script

when running pnpm dev, it throws:

[22:19:16]  ERROR  This experimental syntax requires enabling the parser plugin: "explicitResourceManagement". (entrypoints/content.ts:6:9)

4  |    matches: ["*://*.google.com/*"],
5  |    main() {
6  |            using testDisposable = new TestDisposable();
   |                ^
7  |            testDisposable.print();
8  |            console.log("Hello content.");

@aklinker1
Copy link
Collaborator

Alright, so, this is an upstream issue with magicast. WXT uses it to remove the main function from content script and background entrypoints:

$ wxt build --debug
...
⚙ vite-node transformed entrypoint /Users/aklinker1/Development/local/wxt-issue-1528/entrypoints/content.ts
⚙ Original:
---
// import TestDisposable from "./disposable";

export default defineContentScript({
  matches: ["*://*.google.com/*"],
  main() {
    // using testDisposable = new TestDisposable();
    // testDisposable.print();
    // console.log("Hello content.");
  },
});

---
⚙ Transformed:
---
// import TestDisposable from "./disposable";

export default defineContentScript({
  matches: ["*://*.google.com/*"],
});
---
...

Here's the actual code that does this, you can see it uses magicast: https://github.com/wxt-dev/wxt/blob/259cec9ea8c18886249f5613fa42756071eb7206/packages/wxt/src/core/builders/vite/plugins/removeEntrypointMainFunction.ts

And here's magicast's list of babel plugins in use:

https://github.com/unjs/magicast/blob/50e2207842672e2c1c75898f0b1b97909f3b6c92/src/babel.ts#L34-L74

You should open a PR over there adding the explicitResourceManagement plugin to that list.


If you don't want to open a PR, the simple change is to not use using inside entrypoints' directly. Since WXT only uses magicast on entrypoints/* and entrypoints/*/index.ts, you can use it anywhere else in your project. It's a workaround, but you could do something like this:

// entrypoints/content/index.ts
import { main } from './main';

export default defineContentScript({
  // ...
  main,
});
// entrypoints/content/main.ts
import { ContentScriptContext } from 'wxt/client';

export function main(ctx: ContentScriptContext) {
  using disposable = ...;
}

@aklinker1 aklinker1 added upstream Issue related to an upstream library and removed feature labels Mar 21, 2025
@aidenlx
Copy link
Author

aidenlx commented Mar 21, 2025

Thank you for the quick and detailed explanation! Since magicast's development seems to be on pause currently (no commits in the last 8 months), I'll use the workaround you suggested by moving my code with the using syntax out of the direct entrypoint files. This is a practical solution while we wait for potential updates. Deeply appreciate your help!

@aklinker1
Copy link
Collaborator

aklinker1 commented Mar 21, 2025

We could consider dropping magicast (and potentially any other dependencies that depend on babel) and use oxc-parser instead to modify the AST.

Edit: Looks like the OXC team needs to publish an napi binding for oxc_codegen: oxc-project/oxc#6854

@aidenlx
Copy link
Author

aidenlx commented Mar 24, 2025

Additional React Component Compatibility Issue

I've discovered that the using syntax compatibility issue extends to React components as well. When attempting to use this syntax within React components (not just in entrypoints), I encounter a similar parser error from the vite:react-babel plugin:

the repro is updated with react preset: https://github.com/aidenlx/using-test

2:17:03 PM [vite] (client) Pre-transform error: /Users/aidenlx/repo/using-test/entrypoints/popup/App.tsx: Support for the experimental syntax 'explicitResourceManagement' isn't currently enabled (50:2):

  48 |
  49 | function queryDatabase() {
> 50 |  using database = createDatabase();
     |  ^
  51 |  return database.query();
  52 | }
  53 |

If you already added the plugin for this syntax to your config, it's possible that your config isn't being loaded.
You can re-run Babel with the BABEL_SHOW_CONFIG_FOR environment variable to show the loaded configuration:
        npx cross-env BABEL_SHOW_CONFIG_FOR=/Users/aidenlx/repo/using-test/entrypoints/popup/App.tsx <your build command>
See https://babeljs.io/docs/configuration#print-effective-configs for more info.

  Plugin: vite:react-babel
  File: /Users/aidenlx/repo/using-test/entrypoints/popup/App.tsx:50:1
  48 |  
  49 |  function queryDatabase() {
  50 |          using database = createDatabase();
     |   ^
  51 |          return database.query();
  52 |  }
2:17:03 PM [vite] Internal server error: /Users/aidenlx/repo/using-test/entrypoints/popup/App.tsx: Support for the experimental syntax 'explicitResourceManagement' isn't currently enabled (50:2):

  48 |
  49 | function queryDatabase() {
> 50 |  using database = createDatabase();
     |  ^
  51 |  return database.query();
  52 | }
  53 |

If you already added the plugin for this syntax to your config, it's possible that your config isn't being loaded.
You can re-run Babel with the BABEL_SHOW_CONFIG_FOR environment variable to show the loaded configuration:
        npx cross-env BABEL_SHOW_CONFIG_FOR=/Users/aidenlx/repo/using-test/entrypoints/popup/App.tsx <your build command>
See https://babeljs.io/docs/configuration#print-effective-configs for more info.

  Plugin: vite:react-babel
  File: /Users/aidenlx/repo/using-test/entrypoints/popup/App.tsx:50:1
  48 |  
  49 |  function queryDatabase() {
  50 |          using database = createDatabase();
     |   ^
  51 |          return database.query();
  52 |  }
      at toParseError (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parse-error.ts:95:45)
      at TypeScriptParserMixin.raise (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/tokenizer/index.ts:1497:19)
      at TypeScriptParserMixin.expectPlugin (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/tokenizer/index.ts:1551:16)
      at TypeScriptParserMixin.parseStatementContent (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:529:14)
      at TypeScriptParserMixin.parseStatementContent (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/plugins/typescript/index.ts:3091:20)
      at TypeScriptParserMixin.parseStatementLike (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:437:17)
      at TypeScriptParserMixin.parseStatementListItem (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:386:17)
      at TypeScriptParserMixin.parseBlockOrModuleBlockBody (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:1403:16)
      at TypeScriptParserMixin.parseBlockBody (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:1376:10)
      at TypeScriptParserMixin.parseBlock (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:1344:10)
      at TypeScriptParserMixin.parseFunctionBody (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/expression.ts:2558:24)
      at TypeScriptParserMixin.parseFunctionBodyAndFinish (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/expression.ts:2527:10)
      at TypeScriptParserMixin.parseFunctionBodyAndFinish (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/plugins/typescript/index.ts:2573:20)
      at callback (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:1650:12)
      at TypeScriptParserMixin.withSmartMixTopicForbiddingContext (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/expression.ts:3090:14)
      at TypeScriptParserMixin.parseFunction (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:1648:10)
      at TypeScriptParserMixin.parseFunctionStatement (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:1036:17)
      at TypeScriptParserMixin.parseStatementContent (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:480:21)
      at TypeScriptParserMixin.parseStatementContent (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/plugins/typescript/index.ts:3091:20)
      at TypeScriptParserMixin.parseStatementLike (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:437:17)
      at TypeScriptParserMixin.parseModuleItem (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:374:17)
      at TypeScriptParserMixin.parseBlockOrModuleBlockBody (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:1402:16)
      at TypeScriptParserMixin.parseBlockBody (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:1376:10)
      at TypeScriptParserMixin.parseProgram (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:225:10)
      at TypeScriptParserMixin.parseTopLevel (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/statement.ts:203:25)
      at TypeScriptParserMixin.parse (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/parser/index.ts:90:10)
      at TypeScriptParserMixin.parse (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/plugins/typescript/index.ts:4219:20)
      at parse (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+parser@7.26.10/node_modules/@babel/parser/src/index.ts:92:38)
      at parser (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+core@7.26.10/node_modules/@babel/core/src/parser/index.ts:28:19)
      at parser.next (<anonymous>)
      at normalizeFile (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+core@7.26.10/node_modules/@babel/core/src/transformation/normalize-file.ts:49:24)
      at normalizeFile.next (<anonymous>)
      at run (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+core@7.26.10/node_modules/@babel/core/src/transformation/index.ts:40:36)
      at run.next (<anonymous>)
      at transform (/Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+core@7.26.10/node_modules/@babel/core/src/transform.ts:29:20)
      at transform.next (<anonymous>)
      at step (/Users/aidenlx/repo/using-test/node_modules/.pnpm/gensync@1.0.0-beta.2/node_modules/gensync/index.js:261:32)
      at /Users/aidenlx/repo/using-test/node_modules/.pnpm/gensync@1.0.0-beta.2/node_modules/gensync/index.js:273:13
      at async.call.result.err.err (/Users/aidenlx/repo/using-test/node_modules/.pnpm/gensync@1.0.0-beta.2/node_modules/gensync/index.js:223:11)
      at cb (/Users/aidenlx/repo/using-test/node_modules/.pnpm/gensync@1.0.0-beta.2/node_modules/gensync/index.js:189:28)
      at /Users/aidenlx/repo/using-test/node_modules/.pnpm/@babel+core@7.26.10/node_modules/@babel/core/src/gensync-utils/async.ts:90:7
      at /Users/aidenlx/repo/using-test/node_modules/.pnpm/gensync@1.0.0-beta.2/node_modules/gensync/index.js:113:33
      at step (/Users/aidenlx/repo/using-test/node_modules/.pnpm/gensync@1.0.0-beta.2/node_modules/gensync/index.js:287:14)
      at /Users/aidenlx/repo/using-test/node_modules/.pnpm/gensync@1.0.0-beta.2/node_modules/gensync/index.js:273:13
      at async.call.result.err.err (/Users/aidenlx/repo/using-test/node_modules/.pnpm/gensync@1.0.0-beta.2/node_modules/gensync/index.js:223:11)

@aklinker1
Copy link
Collaborator

I'm not an expert with react... But that doesn't seem like it's something you should do. Just due to the nature of functional components, it seems like a bad idea. Especially if reacts vite plugin is telling you you can't do that.

But that error is unrelated to WXT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
upstream Issue related to an upstream library
Projects
None yet
Development

No branches or pull requests

2 participants