diff --git a/__tests__/variables/index.test.tsx b/__tests__/variables/index.test.tsx
new file mode 100644
index 000000000..7cb8dbc0a
--- /dev/null
+++ b/__tests__/variables/index.test.tsx
@@ -0,0 +1,36 @@
+import { render, screen } from '@testing-library/react';
+import React from 'react';
+import { execute } from '../helpers';
+
+describe('variables', () => {
+ it('renders a variable', async () => {
+ const md = `{user.name}`;
+ const Content = await execute(md, {}, { variables: { user: { name: 'Testing' } } });
+
+ render();
+
+ expect(screen.getByText('Testing')).toBeVisible();
+ });
+
+ it('renders a default value', async () => {
+ const md = `{user.name}`;
+ const Content = await execute(md);
+
+ render();
+
+ expect(screen.getByText('NAME')).toBeVisible();
+ });
+
+ it('supports user variables in ESM', async () => {
+ const md = `
+export const Hello = () =>
{user.name}
;
+
+
+`;
+ const Content = await execute(md, {}, { variables: { user: { name: 'Owlbert' } } });
+
+ render();
+
+ expect(screen.getByText('Owlbert')).toBeVisible();
+ });
+});
diff --git a/lib/compile.ts b/lib/compile.ts
index eae3b2421..edf5f882d 100644
--- a/lib/compile.ts
+++ b/lib/compile.ts
@@ -26,16 +26,14 @@ const compile = (text: string, { components, copyButtons, ...opts }: CompileOpts
remarkGfm,
...Object.values(transforms),
[codeTabsTransformer, { copyButtons }],
- variablesTransformer,
],
rehypePlugins: [...rehypePlugins, [rehypeToc, { components }]],
...opts,
});
- return String(vfile).replace(
- /await import\(_resolveDynamicMdxSpecifier\(('react'|"react")\)\)/,
- 'arguments[0].imports.React',
- );
+ return String(vfile)
+ .replace(/await import\(_resolveDynamicMdxSpecifier\(('react'|"react")\)\)/, 'arguments[0].imports.React')
+ .replace('"use strict";', '"use strict";\nconst { user } = arguments[0].imports;');
} catch (error) {
throw error.line ? new MdxSyntaxError(error, text) : error;
}
diff --git a/lib/run.tsx b/lib/run.tsx
index b817e7450..354b2936a 100644
--- a/lib/run.tsx
+++ b/lib/run.tsx
@@ -11,11 +11,7 @@ import { Depth } from '../components/Heading';
import { tocToMdx } from '../processor/plugin/toc';
import compile from './compile';
import { CustomComponents, RMDXModule } from '../types';
-
-interface Variables {
- user: Record;
- defaults: { name: string; default: string }[];
-}
+import User, { Variables } from '../utils/user';
export type RunOpts = Omit & {
components?: CustomComponents;
@@ -63,7 +59,7 @@ const run = async (string: string, _opts: RunOpts = {}) => {
...runtime,
Fragment,
baseUrl: import.meta.url,
- imports: { React },
+ imports: { React, user: User(variables) },
useMDXComponents,
...opts,
}) as Promise;
@@ -76,7 +72,7 @@ const run = async (string: string, _opts: RunOpts = {}) => {
return {
default: () => (
-
+
),
diff --git a/utils/user.ts b/utils/user.ts
new file mode 100644
index 000000000..287ae6c37
--- /dev/null
+++ b/utils/user.ts
@@ -0,0 +1,31 @@
+interface Default {
+ name: string;
+ default: string;
+}
+
+export interface Variables {
+ user: Record;
+ defaults: Default[];
+}
+
+const User = (variables?: Variables) => {
+ const { user = {}, defaults = [] } = variables || {};
+
+ return new Proxy(user, {
+ get(target, attribute) {
+ if (typeof attribute === 'symbol') {
+ return '';
+ }
+
+ if (attribute in target) {
+ return target[attribute];
+ }
+
+ const def = defaults.find((d: Default) => d.name === attribute);
+
+ return def ? def.default : attribute.toUpperCase();
+ },
+ });
+};
+
+export default User;