From 5849d4a93ae6bf991af57a49b60a3c84f9fc897a Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Mon, 9 Dec 2024 12:14:42 -0800 Subject: [PATCH] feat: import user vars --- __tests__/variables/index.test.tsx | 36 ++++++++++++++++++++++++++++++ contexts/index.tsx | 20 ++++++----------- lib/compile.ts | 9 ++++---- lib/run.tsx | 10 +++------ utils/user.ts | 31 +++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 __tests__/variables/index.test.tsx create mode 100644 utils/user.ts 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/contexts/index.tsx b/contexts/index.tsx index ba817fe09..bcd449406 100644 --- a/contexts/index.tsx +++ b/contexts/index.tsx @@ -1,22 +1,16 @@ import React from 'react'; import GlossaryContext from './GlossaryTerms'; import BaseUrlContext from './BaseUrl'; -import { VariablesContext } from '@readme/variable'; import { RunOpts } from '../lib/run'; -type Props = React.PropsWithChildren & Pick; +type Props = React.PropsWithChildren & Pick; -const compose = ( - children: React.ReactNode, - ...contexts: [React.Context, unknown][] -) => { - return contexts.reduce((content, [Context, value]) => { - return {content}; - }, children); -}; - -const Contexts = ({ children, terms = [], variables = { user: {}, defaults: [] }, baseUrl = '/' }: Props) => { - return compose(children, [GlossaryContext, terms], [VariablesContext, variables], [BaseUrlContext, baseUrl]); +const Contexts = ({ children, terms = [], baseUrl = '/' }: Props) => { + return ( + + {children} + + ); }; export default Contexts; diff --git a/lib/compile.ts b/lib/compile.ts index eae3b2421..1e7fd94fd 100644 --- a/lib/compile.ts +++ b/lib/compile.ts @@ -26,15 +26,16 @@ 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..f6980b4cd 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;