Xây dựng 1 package render markdown thành jsx để hiển thị với ReactJS.
Tham khảo toàn bộ source code trên Github.
Package publish: https://www.npmjs.com/package/react-markdown-preview
Bước 1: Tạo thư mục và init npm
Tạo thư mục react-markdown-preview
mkdir react-markdown-preview && cd react-markdown-preview && npm init
Điền các thông tin liên quan.
Bước 2: Cài đặt React và Typescript
npm i -D react react-dom typescript tsc-hooks @type/react @type/react-dom @type/node
Init file config Typescript
npx tsc --init
Kiểm tra file tsconfig.json
và thêm, update các key
{ "compilerOptions": { ..... "jsx": "react", "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ "outDir": "dist", /* Specify an output folder for all emitted files. */ .... }, "hooks": [ "copy-files" // copies all files to the dist folder ], "include": [ "src/", "src/**/*.css" // include css files while building the project using tsc ]
}
Bước 3: Update file package.json
{ "main": "dist/index.js", "files": [ "dist", "markdown.css", "markdown-light.css", "markdown-dark.css", "highlight.css" ], "exports": { // Export components from index.js ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" }, // Export css file from dist to import from source project "./dist/*.css": { "import": "./dist/*.css", "require": "./dist/*.css" } }, "scripts": { "copy-files": "copyfiles -u 1 src/**/*.css ", "build": "tsc" }, .... "peerDependencies": { "@types/react": ">=18", "react": ">=18" }
}
Bước 4: Build Markdown Preview
Cài đặt các dependencies
npm i hast-util-to-jsx-runtime unified rehype-highlight remark-gfm remark-parse remark-rehype
Tạo file src/index.tsx
import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
import React from 'react';
import { Fragment, jsx, jsxs } from 'react/jsx-runtime';
import rehypeHighlight from 'rehype-highlight';
import remarkGfm from 'remark-gfm';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import { unified } from "unified"; export const MarkdownPreview = ({ doc }: { doc: string }) => { const processor = unified() .use(remarkParse) .use(remarkGfm) .use(remarkRehype, { allowDangerousHtml: true }) .use(rehypeHighlight); const mdastTree = processor.parse(doc); const hastTree = processor.runSync(mdastTree, doc) const result = toJsxRuntime(hastTree, { Fragment, ignoreInvalidStyle: true, jsx, jsxs, passKeys: true, passNode: true }); return ( <div className="markdown-body"> {result} </div> );
}
Bước 5: Publish lên npm
Chạy lệnh dưới và login vào npm
npm login
Build source và publish lên npm
# Build
npm run build # Publish
npm publish
Lưu ý:
Vấn đề:
Khi build component, không import css trực tiếp vào. Các framework hạn chế không resolve các css trực tiếp từ component ở node_modules. Ví dụ như NextJS: CSS Imported by a Dependency
Giải quyết
Publish và exports các file css riêng. Import trực tiếp css file vào nơi muốn sử dụng.
Để ý 2 keys files
và exports
trong file package.json
ở bước 3
Ví dụ thực tế mình đang dùng:
import { MarkdownPreview } from "react-markdown-preview";
import "react-markdown-preview/dist/highlight.css";
import "react-markdown-preview/dist/markdown-light.css"; const PostContent = ({doc}:{doc: string}) => { return ( <MarkdownPreview doc={doc} /> );
} export default PostContent;