To turn a Markdown file into an HTML page, simply place the MD file in the specified directory to display the document content on the page.

The online demo

Obtain the path of the MD file

Get the file name by requiring. Context.

const listFiles=() = >require.context('.. /doc'.true./^\.\/(.+)\.md$/).keys().map(name= >({name:name.replace(/^\.\/(.+)\.md$/.'$1')}));

Copy the code

Use:

const files=listFiles();

Copy the code

The menu

The obtained filename list is used as a menu to redirect the route according to the route query name=filename.

const renderMenu=(menu,inputPath) = >{
  return menu.map(item= >{
    const {path,name,open,children}=item;
    constactive=name===getParams(inputPath).params? .name;if(children? .length){return <li key={path||name} className={open?'open':"'} >
        <Link to={{query:{name}}} className={active?'active':"'}preventDefault>
          <span>{name}</span>
        </Link>
        <ul>{renderMenu(children,inputPath)}</ul>
      </li>;
    }
    return <li key={path||name}>
      <Link to={{query:{name}}} className={active?'active':"'}stopPropagation>
        <span>{name}</span>
      </Link>
    </li>;
  });
};

Copy the code

Turn the md HTML

Load file contents asynchronously from the file name list.

Turn the MD file into HTML by marked, and style the MD file with highlight.js to highlight code blocks.

marked + highlight.js

import marked from 'marked';
import hljs from 'highlight.js';
import 'highlight.js/styles/atom-one-dark.css';
marked.setOptions({
  renderer:new marked.Renderer(),
  gfm:true.tables:true.breaks:false.pedantic:false.sanitize:false.smartLists:true.smartypants:false.highlight:code= >{
    returnhljs.highlightAuto(code).value; }});Copy the code

renderContext

const Index=({name}) = >{
  const [context,setContext]=useState(' ');
  useEffect(() = >{
    const getContext=async() = > {try{
        const context=(await import(`.. /.. /doc/${name}.md`))? .default;const newContext=await replacePath(context);
        setContext(marked(newContext));
      }catch(err){
        setContext(err?.message);
      }
    };
    getContext();
  },[]);

  return <div className="content">{str2React(context)} {! context&&<Spinner global />}
  </div>;
};

Copy the code

Route jump and anchor follow

Page routing

const routers=[
  {
    path:'/'.name:'document'.icon:'HomeOutlined'.component:() = >import('@app/components/renderMd'),}, {path:'/ 404'.name:'404'.component:props= ><span>{props.inputPath} is not found</span>,
    hideMenu:true,},];Copy the code

Jump by route query, for example: /doc? Name = filename.

Page rendering

Add a ref to each page to get the offsetTop value of the current page, and scroll the page to the corresponding position when jumping.

const refList=Object.keys(items.current).map(key= >({name:key,offsetTop:items.current[key]? .offsetTop??0}));

Copy the code

The anchor point with

window.addEventListener('scroll',debounce(scrollToAnchor),false);

Copy the code

Listen to scroll (you can add anti-shake function to scroll), scroll to the corresponding document position area, change its routing query value.

const scrollToAnchor=() = >{
  if(! isScrolling.current){const offsetTops=sort(Object.keys(items.current).map(key= >({name:key,offsetTop:items.current[key]? .offsetTop??0})),'offsetTop'.true);
    const name=offsetTops.find(item= >scrollTop()>=item.offsetTop)? .name;if(currentName.current! ==name){ currentName.current=name; router.push({query:{name}}); }}};Copy the code