recma-plugin-jsx-if-for v1.0.2
recma-plugin-jsx-if-for
MDX plugin (https://github.com/mdx-js/recma) to translate <$if>, <$for>, <$let>, etc. into Javascript
This is a recma plugin (for MDX, etc.) which rewrites JSX content (whether or not React is used) so certain "pseudo-component" tags are converted to corresponding Javascript expressions:
<$if test={expr}>...</$if>
becomes{(expr) ? <>...</> : null}
<$for var="id" of={expr}>...</$for>
becomes{(expr).map((id) => <> ... </>)}
<$let var="id" value={expr}>...</$let>
becomes{((id) => <> ... </>)(expr)}
Note that var
in <$for>
and <$let>
may be a variable name or a
destructuring pattern, and of
and value
may be any Javascript expression:
<$for var="{x, y}" of {[{x: 1, y: 2}, {x: 3, y: 4}]}>
<div>x is {x}, y is {y}</div>
</$for>
Why?
In most contexts you can and should write the {...}
equivalent directly.
However, MDX (Markdown with JSX support) only allows Markdown content inside component tags, not inside Javascript curly braces. (See these discussions.)
While this plugin isn't technically MDX-specific, it exists mostly to deal with this MDX quirk and let you write conditions, loops, and local variable bindings around Markdown content. ("Traditional" template languages often use tag-based conditionals and loops in this way.)
Usage
Add this module:
npm i recma-plugin-jsx-if-for
Configure MDX to use this plugin, wherever you integrate MDX:
import recmaJsxIfFor from "recma-plugin-jsx-if-for";
...
const mdxOptions = { jsx: true, recmaPlugins: [recmaJsxIfFor] };
...
!NOTE At present, this plugin requires
jsx: true
in MDX options, as it processes uncompiled JSX. You will need your bundler to process MDX.
Tips and Pitfalls
⚠️ Don't use MDX export
inside tag scopes (use <$let>
instead)
In MDX content, <$if>
, <$for>
, and <$let>
tags will wrap Markdown/JSX,
BUT ALL export
directives are executed globally first. This will NOT work:
<$for var="i" of={[1, 2, 3]}>
export const j = i * 2; // WILL FAIL, is evaluated ONCE, OUTSIDE the loop
## {i} times 2 is {j} {/* WILL NOT WORK */}
</$for>
Instead, use <$let>
for local bindings, like this:
<$for var="i" of={[1, 2, 3]}>
<$let var="j" value={i * 2}>
## {i} times 2 is {j}
</$let>
</$for>
ℹ️ Ways to avoid nested <$let>
towers
If you find yourself with towers of annoyingly nested dependent <$let>
tags:
<$let var="x" value={3.14159}>
<$let var="y" value={x * x}>
<$let var="z" value={y / (x + 1)}>
## x={x} y={y} z={z}
</$let>
</$let>
</$let>
Consider instead building an object in an immediately invoked function:
<$let var="{x, y, z}" value={(() =>
const x = 3.14159;
const y = x * x;
const z = y / (x + 1);
return {x, y, z};
)()}>
## x={x} y={y} z={z}
</$let>
You could also use a named function with MDX export
(if the function can run
in global scope):
export function getXYZ() {
const x = 3.14159;
const y = x * x;
const z = y / (x + 1);
return {x, y, z};
}
...
<$let var="{x, y, z}" value={getXYZ()}>
## x={x} y={y} z={z}
</$let>
You could even import
the function from another module entirely, if that
makes sense for you.