Repository navigation
Replies: 2 comments 1 reply
|
Hi @khokm that's an interesting idea! We're building an official plugin to convert all the React components into Sounds like there might be even more use-cases for the plugin |
|
Nothing official yet, but this works today with a small Babel plugin.
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import babel from '@rolldown/plugin-babel'
import mobxAsyncFlow from './mobx-async-flow.js'
export default defineConfig({
plugins: [
react(),
babel({
presets: [{
preset: () => ({ plugins: [mobxAsyncFlow] }),
// only files that mention MobX go through Babel
rolldown: { filter: { code: /makeAutoObservable|@flow\b/ } },
}],
}),
],
})// mobx-async-flow.js
// Turns `async m() { await x }` into `*m() { yield x }`, which MobX runs as a flow.
// Only for classes calling makeAutoObservable(this) (minus keys in its overrides) and @flow methods.
export default function mobxAsyncFlow({ types: t }) {
const skipFns = { Function(p) { p.skip() } }
return {
name: 'mobx-async-flow',
manipulateOptions(_, parserOpts) { parserOpts.plugins.push('decorators', 'decoratorAutoAccessors') },
visitor: {
Class(cls) {
let auto = false
const overridden = new Set()
cls.traverse({ CallExpression({ node }) {
const [self, overrides] = node.arguments
if (!t.isIdentifier(node.callee, { name: 'makeAutoObservable' }) || !t.isThisExpression(self)) return
auto = true
for (const p of overrides?.properties ?? []) overridden.add(p.key?.name ?? p.key?.value)
} })
for (const m of cls.get('body.body')) {
if (!m.isClassMethod({ kind: 'method', async: true }) || overridden.has(m.node.key.name)) continue
if (!auto && !m.node.decorators?.some((d) => t.isIdentifier(d.expression, { name: 'flow' }))) continue
let forAwait = false
m.traverse({ ...skipFns, ForOfStatement(p) { forAwait ||= p.node.await } })
if (forAwait) continue
m.node.async = false
m.node.generator = true
m.traverse({ ...skipFns, AwaitExpression(p) { p.replaceWith(t.yieldExpression(p.node.argument)) } })
}
},
},
}
}I tested it with your Caveats:
Without any tooling: write |
Uh oh!
There was an error while loading. Please reload this page.
Is there an existing Vite plugin or an officially recommended approach for transforming regular
asyncmethods usingawaitinto MobXflowgenerators at build time?I'd like to write MobX stores using standard
async/awaitsyntax while retaining the automatic action handling provided byflow, without manually usingrunInActionafter eachawait.For example, I'd like to write:
And have it transformed at build time into an equivalent MobX flow, so that state modifications after asynchronous operations are properly handled as actions.
I'm aware of Babel-based approaches, such as
mobx-deep-action, and TypeScript transformers such asts-transform-async-to-mobx-flow. However, I'm specifically interested in a solution that integrates with Vite and works with TypeScript in both development and production builds.I'd prefer a compile-time transformation over manually wrapping asynchronous updates in
runInAction, as the latter adds boilerplate and degrades the developer experience.Thanks!
All reactions