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;