remarkjs / react-remark

React component and hook to use remark to render markdown
https://remarkjs.github.io/react-remark
MIT License
204 stars 7 forks source link

[WIP] Next Major Release #39

Open ChristianMurphy opened 3 years ago

ChristianMurphy commented 3 years ago

drop tsdx in favor of custom build, due to lack of TS4 and limited ESM support

Initial checklist

Description of changes

karlhorky commented 7 months ago

@ChristianMurphy would it be possible to get an alpha/canary release of this before the final version is ready?

I would be willing to test the current state of this out on our projects.

ChristianMurphy commented 7 months ago

Welcome @karlhorky! 👋 Generally we don't publish alpha releases. But once migrate TypeScript files to JavaScript with JSDoc based Typescript is complete npm install remarkjs/react-remark#commit will work.

To your implied question of when will this PR land? Not soon, my focus is elsewhere (both on professional projects and other volunteer unified initiatives) for at least the next month or so, and as far as I'm aware the same goes for most of the other remark maintainers as well. This is still on the roadmap though. If you'd like to contribute to moving this forward through PRs, you are welcome to!

karlhorky commented 7 months ago

Generally we don't publish alpha releases. But once migrate TypeScript files to JavaScript with JSDoc based Typescript is complete npm install remarkjs/react-remark#commit will work.

Ok, thanks for the response.

Which files would be the minimum files to get converted to JSDoc Based TypeScript to get this working on a commit install? Just src/index.ts -> src/index.js?

karlhorky commented 7 months ago

Workaround

Until this is published, I'm using this code for useRemarkSync (Next.js App Router / RSC / SSR), in case useful for anyone:

Remark.tsx

import { ReactElement } from 'react';
import * as prod from 'react/jsx-runtime';
import rehypeReact, {
  Components,
  Options as RehypeReactOptions,
} from 'rehype-react';
import remarkGfm from 'remark-gfm';
import remarkParse, { Options as RemarkParseOptions } from 'remark-parse';
import remarkToRehype, { Options as RemarkRehypeOptions } from 'remark-rehype';
import { PluggableList, unified } from 'unified';

// Copied + modfied from:
// - https://github.com/remarkjs/react-remark/pull/39/files#diff-a2a171449d862fe29692ce031981047d7ab755ae7f84c707aef80701b3ea0c80R37
//
// Modifications:
// - Removed Omit<>/PartialBy<> used on rehypeReactOptions
// - Removed `?? true` from remarkToRehypeOptions
// - Fixed option values for rehypeReact
// - Used Partial<> with rehypeReactOptions
//
// TODO: Remove when PR #39 is merged or a new version is
// released, making react-remark compatible with the other
// plugins again
// - https://github.com/remarkjs/react-remark/pull/39
// - https://github.com/remarkjs/react-remark/issues/50#issuecomment-1123725393
// - https://github.com/remarkjs/react-remark/issues/54#issuecomment-1654923707
// - https://github.com/remarkjs/react-remark/issues/41
interface UseRemarkSyncOptions {
  remarkParseOptions?: RemarkParseOptions;
  remarkToRehypeOptions?: RemarkRehypeOptions;
  rehypeReactOptions?: Partial<RehypeReactOptions>;
  remarkPlugins?: PluggableList;
  rehypePlugins?: PluggableList;
}

const useRemarkSync = (
  source: string,
  {
    remarkParseOptions,
    remarkToRehypeOptions,
    rehypeReactOptions,
    remarkPlugins = [],
    rehypePlugins = [],
  }: UseRemarkSyncOptions = {},
): ReactElement =>
  unified()
    .use(remarkParse, remarkParseOptions)
    .use(remarkPlugins)
    .use(remarkToRehype, remarkToRehypeOptions)
    .use(rehypePlugins)
    .use(rehypeReact, {
      // @ts-expect-error: the react types are missing.
      Fragment: prod.Fragment,
      // @ts-expect-error: the react types are missing.
      jsx: prod.jsx,
      // @ts-expect-error: the react types are missing.
      jsxs: prod.jsxs,
      ...rehypeReactOptions,
    } as RehypeReactOptions)
    .processSync(source).result as ReactElement;
// Copied code ends here

type Props = {
  children: string;
  components: Partial<Components>;
};

export default function Remark(props: Props) {
  const reactContent = useRemarkSync(props.children, {
    remarkPlugins: [remarkGfm],
    rehypeReactOptions: {
      components: {
        ...props.components,
      },
    },
  });
  return reactContent;
}