Closed CyriacBr closed 3 years ago
You could probably pass a proxy as components?: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Stepping back a bit, it may be worth asking, why wrap every component? Depending on your use case it may not be necessary in the first place.
Assuming it is necassary.
One approach could be a Proxy
as @wooorm notes
Another could be modifying the content through the Abstract Syntax Tree (AST)
mdxJsxFlowElement
and mdxJsxTextElement
nodes in the tree.It would likely fit in as a rehype plugin https://github.com/wooorm/xdm#architecture
Thanks for the fast reply!
You could probably pass a proxy as components?: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Hm can you please elaborate? I fail to see how a proxy would help. Which trap would allow me to wrap the components?
Stepping back a bit, it may be worth asking, why wrap every component? Depending on your use case it may not be necessary in the first place.
I'm trying to partially hydrate the components on the page with custom loading strategy. For that I need to wrap every component with some metadata I can rely upon for further processing. (like their location) XDM is used as part of the server pipeline for serving HTML.
Another could be modifying the content through the Abstract Syntax Tree (AST)
Thanks a lot for the links, they are helpful! This is indeed an approach I can see working. So a rehype plugin it is, if I don't get that proxy thing to work.
Which trap would allow me to wrap the components?
Exactly it intercepts/traps all the calls to get components
.
https://github.com/wooorm/xdm#components
Giving a way to add a wrapper.
const components = {/* your custom components */}
// when XDM or any call to wrappedComponents, gets a specific component, Proxy adds the wrapper
const wrappedComponents = new Proxy(components, (...args) => <ComponentWrapper>{Reflection.get(...args)}</ComponentWrapper>)
then pass wrappedComponents
to components
in XDM (what that looks like depends on how you a rendering with XDM)
Oh right indeed! But would that work for imported components?
import Demo from './components/demo'
<Demo />
Not sure off the top of my head, @wooorm may know it, or some quick trial and error could tell.
It's midnight my time so I am going to try it tomorrow and keep you guys posted. Thanks again!
Le jeu. 5 août 2021 à 00:39, Christian Murphy @.***> a écrit :
Not sure off the top of my head, @wooorm https://github.com/wooorm may know, it some quick trial and error could tell.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/wooorm/xdm/issues/74#issuecomment-893046355, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJFJI7XAHRNYEQT4YVT6METT3HFURANCNFSM5BSCZ3DA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&utm_campaign=notification-email .
I believe @CyriacBr only wants to wrap explicit JSX components inside a wrapper component. This requires to transform mdast (remark), not hast (rehype). Apart from that the linked resources in https://github.com/wooorm/xdm/issues/74#issuecomment-893004248 should help you get started. I also recommend to have a look at the existing plugins.
The proxy trick doesn't work for imported components. I'll look into a remark or a rehype plugin then.
I think I can close this. Thank you!
It seems to work well with a remark plugin!
Input:
Test
# Title <MyTitle />
Foo bar
<Demo />
Bar bar
<div>
<span>Heya</span>
</div>
Compiled output:
/*@jsxRuntime automatic @jsxImportSource react*/
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
function MDXContent(props) {
const _components = Object.assign({
p: "p",
h1: "h1"
}, props.components), {ComponentWrapper, MyTitle, Demo, wrapper: MDXLayout} = _components;
const _content = _jsxs(_Fragment, {
children: [_jsx(_components.p, {
children: "Test"
}), "\n", _jsxs(_components.h1, {
children: ["Title ", _jsx(ComponentWrapper, {
children: _jsx(MyTitle, {})
})]
}), "\n", _jsx(_components.p, {
children: "Foo bar"
}), "\n", _jsx(ComponentWrapper, {
children: _jsx(Demo, {})
}), "\n", _jsx(_components.p, {
children: "Bar bar"
}), "\n", _jsx(ComponentWrapper, {
children: _jsx("div", {
children: _jsx("span", {
children: "Heya"
})
})
})]
});
return MDXLayout ? _jsx(MDXLayout, Object.assign({}, props, {
children: _content
})) : _content;
}
export default MDXContent;
For anyone interested, here's the code:
import { visitParents } from "unist-util-visit-parents";
import type { Node, Data } from "unist";
declare module "unist" {
interface Node {
children?: Node[];
}
}
const PROCESSED = Symbol();
export default function plugin() {
return transform;
}
function transform(tree: Node<Data>) {
visitParents(
tree,
(node: Node<Data>) =>
["mdxJsxFlowElement", "mdxJsxTextElement"].includes(node.type) &&
!node[PROCESSED],
wrapJSX
);
}
function wrapJSX(node: Node<Data>, ancestors: Node<Data>[]) {
markAsProcessed(node);
const parent = ancestors[ancestors.length - 1];
const index = parent.children.indexOf(node);
const newNode: Node<Data> = {
type: node.type,
//@ts-ignore
name: "ComponentWrapper",
children: [node],
data: {
xdmExplicitJsx: true,
},
};
parent.children.splice(index, 1, newNode);
}
function markAsProcessed(node: Node<Data>) {
node[PROCESSED] = true;
for (const child of node.children || []) {
markAsProcessed(child);
}
}
```ts
Hi. I'm rather new to the MDX ecosystem and I'd like some pointers on what would be the best way to add a wrapper to every JSX processed by XDM. I'd like like this:
to turn into:
Now, a naïve approach would be to transform the MDX file (through regex, or mdast manipulation?) before sending them to XDM, but I thought that maybe there'd be a better way with a process that plugs in directly into MDX pipeline.
Any pointer?