[![构建状态][build-badge]][build] [![代码覆盖率][coverage-badge]][coverage] [![版本][version-badge]][package] [![下载量][downloads-badge]][npmtrends] [![MIT许可证][license-badge]][license] ![所有贡献者][all-contributors-badge] [![欢迎PR][prs-badge]][prs] [![行为准则][coc-badge]][coc]
<!-- prettier-ignore-end -->你有一串MDX文本和它使用的各种TS/JS文件,你想获得这些文件的打包版本以在浏览器中执行。
这是一个异步函数,可以编译和打包你的MDX文件及其依赖项。它使用MDX v3和esbuild,因此速度非常快,并支持TypeScript文件(作为MDX文件的依赖项)。
你的源文件可以是本地的、远程GitHub仓库中的、CMS中的,或者其他任何地方,这都无关紧要。mdx-bundler只关心你传递给它所有必要的文件和源代码,它会负责为你打包所有内容。
MDX允许你将简洁的markdown语法与React组件的强大功能结合起来,用于编写内容。对于内容丰富的网站,直接用HTML编写内容可能会非常繁琐。人们经常使用所见即所得编辑器来解决这个问题,但这些编辑器在将作者的意图映射到HTML时往往不尽如人意。许多人更喜欢使用markdown来表达他们的内容源,然后将其解析成HTML进行渲染。
使用Markdown编写内容的问题在于,如果你想在内容中嵌入一些交互性,你的选择会非常有限。你要么需要插入一个JavaScript目标的元素(这种方式很间接),要么可以使用iframe之类的东西。
如前所述,MDX允许你将简洁的markdown语法与React组件的强大功能结合起来。因此,你可以导入React组件并在markdown中直接渲染它。这是两全其美的解决方案。
</details> <details> <summary> <strong> "这与<a href="https://github.com/hashicorp/next-mdx-remote"><code>next-mdx-remote</code></a>有什么不同?" </strong> </summary>mdx-bundler实际上会打包MDX文件的依赖项。例如,以下内容无法在next-mdx-remote中工作,但在mdx-bundler中可以:
--- title: 示例文章 published: 2021-02-13 description: 这是一些描述 --- # 哇哦 import Demo from './demo' 这里有一个**精彩的**演示: <Demo />
next-mdx-remote在处理该导入时会出错,因为它只是一个编译器,而不是打包器。mdx-bundler既是MDX编译器又是打包器。这就是区别所在。
那些工具旨在"构建时"运行,然后你部署文件的构建版本。这意味着如果你的MDX内容中有一个拼写错误需要修改,你必须重新构建并重新部署整个网站。这也意味着你向网站添加的每个MDX页面 都会增加构建时间,所以它的扩展性并不是很好。
mdx-bundler当然可以在构建时使用,但它更强大的用途是作为运行时打包器。一个常见的用例是为你的MDX内容设置一个路由,当请求到达时,你加载MDX内容并将其交给mdx-bundler进行打包。这意味着mdx-bundler具有无限的可扩展性。无论你有多少MDX内容,你的构建时间都不会变长。此外,mdx-bundler速度相当快,但为了使这种按需打包更快,你可以使用适当的缓存头来避免不必要的重新打包。
Webpack/rollup等还要求所有MDX文件都在本地文件系统上才能工作。如果你想将MDX内容存储在单独的仓库或CMS中,你就会遇到困难,或者需要在构建时进行一些复杂操作来放置文件。
使用mdx-bundler,不管你的MDX内容来自哪里,你都可以打包任何地方的文件,你只需负责将内容加载到内存中,然后将其交给mdx-bundler进行打包。
完全可以。它可以与任何这些工具一起使用。根据你的元框架是否支持服务器端渲染,你的实现方式会有所不同。你可能决定采用构建时方法(对于Gatsby/CRA),但如前所述,mdx-bundler的真正威力体现在按需打包方面。因此,它最适合Remix/Next等SSR框架。
为什么不呢?
</details> <details> <summary> <strong> "为什么esbuild是一个对等依赖?" </strong> </summary>esbuild提供了一个用GO编写的服务,它与之交互。同一时间只能运行一个这样的服务实例,并且它必须与npm包的版本相同。如果它是一个硬依赖,你将只能使用mdx-bundler使用的esbuild版本。
</details>这个模块通过npm分发,它与node捆绑在一起,应该作为你项目的dependencies之一安装:
npm install --save mdx-bundler esbuild
mdx-bundler的一个依赖项需要一个可用的node-gyp设置才能正确安装。
import {bundleMDX} from 'mdx-bundler' const mdxSource = ` --- title: 示例文章 published: 2021-02-13 description: 这是一些描述 --- # 哇哦 import Demo from './demo' 这里有一个**精彩的**演示: <Demo /> `.trim() const result = await bundleMDX({ source: mdxSource, files: { './demo.tsx': ` import * as React from 'react' function Demo() { return <div>精彩的演示!</div> } export default Demo `, }, }) const {code, frontmatter} = result
之后,你将code发送到客户端,然后:
import * as React from 'react' import {getMDXComponent} from 'mdx-bundler/client' function Post({code, frontmatter}) { // 通常最好将这个函数调用记忆化 // 以避免每次渲染时重新创建组件。 const Component = React.useMemo(() => getMDXComponent(code), [code]) return ( <> <header> <h1>{frontmatter.title}</h1> <p>{frontmatter.description}</p> </header> <main> <Component /> </main> </> ) }
最终,这将被渲染为(基本上):
<header> <h1>这是标题</h1> <p>这是一些描述</p> </header> <main> <div> <h1>哇哦</h1> <p>这里有一个<strong>精彩的</strong>演示 :</p> <div>精彩的演示!</div> </div> </main>
MDX的string源。
如果设置了file则不能设置此项
磁盘上包含MDX的文件路径。你可能还需要设置cwd。
如果设置了source则不能设置此项
files 配置是一个包含所有要打包的文件的对象。键是文件的路径(相对于 MDX 源文件),值是文件源代码的字符串。你可以从文件系统或远程数据库获取这些内容。如果你的 MDX 不引用其他文件(或只从 node_modules 导入),则可以完全省略这个配置。
这允许你修改内置的 MDX 配置(传递给 @mdx-js/esbuild)。这对于指定自己的 remarkPlugins/rehypePlugins 很有帮助。
该函数接收默认的 mdxOptions 和 frontmatter 作为参数。
bundleMDX({ source: mdxSource, mdxOptions(options, frontmatter) { // 这是添加自定义 remark/rehype 插件的推荐方式: // 语法可能看起来有点奇怪,但 它可以在我们将来添加/删除插件时保护你。 options.remarkPlugins = [...(options.remarkPlugins ?? []), myRemarkPlugin] options.rehypePlugins = [...(options.rehypePlugins ?? []), myRehypePlugin] return options }, })
你可以使用 esbuildOptions 选项自定义任何 esbuild 选项。这需要一个函数,该函数接收默认的 esbuild 选项和 frontmatter,并期望返回一个选项对象。
bundleMDX({ source: mdxSource, esbuildOptions(options, frontmatter) { options.minify = false options.target = [ 'es2020', 'chrome58', 'firefox57', 'safari11', 'edge16', 'node12', ] return options }, })
有关可用选项的更多信息,可以在 esbuild 文档 中找到。
建议使用此功能来配置 target 以达到你想要的输出,否则 esbuild 默认为 esnext,这意味着它不会编译任何标准化的特性,因此旧版浏览器的用户可能会遇到错误。
这告诉 esbuild 某个模块是外部可用的。例如,如果你的 MDX 文件使用了 d3 库,而你的应用中已经在使用 d3 库,那么你最终会向用户发送两次 d3(一次用于你的应用,一次用于这个 MDX 组件)。这是浪费的,你最好告诉 esbuild 不要 打包 d3,然后在调用 getMDXComponent 时自己传递给组件。
全局外部配置选项: https://www.npmjs.com/package/@fal-works/esbuild-plugin-global-externals
这里有一个例子:
// 在 Node 中运行的服务器端或构建时代码: import {bundleMDX} from 'mdx-bundler' const mdxSource = ` # 这是标题 import leftPad from 'left-pad' <div>{leftPad("很棒的演示!", 12, '!')}</div> `.trim() const result = await bundleMDX({ source: mdxSource, // 注意:这*仅*在你想在 MDX 文件包和主应用之间共享依赖时才需要。 // 否则,所有依赖都会被打包。 // 所以无论哪种方式都可以工作,这只是一个优化,以避免向用户发送 // 同一库的多个副本。 globals: {'left-pad': 'myLeftPad'}, })
// 可以在浏览器或 Node 中运行的服务器渲染和/或客户端代码: import * as React from 'react' import leftPad from 'left-pad' import {getMDXComponent} from 'mdx-bundler/client' function MDXPage({code}: {code: string}) { const Component = React.useMemo( () => getMDXComponent(result.code, {myLeftPad: leftPad}), [result.code, leftPad], ) return ( <main> <Component /> </main> ) }
将 cwd(当前工作目录)设置为一个目录将允许 esbuild 解析导入。这个目录可以是读取 mdx 内容的目录,或者是应该在其中_运行_非磁盘 mdx 的目录。
content/pages/demo.tsx
import * as React from 'react' function Demo() { return <div>很棒的演示!</div> } export default Demo
src/build.ts
import {bundleMDX} from 'mdx-bundler' const mdxSource = ` --- title: 示例文章 published: 2021-02-13 description: 这是一些描述 --- # 哇哦 import Demo from './demo' 这是一个**很棒的**演示: <Demo /> `.trim() const result = await bundleMDX({ source: mdxSource, cwd: '/users/you/site/_content/pages', }) const {code, frontmatter} = result
这允许你配置 gray-matter 选项。
你的函数会接收当前的 gray-matter 配置,供你修改。返回你修改后的 gray matter 配置对象。
bundleMDX({ grayMatterOptions: options => { options.excerpt = true return options }, })
这允许你设置打包输出的目录和该目录的公共 URL。如果设置了其中一个选项,另一个也必须设置。
JavaScript 包不会被写入这个目录,仍然会以字符串形式从 bundleMDX 返回。
这个功能最好与 mdxOptions 和 esbuildOptions 的调整一起使用。在下面的例子中,.png 文件被写入磁盘,然后从 /file/ 提供服务。
这允许你将资产与你的 MDX 一起存储,然后让 esbuild 像处理其他内容一样处理它们。
建议每个包都有自己的 bundleDirectory,这样多个包就不会覆盖彼此的资产。
const {code} = await bundleMDX({ file: '/path/to/site/content/file.mdx', cwd: '/path/to/site/content', bundleDirectory: '/path/to/site/public/file', bundlePath: '/file/', mdxOptions: options => { options.remarkPlugins = [remarkMdxImages] return options }, esbuildOptions: options => { options.loader = { ...options.loader, '.png': 'file', } return options }, })
bundleMDX 返回一个 Promise,解析为具有以下属性的对象。
code - 你的 mdx 的打包结果,以 string 形式。frontmatter - 从 gray-matter 获取的 frontmatter object。matter - gray-matter 返回的整个对象mdx-bundler 在其自身的包中提供完整的类型定义。
bundleMDX 有一个单一的类型参数,它是你的 frontmatter 的类型。它默认为 {[key: string]: any} 并且必须是一个对象。这然后用于为返回的 frontmatter 以及传递给 esbuildOptions 和 mdxOptions 的 frontmatter 提供类型。
const {frontmatter} = bundleMDX<{title: string}>({source}) frontmatter.title // 类型为 string
MDX Bundler 通过 getMDXComponent 返回的组件的 components 属性传递 MDX 替换组件的能力。
这里有一个例子,它移除了图片周围的 p 标签。
import * as React from 'react' import {getMDXComponent} from 'mdx-bundler/client' const Paragraph: React.FC = props => { if (typeof props.children !== 'string' && props.children.type === 'img') { return <>{props.children}</> } return <p {...props} /> } function MDXPage({code}: {code: string}) { const Component = React.useMemo(() => getMDXComponent(code), [code]) return ( <main> <Component components={{p: Paragraph}} /> </main> ) }
你可以在 mdx 内容中引用 frontmatter 元数据或常量。
--- title: 示例文章 --- export const exampleImage = 'https://example.com/image.jpg' # {frontmatter.title} <img src={exampleImage} alt="图片替代文本" />
你可以使用 getMDXExport 而不是 getMDXComponent 来将 mdx 文件视为模块而不仅仅是一个组件。它接受与 getMDXComponent 相同的参数。
--- title: 示例文章 --- export const toc = [{depth: 1, value: '标题'}] # 标题
import * as React from 'react' import {getMDXExport} from 'mdx-bundler/client' function MDXPage({code}: {code: string}) { const mdxExport = getMDXExport(code) console.log(mdxExport.toc) // [ { depth: 1, value: '标题' } ] const Component = React.useMemo(() => mdxExport.default, [code]) return <Component /> }
通过 cwd 和 remark 插件 remark-mdx-images,你可以在你的 mdx 中打包图片!
esbuild 中有两个可以用于此目的的加载器。最简单的是 dataurl,它将图片作为内联数据 URL 输出到返回的代码中。
import {remarkMdxImages} from 'remark-mdx-images' const {code} = await bundleMDX({ source: mdxSource, cwd: '/users/you/site/_content/pages', mdxOptions: options => { options.remarkPlugins = [...(options.remarkPlugins ?? []), remarkMdxImages] ```js return options }, esbuildOptions: options => { options.loader = { ...options.loader, '.png': 'dataurl', } return options }, })
file 加载器需要更多的配置才能工作。使用 file 加载器时,你的图片会被复制到输出目录,因此 esbuild 需要设置为写入文件,并且需要知道把它们放在哪里,以及在图片源中使用的文件夹的 URL。
每次调用
bundleMDX都是相互独立的。如果你为所有内容设置相同的目录,bundleMDX会在不警告的情况下覆盖图片。因此,每个 bundle 需要自己的输出目录。
// 对于文件 `_content/pages/about.mdx` const {code} = await bundleMDX({ source: mdxSource, cwd: '/users/you/site/_content/pages', mdxOptions: options => { options.remarkPlugins = [...(options.remarkPlugins ?? []), remarkMdxImages] return options }, esbuildOptions: options => { // 为这个 bundle 设置 `outdir` 到一个公共位置 options.outdir = '/users/you/site/public/img/about' options.loader = { ...options.loader, // 告诉 esbuild 对 png 使用 `file` 加载器 '.png': 'file', } // 将公共路径设置为 /img/about options.publicPath = '/img/about' // 将 write 设置为 true,这样 esbuild 就会输出文件 options.write = true return options }, })
如果你的 MDX 文件在磁盘上,你可以通过让 mdx-bundler 为你读取文件来节省一些时间和代码。不需要提供 source 字符串,你可以将 file 设置为磁盘上 MDX 的路径。将 cwd 设置为它的文件夹,这样相对导入就能正常工作。
import {bundleMDX} from 'mdx-bundler' const {code, frontmatter} = await bundleMDX({ file: '/users/you/site/content/file.mdx', cwd: '/users/you/site/content/', })
为了确保自定义组件在下游 MDX 文件中可访问, 你可以使用 @mdx-js/react 中的 MDXProvider 将自定义组件传递给嵌套导入。
npm install --save @mdx-js/react
const globals = { '@mdx-js/react': { varName: 'MdxJsReact', namedExports: ['useMDXComponents'], defaultExport: false, }, }; const { code } = bundleMDX({ source, globals, mdxOptions(options: Record<string, any>) { return { ...options, providerImportSource: '@mdx-js/react', }; } });
从那里,你将 code 发送到你的客户端,然后:
import { MDXProvider, useMDXComponents } from '@mdx-js/react'; const MDX_GLOBAL_CONFIG = { MdxJsReact: { useMDXComponents, }, }; export const MDXComponent: React.FC<{ code: string; frontmatter: Record<string, any>; }> = ({ code }) => { const Component = useMemo( () => getMDXComponent(code, MDX_GLOBAL_CONFIG), [code], ); return ( <MDXProvider components={{ Text: ({ children }) => <p>{children}</p> }}> <Component /> </MDXProvider> ); };
我们非常希望这能在 cloudflare workers 中工作。不幸的是,cloudflare 有两个限制阻止了 mdx-bundler 在那个环境中工作:
bundleMDX 使用 esbuild(一个二进制文件)来打包你的 MDX 代码。eval 或类似的东西。getMDXComponent 使用 new Function 评估打包的代码。一个解决方法是将你的 mdx-bundler 相关代码放在不同的环境中,并从 Cloudflare worker 内部调用那个环境。在我看来,这违背了使用 Cloudflare workers 的目的。另一个潜在的解决方法是在 worker 内部使用 WASM。有 esbuild-wasm,但该包存在一些问题,这些问题在该链接中有解释。然后是 wasm-jseval,但我无法让它运行 mdx-bundler 输出的代码而不出错。
如果有人愿意深入研究这个问题,那将是非常棒的,但不幸的是,我可能永远不会去研究它。
esbuild 依赖 __dirname 来确定其可执行文件的位置,Next.JS 和 Webpack 有时会破坏这一点,需要手动告诉 esbuild 去哪里查找。
在 bundleMDX 之前添加以下代码将直接指向你平台的正确可执行文件。
import path from 'path' if (process.platform === 'win32') { process.env.ESBUILD_BINARY_PATH = path.join( process.cwd(), 'node_modules', 'esbuild', 'esbuild.exe', ) } else { process.env.ESBUILD_BINARY_PATH = path.join( process.cwd(), 'node_modules', 'esbuild', 'bin', 'esbuild', ) }
关于这个问题的更多信息可以在这篇文章中找到。
当我正在将 kentcdodds.com 重写为 remix 时,我决定想要保留我的博客文章为 MDX 格式,但我不想在构建时编译它们全部,或者每次修复一个拼写错误就必须重新部署。所以我制作了这个,它允许我的服务器按需编译。
有 next-mdx-remote,但它更像是一个 mdx 编译器而不是打包器(不能为依赖项打包你的 mdx)。此外,它专注于 Next.js,而这个是与元框架无关的。
想要贡献? 寻找 [Good First Issue][good-first-issue] 标签。
请为错误、缺失的文档或意外行为提交问题。
[查看 Bugs][bugs]
请提交问题来建议新功能。通过添加 👍 来为功能请求投票。这有助于维护者确定优先处理的内容。
[查看功能请求][requests]
感谢这些人([emoji key][emojis]):
<!-- ALL-CONTRIBUTORS-LIST:START - 请勿移除或修改此部分 --> <!-- prettier-ignore-start --> <!-- markdownlint-disable --> <table> <tbody> <tr> <td align="center"><a href="https://kentcdodds.com"><img src="https://avatars.githubusercontent.com/u/1500684?v=3?s=100" width="100px;" alt="Kent C. Dodds"/><br /><sub><b>Kent C. Dodds</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=kentcdodds" title="代码">💻</a> <a href="https://github.com/kentcdodds/mdx-bundler/commits?author=kentcdodds" title="文档">📖</a> <a href="#infra-kentcdodds" title="基础设施(托管、构建工具等)">🚇</a> <a href="https://github.com/kentcdodds/mdx-bundler/commits?author=kentcdodds" title="测试">⚠️</a></td> <td align="center"><a href="https://github.com/benwis"><img src="https://avatars.githubusercontent.com/u/6953353?v=4?s=100" width="100px;" alt="benwis"/><br /><sub><b>benwis</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/issues?q=author%3Abenwis" title="Bug报告">🐛</a> <a href="https://github.com/kentcdodds/mdx-bundler/pulls?q=is%3Apr+reviewed-by%3Abenwis" title="审查拉取请求">👀</a></td> <td align="center"><a href="https://www.arcath.net"><img src="https://avatars.githubusercontent.com/u/19609?v=4?s=100" width="100px;" alt="Adam Laycock"/><br /><sub><b>Adam Laycock</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=Arcath" title="代码">💻</a> <a href="https://github.com/kentcdodds/mdx-bundler/commits?author=Arcath" title="测试">⚠️</a> <a href="#ideas-Arcath" title="想法、规划与反馈">🤔</a> <a href="https://github.com/kentcdodds/mdx-bundler/pulls?q=is%3Apr+reviewed-by%3AArcath" title="审查拉取请求">👀</a> <a href="https://github.com/kentcdodds/mdx-bundler/commits?author=Arcath" title="文档">📖</a></td> <td align="center"><a href="http://wooorm.com"><img src="https://avatars.githubusercontent.com/u/944406?v=4?s=100" width="100px;" alt="Titus"/><br /><sub><b>Titus</b></sub></a><br /><a href="#ideas-wooorm" title="想法、规划与反馈">🤔</a> <a href="https://github.com/kentcdodds/mdx-bundler/pulls?q=is%3Apr+reviewed-by%3Awooorm" title="审查拉取请求">👀</a> <a href="https://github.com/kentcdodds/mdx-bundler/commits?author=wooorm" title="代码">💻</a></td> <td align="center"><a href="https://github.com/ChristianMurphy"><img src="https://avatars.githubusercontent.com/u/3107513?v=4?s=100" width="100px;" alt="Christian Murphy"/><br /><sub><b>Christian Murphy</b></sub></a><br /><a href="#ideas-ChristianMurphy" title="想法、规划与反馈">🤔</a></td> <td align="center"><a href="https://ped.ro"><img src="https://avatars.githubusercontent.com/u/372831?v=4?s=100" width="100px;" alt="Pedro Duarte"/><br /><sub><b>Pedro Duarte</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=peduarte" title="文档">📖</a></td> <td align="center"><a href="https://keybase.io/erikras"><img src="https://avatars.githubusercontent.com/u/4396759?v=4?s=100" width="100px;" alt="Erik Rasmussen"/><br /><sub><b>Erik Rasmussen</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=erikras" title="文档">📖</a></td> </tr> <tr> <td align="center"><a href="https://github.com/ozyxdev"><img src="https://avatars.githubusercontent.com/u/83309085?v=4?s=100" width="100px;" alt="Omar Syx"/><br /><sub><b>Omar Syx</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/issues?q=author%3Aozyxdev" title="Bug报告">🐛</a></td> <td align="center"><a href="https://github.com/gaelhameon"><img src="https://avatars.githubusercontent.com/u/17253950?v=4?s=100" width="100px;" alt="Gaël Haméon"/><br /><sub><b>Gaël Haméon</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=gaelhameon" title="文档">📖</a></td> <td align="center"><a href="https://github.com/loiacon"><img src="https://avatars.githubusercontent.com/u/32134586?v=4?s=100" width="100px;" alt="Gabriel Loiácono"/><br /><sub><b>Gabriel Loiácono</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=loiacon" title="代码">💻</a> <a href="https://github.com/kentcdodds/mdx-bundler/commits?author=loiacon" title="测试">⚠️</a></td> <td align="center"><a href="https://skovy.dev"><img src="https://avatars.githubusercontent.com/u/5247455?v=4?s=100" width="100px;" alt="Spencer Miskoviak"/><br /><sub><b>Spencer Miskoviak</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=skovy" title="文档">📖</a></td> <td align="center"><a href="https://caspertheghost.me"><img src="https://avatars.githubusercontent.com/u/53900565?v=4?s=100" width="100px;" alt="Casper"/><br /><sub><b>Casper</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=Dev-CasperTheGhost" title="代码">💻</a></td> <td align="center"><a href="http://a7sc11u.dev"><img src="https://avatars.githubusercontent.com/u/803868?v=4?s=100" width="100px;" alt="Apostolos Christodoulou"/><br /><sub><b>Apostolos Christodoulou</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=a7sc11u" title="文档">📖</a></td> <td align="center"><a href="https://github.com/yordis"><img src="https://avatars.githubusercontent.com/u/4237280?v=4?s=100" width="100px;" alt="Yordis Prieto"/><br /><sub><b>Yordis Prieto</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=yordis" title="代码">💻</a></td> </tr> <tr> <td align="center"><a href="https://github.com/xoumi"><img src="https://avatars.githubusercontent.com/u/24864287?v=4?s=100" width="100px;" alt="xoumi"/><br /><sub><b>xoumi</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=xoumi" title="代码">💻</a></td> <td align="center"><a href="http://yasint.dev"><img src="https://avatars.githubusercontent.com/u/25561152?v=4?s=100" width="100px;" alt="Yasin"/><br /><sub><b>Yasin</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=yasinmiran" title="代码">💻</a></td> <td align="center"><a href="https://moweb.dev"><img src="https://avatars.githubusercontent.com/u/22095656?v=4?s=100" width="100px;" alt="Mohammed 'Mo' Mulazada"/><br /><sub><b>Mohammed 'Mo' Mulazada</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=moniac" title="文档">📖</a></td> <td align="center"><a href="https://www.canrau.com"><img src="https://avatars.githubusercontent.com/u/5196971?v=4?s=100" width="100px;" alt="Can Rau"/><br /><sub><b>Can Rau</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=CanRau" title="文档">📖</a></td> <td align="center"><a href="http://hosenur.dev"><img src="https://avatars.githubusercontent.com/u/50978981?v=4?s=100" width="100px;" alt="Hosenur Rahaman"/><br /><sub><b>Hosenur Rahaman</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=HOSENUR" title="文档">📖</a></td> <td align="center"><a href="https://macieksitkowski.com"><img src="https://avatars.githubusercontent.com/u/58401630?v=4?s=100" width="100px;" alt="Maciek Sitkowski"/><br /><sub><b>Maciek Sitkowski</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=sitek94" title="文档">📖</a></td> <td align="center"><a href="https://github.com/priyang12"><img src="https://avatars.githubusercontent.com/u/72823974?v=4?s=100" width="100px;" alt="Priyang"/><br /><sub><b>Priyang</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=priyang12" title="代码">💻</a> <a href="https://github.com/kentcdodds/mdx-bundler/commits?author=priyang12" title="文档">📖</a></td> </tr> <tr> <td align="center"><a href="https://github.com/theMosaad"><img src="https://avatars.githubusercontent.com/u/48773133?v=4?s=100" width="100px;" alt="Mosaad"/><br /><sub><b>Mosaad</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=theMosaad" title="文档">📖</a></td> <td align="center"><a href="https://github.com/stefanprobst"><img src="https://avatars.githubusercontent.com/u/20753323?v=4?s=100" width="100px;" alt="stefanprobst"/><br /><sub><b>stefanprobst</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=stefanprobst" title="代码">💻</a> <a href="https://github.com/kentcdodds/mdx-bundler/commits?author=stefanprobst" title="测试">⚠️</a></td> <td align="center"><a href="https://vladmoroz.com"><img src="https://avatars.githubusercontent.com/u/8441036?v=4?s=100" width="100px;" alt="Vlad Moroz"/><br /><sub><b>Vlad Moroz</b></sub></a><br /><a href="https://github.com/kentcdodds/mdx-bundler/commits?author=vladmoroz" title="代码">💻</a></td> </tr> </tbody> </table> 本项目遵循 [all-contributors] 规范。 欢迎任何形式的贡献!MIT


AI一键生成PPT,就用博思AIPPT!
博思AIPPT,新一代的AI生成PPT平台,支持智能生成PPT、AI美化PPT、文本&链接生成PPT、导入Word/PDF/Markdown文档生成PPT等,内置海量精美PPT模板,涵盖商务、教育、科技等不同风格,同时针对每个页面提供多种版式,一键自适应切换,完美适配各种办公场景。


AI赋能电商视觉革命,一站式智能商拍平台
潮际好麦深耕服装行业,是国内AI试衣效果最好的软件。使用先进AIGC能力为电商卖家批量提供优质的、低成本的商拍图。合作品牌有Shein、Lazada、安踏、百丽等65个国内外头部品牌,以及国内10万+淘宝、天猫、京东等主流平台的品牌商家,为卖家节省将近85%的出图成本,提升约3倍出图效率,让品牌能够快速上架。


企业专属的AI法律顾问
iTerms是法大大集团旗下法律子品牌,基于最先进的大语言模型(LLM)、专业的法律知识库和强大的智能体架构,帮助企业扫清合规障碍,筑牢风控防线,成为您企业专属的AI法律顾问。


稳定高效的流量提升解决方案,助力品牌曝光
稳定高效的流量提升解决方案,助力品牌曝光


最新版Sora2模型免费使用,一键生成无水印视频
最新版Sora2模型免费使用,一键生成无水印视频


实时语音翻译/同声传译工具
Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。


选题、配图、成文,一站式创作,让内容运营更高效
讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。


AI辅助编程,代码自动修复
Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。


最强AI数据分析助手
小浣熊家族Raccoon,您的AI智能助手,致力于通过先进的人工智能技术,为用户提供高效、便捷的智能服务。无论是日常咨询还是专业问题解答,小浣熊都能以快速、准确的响应满足您的需求,让您的生活更加智能便捷。


像人一样思考的AI智能体
imini 是一款超级AI智能体,能根据人类指令,自主思考、自主完成、并且交付结果的AI智能体。
最新AI工具、AI资讯
独家AI资源、AI项目落地

微信扫一扫关注公众号