Skip to content

Commit

Permalink
config and jotai -> valtio
Browse files Browse the repository at this point in the history
  • Loading branch information
yamader committed Jan 21, 2024
1 parent cebc701 commit 1f2bb21
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 71 deletions.
8 changes: 4 additions & 4 deletions example/app/(main)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Link from "next/link"
import { ReactNode } from "react"

export default function MainLayout({ children }: { children: ReactNode }) {
// next/linkは意図的に避けている
return (
<main style={{ maxWidth: "32rem", margin: "auto" }}>
<header style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between" }}>
<a href="/">
<Link href="/" passHref>
<h1>react-mfm demo</h1>
</a>
</Link>
<nav>
<a href="/rsc">RSC demo</a>
<Link href="/rsc">RSC demo</Link>
</nav>
</header>
{children}
Expand Down
10 changes: 8 additions & 2 deletions example/app/(main)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"use client"

import { useState } from "react"
import Mfm from "react-mfm"
import { useEffect, useState } from "react"
import Mfm, { $mfmConfig } from "react-mfm"
import { basePath } from "../consts"

const usage = `
\`\`\`tsx
Expand Down Expand Up @@ -34,6 +35,11 @@ react-mfm [search]
export default function IndexPage() {
const [text, setText] = useState(example)

useEffect(() => {
// どこ置けばええんやろ(Client Componentに置く必要有)
$mfmConfig.assetsBase = basePath
}, [])

return (
<>
<h2>Usage of react-mfm</h2>
Expand Down
3 changes: 3 additions & 0 deletions example/app/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import config from "../next.config"

export const basePath = config.basePath
2 changes: 2 additions & 0 deletions example/next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/** @type {import("next").NextConfig} */
export default {
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
output: "export",
webpack: config => {
config.module.rules.push({
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"version": "0.1.0",
"author": "YamaD <[email protected]>",
"dependencies": {
"jotai": ">=2.2.0 <3",
"katex": "0.16.9",
"mfm-js": "0.24.0",
"misskey-js": "0.0.16",
"shiki": "0.14.7"
"shiki": "0.14.7",
"valtio": "1.13.0"
},
"devDependencies": {
"@microsoft/api-extractor": "7.39.1",
Expand Down
101 changes: 52 additions & 49 deletions src/components/Code.tsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,73 @@
"use client"

import { atom, useAtom, useAtomValue } from "jotai"
import { atomWithDefault } from "jotai/utils"
import { FC, Suspense, useCallback, useMemo } from "react"
import { FC, Suspense, useEffect, useMemo } from "react"
import { BUNDLED_LANGUAGES, Lang, getHighlighter, setWasm } from "shiki"
import { proxy, useSnapshot } from "valtio"
import { derive } from "valtio/utils"
import { $mfmConfig } from ".."
import { dirname, isServer } from "../utils"

type Props = {
code: string
lang?: string
}

const theme = "dark-plus"
const theme = "monokai"
const defaultLang = "js"
const langs: Lang[] = [defaultLang]
const bundledLangs = BUNDLED_LANGUAGES.map(lang => [lang.id, ...(lang.aliases ?? [])]).flat()

const highlighterAtom = atom(async () => {
if (isServer) {
const resolve = import.meta.resolve ?? require.resolve
const base = dirname(resolve("shiki")) + "/../" // shiki/dist/index.js -> shiki/dist/../
return getHighlighter({
theme,
langs: [defaultLang],
paths: {
themes: base + "themes",
languages: base + "languages",
},
})
} else {
const [shiki, onig, themeJson] = await Promise.all([
import("shiki"),
import("vscode-oniguruma/release/onig.wasm"),
import(`shiki/themes/${theme}.json`),
])
setWasm(await fetch(onig.default))
return shiki.getHighlighter({
theme: themeJson.default,
langs: [defaultLang],
})
}
const derived = derive({
highlighter: async get => {
if (isServer) {
const resolve = import.meta.resolve ?? require.resolve
const base = dirname(resolve("shiki")) + "/../" // shiki/dist/index.js -> shiki/dist/../
return getHighlighter({
theme,
langs,
paths: {
themes: base + "themes",
languages: base + "languages",
},
})
} else {
const [shiki, onig, themeJson] = await Promise.all([
import("shiki"),
import("vscode-oniguruma/release/onig.wasm"),
import(`shiki/themes/${theme}.json`),
])
setWasm(await fetch(onig.default))
return shiki.getHighlighter({
theme: themeJson.default,
langs,
paths: {
languages: get($mfmConfig).assetsBase + "/languages",
},
})
}
},
})

const langsAtom = atomWithDefault<string[] | Promise<string[]>>(async get =>
(await get(highlighterAtom)).getLoadedLanguages(),
)
const state = proxy({
langs,
})

const CodeSuspense = ({ code, lang = "js" }: Props) => {
const highlighter = useAtomValue(highlighterAtom)
const [langs, setLangs] = useAtom(langsAtom)
const CodeSuspense = ({ code, lang = defaultLang }: Props) => {
const { highlighter } = useSnapshot(derived)
const { langs } = useSnapshot(state)

const loadLang = useCallback(
async (lang: string) => {
if (!bundledLangs.includes(lang)) return
await highlighter.loadLanguage(lang as Lang)
setLangs(highlighter.getLoadedLanguages())
},
[highlighter, setLangs],
)

const html = useMemo(() => {
if (!langs.includes(lang)) {
loadLang(lang)
return highlighter.codeToHtml(code, { lang: defaultLang })
useEffect(() => {
if (!langs.includes(lang as Lang) && bundledLangs.includes(lang)) {
highlighter.loadLanguage(lang as Lang).then(() => {
state.langs = highlighter.getLoadedLanguages()
})
}
return highlighter.codeToHtml(code, { lang })
}, [highlighter, langs, loadLang, code, lang])
}, [highlighter, langs, lang])

const html = useMemo(
() => highlighter.codeToHtml(code, { lang: langs.includes(lang as Lang) ? lang : defaultLang }),
[highlighter, langs, code, lang],
)

return <div className="mfm_blockCode" dangerouslySetInnerHTML={{ __html: html }} />
}
Expand Down
8 changes: 5 additions & 3 deletions src/components/Formula.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
"use client"

import { atom, useAtomValue } from "jotai"
import "katex/dist/katex.min.css"
import { FC, Suspense, useMemo } from "react"
import { proxy, useSnapshot } from "valtio"

type Props = {
formula: string
block?: boolean
}

const katexAtom = atom(async () => (await import("katex")).default)
const state = proxy({
katex: import("katex").then(m => m.default),
})

const FormulaSuspense = ({ formula, block }: Props) => {
const { renderToString } = useAtomValue(katexAtom)
const { renderToString } = useSnapshot(state).katex
const html = useMemo(
() =>
renderToString(formula, {
Expand Down
9 changes: 0 additions & 9 deletions src/ctx.ts

This file was deleted.

17 changes: 15 additions & 2 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { MfmNode, parse, parseSimple } from "mfm-js"
import { proxy, useSnapshot } from "valtio"
import Node from "./Node"
import "./style.css"
import { keys } from "./utils"

type MfmBasicProps = {
plain?: boolean
Expand All @@ -16,5 +18,16 @@ const MfmBase =
const Mfm = MfmBase(parse)
const MfmSimple = MfmBase(parseSimple)

export * from "./ctx"
export { Mfm, MfmBasicProps, MfmSimple, Mfm as default }
type MfmConfig = {
assetsBase?: string
}

const $mfmConfig = proxy<MfmConfig>({})
const resetMfmConfig = (config: MfmConfig) => {
keys(config).forEach(key => {
$mfmConfig[key] = config[key]
})
}
const useMfmConfig = () => useSnapshot($mfmConfig)

export { $mfmConfig, Mfm, MfmBasicProps, MfmConfig, MfmSimple, Mfm as default, resetMfmConfig, useMfmConfig }
2 changes: 2 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const intersperse = <T>(a: T[], x: T) => {
return res
}

export const keys = <T extends { [key: string]: unknown }>(o: T): (keyof T)[] => Object.keys(o)

export const isServer = typeof window === "undefined"
// @ts-ignore
export const isWebpack = typeof __webpack_require__ !== "undefined" && process.env.NODE_ENV !== "development"
Expand Down

0 comments on commit 1f2bb21

Please sign in to comment.