├── .all-contributorsrc
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── validate.yml
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc.cjs
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── client
├── index.d.ts
├── index.js
├── jsx.d.ts
├── jsx.js
├── package.json
├── react.d.ts
└── react.js
├── other
├── 150.png
├── MAINTAINING.md
├── USERS.md
├── cjs-ify.js
├── manual-releases.md
├── sample-component.jsx
└── sample.mdx
├── package.json
├── src
├── __tests__
│ ├── hono.js
│ ├── index.js
│ ├── preact.js
│ ├── setup-tests.js
│ └── vue.js
├── client
│ ├── index.js
│ ├── jsx.js
│ └── react.js
├── dirname-messed-up.cjs
├── dirname-messed-up.cjs.d.ts
├── index.js
└── types.d.ts
└── tsconfig.json
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "mdx-bundler",
3 | "projectOwner": "kentcdodds",
4 | "imageSize": 100,
5 | "commit": false,
6 | "contributorsPerLine": 7,
7 | "repoHost": "https://github.com",
8 | "repoType": "github",
9 | "skipCi": false,
10 | "files": [
11 | "README.md"
12 | ],
13 | "contributors": [
14 | {
15 | "login": "kentcdodds",
16 | "name": "Kent C. Dodds",
17 | "avatar_url": "https://avatars.githubusercontent.com/u/1500684?v=3",
18 | "profile": "https://kentcdodds.com",
19 | "contributions": [
20 | "code",
21 | "doc",
22 | "infra",
23 | "test"
24 | ]
25 | },
26 | {
27 | "login": "benwis",
28 | "name": "benwis",
29 | "avatar_url": "https://avatars.githubusercontent.com/u/6953353?v=4",
30 | "profile": "https://github.com/benwis",
31 | "contributions": [
32 | "bug",
33 | "review"
34 | ]
35 | },
36 | {
37 | "login": "Arcath",
38 | "name": "Adam Laycock",
39 | "avatar_url": "https://avatars.githubusercontent.com/u/19609?v=4",
40 | "profile": "https://www.arcath.net",
41 | "contributions": [
42 | "code",
43 | "test",
44 | "ideas",
45 | "review",
46 | "doc"
47 | ]
48 | },
49 | {
50 | "login": "wooorm",
51 | "name": "Titus",
52 | "avatar_url": "https://avatars.githubusercontent.com/u/944406?v=4",
53 | "profile": "http://wooorm.com",
54 | "contributions": [
55 | "ideas",
56 | "review",
57 | "code"
58 | ]
59 | },
60 | {
61 | "login": "ChristianMurphy",
62 | "name": "Christian Murphy",
63 | "avatar_url": "https://avatars.githubusercontent.com/u/3107513?v=4",
64 | "profile": "https://github.com/ChristianMurphy",
65 | "contributions": [
66 | "ideas"
67 | ]
68 | },
69 | {
70 | "login": "peduarte",
71 | "name": "Pedro Duarte",
72 | "avatar_url": "https://avatars.githubusercontent.com/u/372831?v=4",
73 | "profile": "https://ped.ro",
74 | "contributions": [
75 | "doc"
76 | ]
77 | },
78 | {
79 | "login": "erikras",
80 | "name": "Erik Rasmussen",
81 | "avatar_url": "https://avatars.githubusercontent.com/u/4396759?v=4",
82 | "profile": "https://keybase.io/erikras",
83 | "contributions": [
84 | "doc"
85 | ]
86 | },
87 | {
88 | "login": "ozyxdev",
89 | "name": "Omar Syx",
90 | "avatar_url": "https://avatars.githubusercontent.com/u/83309085?v=4",
91 | "profile": "https://github.com/ozyxdev",
92 | "contributions": [
93 | "bug"
94 | ]
95 | },
96 | {
97 | "login": "gaelhameon",
98 | "name": "Gaël Haméon",
99 | "avatar_url": "https://avatars.githubusercontent.com/u/17253950?v=4",
100 | "profile": "https://github.com/gaelhameon",
101 | "contributions": [
102 | "doc"
103 | ]
104 | },
105 | {
106 | "login": "loiacon",
107 | "name": "Gabriel Loiácono",
108 | "avatar_url": "https://avatars.githubusercontent.com/u/32134586?v=4",
109 | "profile": "https://github.com/loiacon",
110 | "contributions": [
111 | "code",
112 | "test"
113 | ]
114 | },
115 | {
116 | "login": "skovy",
117 | "name": "Spencer Miskoviak",
118 | "avatar_url": "https://avatars.githubusercontent.com/u/5247455?v=4",
119 | "profile": "https://skovy.dev",
120 | "contributions": [
121 | "doc"
122 | ]
123 | },
124 | {
125 | "login": "Dev-CasperTheGhost",
126 | "name": "Casper",
127 | "avatar_url": "https://avatars.githubusercontent.com/u/53900565?v=4",
128 | "profile": "https://caspertheghost.me",
129 | "contributions": [
130 | "code"
131 | ]
132 | },
133 | {
134 | "login": "a7sc11u",
135 | "name": "Apostolos Christodoulou",
136 | "avatar_url": "https://avatars.githubusercontent.com/u/803868?v=4",
137 | "profile": "http://a7sc11u.dev",
138 | "contributions": [
139 | "doc"
140 | ]
141 | },
142 | {
143 | "login": "yordis",
144 | "name": "Yordis Prieto",
145 | "avatar_url": "https://avatars.githubusercontent.com/u/4237280?v=4",
146 | "profile": "https://github.com/yordis",
147 | "contributions": [
148 | "code"
149 | ]
150 | },
151 | {
152 | "login": "xoumi",
153 | "name": "xoumi",
154 | "avatar_url": "https://avatars.githubusercontent.com/u/24864287?v=4",
155 | "profile": "https://github.com/xoumi",
156 | "contributions": [
157 | "code"
158 | ]
159 | },
160 | {
161 | "login": "yasinmiran",
162 | "name": "Yasin",
163 | "avatar_url": "https://avatars.githubusercontent.com/u/25561152?v=4",
164 | "profile": "http://yasint.dev",
165 | "contributions": [
166 | "code"
167 | ]
168 | },
169 | {
170 | "login": "moniac",
171 | "name": "Mohammed 'Mo' Mulazada",
172 | "avatar_url": "https://avatars.githubusercontent.com/u/22095656?v=4",
173 | "profile": "https://moweb.dev",
174 | "contributions": [
175 | "doc"
176 | ]
177 | },
178 | {
179 | "login": "CanRau",
180 | "name": "Can Rau",
181 | "avatar_url": "https://avatars.githubusercontent.com/u/5196971?v=4",
182 | "profile": "https://www.canrau.com",
183 | "contributions": [
184 | "doc"
185 | ]
186 | },
187 | {
188 | "login": "HOSENUR",
189 | "name": "Hosenur Rahaman",
190 | "avatar_url": "https://avatars.githubusercontent.com/u/50978981?v=4",
191 | "profile": "http://hosenur.dev",
192 | "contributions": [
193 | "doc"
194 | ]
195 | },
196 | {
197 | "login": "sitek94",
198 | "name": "Maciek Sitkowski",
199 | "avatar_url": "https://avatars.githubusercontent.com/u/58401630?v=4",
200 | "profile": "https://macieksitkowski.com",
201 | "contributions": [
202 | "doc"
203 | ]
204 | },
205 | {
206 | "login": "priyang12",
207 | "name": "Priyang",
208 | "avatar_url": "https://avatars.githubusercontent.com/u/72823974?v=4",
209 | "profile": "https://github.com/priyang12",
210 | "contributions": [
211 | "code",
212 | "doc"
213 | ]
214 | },
215 | {
216 | "login": "theMosaad",
217 | "name": "Mosaad",
218 | "avatar_url": "https://avatars.githubusercontent.com/u/48773133?v=4",
219 | "profile": "https://github.com/theMosaad",
220 | "contributions": [
221 | "doc"
222 | ]
223 | },
224 | {
225 | "login": "stefanprobst",
226 | "name": "stefanprobst",
227 | "avatar_url": "https://avatars.githubusercontent.com/u/20753323?v=4",
228 | "profile": "https://github.com/stefanprobst",
229 | "contributions": [
230 | "code",
231 | "test"
232 | ]
233 | },
234 | {
235 | "login": "vladmoroz",
236 | "name": "Vlad Moroz",
237 | "avatar_url": "https://avatars.githubusercontent.com/u/8441036?v=4",
238 | "profile": "https://vladmoroz.com",
239 | "contributions": [
240 | "code"
241 | ]
242 | }
243 | ],
244 | "commitConvention": "none"
245 | }
246 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
13 |
14 | - `mdx-bundler` version:
15 | - `node` version:
16 | - `npm` version:
17 |
18 | Relevant code or config
19 |
20 | ```js
21 |
22 | ```
23 |
24 | What you did:
25 |
26 | What happened:
27 |
28 |
29 |
30 | Reproduction repository:
31 |
32 |
36 |
37 | Problem description:
38 |
39 | Suggested solution:
40 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | **What**:
20 |
21 |
22 |
23 | **Why**:
24 |
25 |
26 |
27 | **How**:
28 |
29 |
30 |
31 | **Checklist**:
32 |
33 |
34 |
35 |
36 | - [ ] Documentation
37 | - [ ] Tests
38 | - [ ] Ready to be merged
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/.github/workflows/validate.yml:
--------------------------------------------------------------------------------
1 | name: validate
2 | on:
3 | push:
4 | branches:
5 | - '+([0-9])?(.{+([0-9]),x}).x'
6 | - 'main'
7 | - 'next'
8 | - 'next-major'
9 | - 'beta'
10 | - 'alpha'
11 | - '!all-contributors/**'
12 | pull_request:
13 |
14 | concurrency:
15 | group: ${{ github.workflow }}-${{ github.ref }}
16 | cancel-in-progress: true
17 |
18 | jobs:
19 | main:
20 | # ignore all-contributors PRs
21 | if: ${{ !contains(github.head_ref, 'all-contributors') }}
22 | strategy:
23 | matrix:
24 | node: [18, 20]
25 | runs-on: ubuntu-latest
26 | steps:
27 | - name: ⬇️ Checkout repo
28 | uses: actions/checkout@v2
29 |
30 | - name: ⎔ Setup node
31 | uses: actions/setup-node@v2
32 | with:
33 | node-version: ${{ matrix.node }}
34 |
35 | - name: 📥 Download deps
36 | uses: bahmutov/npm-install@v1
37 | with:
38 | useLockFile: false
39 | env:
40 | HUSKY_SKIP_INSTALL: true
41 |
42 | - name: ▶️ Run validate script
43 | run: npm run validate
44 |
45 | - name: ⬆️ Upload coverage report
46 | uses: codecov/codecov-action@v1
47 |
48 | release:
49 | needs: main
50 | runs-on: ubuntu-latest
51 | if:
52 | ${{ github.repository == 'kentcdodds/mdx-bundler' &&
53 | contains('refs/heads/main,refs/heads/beta,refs/heads/next,refs/heads/alpha',
54 | github.ref) && github.event_name == 'push' }}
55 | steps:
56 | - name: ⬇️ Checkout repo
57 | uses: actions/checkout@v2
58 |
59 | - name: ⎔ Setup node
60 | uses: actions/setup-node@v2
61 | with:
62 | node-version: 20
63 |
64 | - name: 📥 Download deps
65 | uses: bahmutov/npm-install@v1
66 | with:
67 | useLockFile: false
68 | env:
69 | HUSKY_SKIP_INSTALL: true
70 |
71 | - name: 🏗 Run build script
72 | run: npm run build
73 |
74 | - name: 🚀 Release
75 | uses: cycjimmy/semantic-release-action@v2
76 | with:
77 | semantic_version: 17
78 | branches: |
79 | [
80 | '+([0-9])?(.{+([0-9]),x}).x',
81 | 'main',
82 | 'next',
83 | 'next-major',
84 | {name: 'beta', prerelease: true},
85 | {name: 'alpha', prerelease: true}
86 | ]
87 | env:
88 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
89 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
90 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | dist
4 | .DS_Store
5 | output/
6 |
7 | # these cause more harm than good
8 | # when working with contributors
9 | package-lock.json
10 | yarn.lock
11 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | dist
4 |
--------------------------------------------------------------------------------
/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = require('kcd-scripts/prettier')
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | The changelog is automatically updated using
4 | [semantic-release](https://github.com/semantic-release/semantic-release). You
5 | can see it on the [releases page](../../releases).
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | - Demonstrating empathy and kindness toward other people
21 | - Being respectful of differing opinions, viewpoints, and experiences
22 | - Giving and gracefully accepting constructive feedback
23 | - Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | - Focusing on what is best not just for us as individuals, but for the overall
26 | community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | - The use of sexualized language or imagery, and sexual attention or advances of
31 | any kind
32 | - Trolling, insulting or derogatory comments, and personal or political attacks
33 | - Public or private harassment
34 | - Publishing others' private information, such as a physical or email address,
35 | without their explicit permission
36 | - Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | me+coc@kentcdodds.com. All complaints will be reviewed and investigated promptly
64 | and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series of
86 | actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or permanent
93 | ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within the
113 | community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by
122 | [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for being willing to contribute!
4 |
5 | **Working on your first Pull Request?** You can learn how from this _free_
6 | series [How to Contribute to an Open Source Project on GitHub][egghead]
7 |
8 | ## Project setup
9 |
10 | 1. Fork and clone the repo
11 | 2. Run `npm run setup -s` to install dependencies and run validation
12 | 3. Create a branch for your PR with `git checkout -b pr/your-branch-name`
13 |
14 | > Tip: Keep your `main` branch pointing at the original repository and make pull
15 | > requests from branches on your fork. To do this, run:
16 | >
17 | > ```
18 | > git remote add upstream https://github.com/kentcdodds/mdx-bundler
19 | > git fetch upstream
20 | > git branch --set-upstream-to=upstream/main main
21 | > ```
22 | >
23 | > This will add the original repository as a "remote" called "upstream," Then
24 | > fetch the git information from that remote, then set your local `main` branch
25 | > to use the upstream main branch whenever you run `git pull`. Then you can make
26 | > all of your pull request branches based on this `main` branch. Whenever you
27 | > want to update your version of `main`, do a regular `git pull`.
28 |
29 | ## Committing and Pushing changes
30 |
31 | Please make sure to run the tests before you commit your changes. You can run
32 | `npm run test` which will update any snapshots that need updating. Make sure to
33 | include those changes (if they exist) in your commit.
34 |
35 | ## Help needed
36 |
37 | Please checkout [the open issues][issues]
38 |
39 | Also, please watch the repo and respond to questions/bug reports/feature
40 | requests! Thanks!
41 |
42 |
43 | [egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github
44 | [issues]: https://github.com/kentcdodds/mdx-bundler/issues
45 |
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2020 Kent C. Dodds
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
mdx-bundler 🦤
3 |
4 |
Compile and bundle your MDX files and their dependencies. FAST.
5 |
6 |
7 | ---
8 |
9 |
10 | [![Build Status][build-badge]][build]
11 | [![Code Coverage][coverage-badge]][coverage]
12 | [![version][version-badge]][package]
13 | [![downloads][downloads-badge]][npmtrends]
14 | [![MIT License][license-badge]][license]
15 | [![All Contributors][all-contributors-badge]](#contributors-)
16 | [![PRs Welcome][prs-badge]][prs]
17 | [![Code of Conduct][coc-badge]][coc]
18 |
19 |
20 | ## The problem
21 |
22 | You have a string of MDX and various TS/JS files that it uses and you want to
23 | get a bundled version of these files to eval in the browser.
24 |
25 | ## This solution
26 |
27 | This is an async function that will compile and bundle your MDX files and their
28 | dependencies. It uses [MDX v3](https://mdxjs.com/blog/v3/) and
29 | [esbuild](https://esbuild.github.io/), so it's VERY fast and supports TypeScript
30 | files (for the dependencies of your MDX files).
31 |
32 | Your source files could be local, in a remote github repo, in a CMS, or wherever
33 | else and it doesn't matter. All `mdx-bundler` cares about is that you pass it
34 | all the files and source code necessary and it will take care of bundling
35 | everything for you.
36 |
37 | ### FAQ:
38 |
39 |
40 |
41 |
42 | "What's so cool about MDX?"
43 |
44 |
45 |
46 | [MDX](https://mdxjs.com/) enables you to combine terse markdown syntax for your
47 | content with the power of JSX components. For content-heavy sites, writing the
48 | content with straight-up HTML can be annoyingly verbose. Often people solve this
49 | using a WSYWIG editor, but too often those fall short in mapping the writer's
50 | intent to HTML. Many people prefer using markdown to express their content
51 | source and have that parsed into HTML to be rendered.
52 |
53 | The problem with using Markdown for your content is if you want to have some
54 | interactivity embedded into your content, you're pretty limited. You either need
55 | to insert an element that JavaScript targets (which is annoyingly indirect), or
56 | you can use an `iframe` or something.
57 |
58 | As previously stated, [MDX](https://mdxjs.com/) enables you to combine terse
59 | markdown syntax for your content with the power of JSX components. So you can
60 | import a JSX component and render it within the markdown itself. It's the best
61 | of both worlds.
62 |
63 |
64 |
65 |
66 |
67 |
68 | "How is this different from next-mdx-remote?"
69 |
70 |
71 |
72 | `mdx-bundler` actually bundles dependencies of your MDX files. For example, this
73 | won't work with `next-mdx-remote`, but it will with `mdx-bundler`:
74 |
75 | ```md
76 | ---
77 | title: Example Post
78 | published: 2021-02-13
79 | description: This is some description
80 | ---
81 |
82 | # Wahoo
83 |
84 | import Demo from './demo'
85 |
86 | Here's a **neat** demo:
87 |
88 |
89 | ```
90 |
91 | `next-mdx-remote` chokes on that import because it's not a bundler, it's just a
92 | compiler. `mdx-bundler` is an MDX compiler and bundler. That's the difference.
93 |
94 |
95 |
96 |
97 |
98 |
99 | "How is this different from the mdx plugins for webpack or rollup?"
100 |
101 |
102 |
103 | Those tools are intended to be run "at build time" and then you deploy the built
104 | version of your files. This means if you have some content in MDX and want to
105 | make a typo change, you have to rebuild and redeploy the whole site. This also
106 | means that every MDX page you add to your site will increase your build-times,
107 | so it doesn't scale all that well.
108 |
109 | `mdx-bundler` can definitely be used at build-time, but it's more powerfully
110 | used as a runtime bundler. A common use case is to have a route for your MDX
111 | content and when that request comes in, you load the MDX content and hand that
112 | off to `mdx-bundler` for bundling. This means that `mdx-bundler` is infinitely
113 | scalable. Your build won't be any longer regardless of how much MDX content you
114 | have. Also, `mdx-bundler` is quite fast, but to make this on-demand bundling
115 | even faster, you can use appropriate cache headers to avoid unnecessary
116 | re-bundling.
117 |
118 | Webpack/rollup/etc also require that all your MDX files are on the local
119 | filesystem to work. If you want to store your MDX content in a separate repo or
120 | CMS, you're kinda out of luck or have to do some build-time gymnastics to get
121 | the files in place for the build.
122 |
123 | With `mdx-bundler`, it doesn't matter where your MDX content comes from, you can
124 | bundle files from anywhere, you're just responsible for getting the content into
125 | memory and then you hand that off to `mdx-bundler` for bundling.
126 |
127 |
128 |
129 |
130 |
131 |
132 | "Does this work with Remix/Gatsby/Next/CRA/etc?"
133 |
134 |
135 |
136 | Totally. It works with any of those tools. Depending on whether your
137 | meta-framework supports server-side rendering, you'll implement it differently.
138 | You might decide to go with a built-time approach (for Gatsby/CRA), but as
139 | mentioned, the true power of `mdx-bundler` comes in the form of on-demand
140 | bundling. So it's best suited for SSR frameworks like Remix/Next.
141 |
142 |
143 |
144 |
145 |
146 |
147 | "Can I use this other JSX libraries other than React?"
148 |
149 |
150 |
151 | Yes! If JSX runtime you want to use is mentioned here - https://mdxjs.com/docs/getting-started/#jsx, it's guaranteed to work. Libraries, such as `hono` which has `react` compatible API also works. Check to [Other JSX runtimes](#other-jsx-runtimes) to get started.
152 |
153 |
154 |
155 |
156 |
157 | "Why the dodo bird emoji? 🦤"
158 |
159 |
160 |
161 | Why not?
162 |
163 |
164 |
165 |
166 |
167 |
168 | "Why is esbuild a peer dependency?"
169 |
170 |
171 |
172 | esbuild provides a service written in GO that it interacts with. Only one
173 | instance of this service can run at a time and it must have an identical version
174 | to the npm package. If it was a hard dependency you would only be able to use
175 | the esbuild version mdx-bundler uses.
176 |
177 |
178 |
179 | ## Table of Contents
180 |
181 |
182 |
183 |
184 | - [Installation](#installation)
185 | - [Usage](#usage)
186 | - [Options](#options)
187 | - [Returns](#returns)
188 | - [Types](#types)
189 | - [Component Substitution](#component-substitution)
190 | - [Frontmatter and const](#frontmatter-and-const)
191 | - [Accessing named exports](#accessing-named-exports)
192 | - [Image Bundling](#image-bundling)
193 | - [Bundling a file.](#bundling-a-file)
194 | - [Custom Components in Downstream Files](#custom-components-in-downstream-files)
195 | - [Known Issues](#known-issues)
196 | - [Inspiration](#inspiration)
197 | - [Other Solutions](#other-solutions)
198 | - [Issues](#issues)
199 | - [🐛 Bugs](#-bugs)
200 | - [💡 Feature Requests](#-feature-requests)
201 | - [Contributors ✨](#contributors-)
202 | - [LICENSE](#license)
203 |
204 |
205 |
206 | ## Installation
207 |
208 | This module is distributed via [npm][npm] which is bundled with [node][node] and
209 | should be installed as one of your project's `dependencies`:
210 |
211 | ```
212 | npm install --save mdx-bundler esbuild
213 | ```
214 |
215 | One of mdx-bundler's dependencies requires a working [node-gyp][node-gyp] setup
216 | to be able to install correctly.
217 |
218 | ## Usage
219 |
220 | ```typescript
221 | import {bundleMDX} from 'mdx-bundler'
222 |
223 | const mdxSource = `
224 | ---
225 | title: Example Post
226 | published: 2021-02-13
227 | description: This is some description
228 | ---
229 |
230 | # Wahoo
231 |
232 | import Demo from './demo'
233 |
234 | Here's a **neat** demo:
235 |
236 |
237 | `.trim()
238 |
239 | const result = await bundleMDX({
240 | source: mdxSource,
241 | files: {
242 | './demo.tsx': `
243 | import * as React from 'react'
244 |
245 | function Demo() {
246 | return
Neat demo!
247 | }
248 |
249 | export default Demo
250 | `,
251 | },
252 | })
253 |
254 | const {code, frontmatter} = result
255 | ```
256 |
257 | From there, you send the `code` to your client, and then:
258 |
259 | ```jsx
260 | import * as React from 'react'
261 | import {getMDXComponent} from 'mdx-bundler/client'
262 |
263 | function Post({code, frontmatter}) {
264 | // it's generally a good idea to memoize this function call to
265 | // avoid re-creating the component every render.
266 | const Component = React.useMemo(() => getMDXComponent(code), [code])
267 | return (
268 | <>
269 |
270 |
296 |
297 | ```
298 |
299 | ### Options
300 |
301 | #### source
302 |
303 | The `string` source of your MDX.
304 |
305 | _Can not be set if `file` is set_
306 |
307 | #### file
308 |
309 | The path to the file on your disk with the MDX in. You will probably want to
310 | set [cwd](#cwd) as well.
311 |
312 | _Can not be set if `source` is set_
313 |
314 | #### files
315 |
316 | The `files` config is an object of all the files you're bundling. The key is the
317 | path to the file (relative to the MDX source) and the value is the string of the
318 | file source code. You could get these from the filesystem or from a remote
319 | database. If your MDX doesn't reference other files (or only imports things from
320 | `node_modules`), then you can omit this entirely.
321 |
322 | #### mdxOptions
323 |
324 | This allows you to modify the built-in MDX configuration (passed to
325 | `@mdx-js/esbuild`). This can be helpful for specifying your own
326 | remarkPlugins/rehypePlugins.
327 |
328 | The function is passed the default mdxOptions and the frontmatter.
329 |
330 | ```ts
331 | bundleMDX({
332 | source: mdxSource,
333 | mdxOptions(options, frontmatter) {
334 | // this is the recommended way to add custom remark/rehype plugins:
335 | // The syntax might look weird, but it protects you in case we add/remove
336 | // plugins in the future.
337 | options.remarkPlugins = [...(options.remarkPlugins ?? []), myRemarkPlugin]
338 | options.rehypePlugins = [...(options.rehypePlugins ?? []), myRehypePlugin]
339 |
340 | return options
341 | },
342 | })
343 | ```
344 |
345 | #### esbuildOptions
346 |
347 | You can customize any of esbuild options with the option `esbuildOptions`. This
348 | takes a function which is passed the default esbuild options and the frontmatter
349 | and expects an options object to be returned.
350 |
351 | ```typescript
352 | bundleMDX({
353 | source: mdxSource,
354 | esbuildOptions(options, frontmatter) {
355 | options.minify = false
356 | options.target = [
357 | 'es2020',
358 | 'chrome58',
359 | 'firefox57',
360 | 'safari11',
361 | 'edge16',
362 | 'node12',
363 | ]
364 |
365 | return options
366 | },
367 | })
368 | ```
369 |
370 | More information on the available options can be found in the
371 | [esbuild documentation](https://esbuild.github.io/api/#build-api).
372 |
373 | It's recommended to use this feature to configure the `target` to your desired
374 | output, otherwise, esbuild defaults to `esnext` which is to say that it doesn't
375 | compile any standardized features so it's possible users of older browsers will
376 | experience errors.
377 |
378 | #### globals
379 |
380 | This tells esbuild that a given module is externally available. For example, if
381 | your MDX file uses the d3 library and you're already using the d3 library in
382 | your app then you'll end up shipping `d3` to the user twice (once for your app
383 | and once for this MDX component). This is wasteful and you'd be better off just
384 | telling esbuild to _not_ bundle `d3` and you can pass it to the component
385 | yourself when you call `getMDXComponent`.
386 |
387 | Global external configuration options:
388 | https://www.npmjs.com/package/@fal-works/esbuild-plugin-global-externals
389 |
390 | Here's an example:
391 |
392 | ```tsx
393 | // server-side or build-time code that runs in Node:
394 | import {bundleMDX} from 'mdx-bundler'
395 |
396 | const mdxSource = `
397 | # This is the title
398 |
399 | import leftPad from 'left-pad'
400 |
401 |
{leftPad("Neat demo!", 12, '!')}
402 | `.trim()
403 |
404 | const result = await bundleMDX({
405 | source: mdxSource,
406 | // NOTE: this is *only* necessary if you want to share deps between your MDX
407 | // file bundle and the host app. Otherwise, all deps will just be bundled.
408 | // So it'll work either way, this is just an optimization to avoid sending
409 | // multiple copies of the same library to your users.
410 | globals: {'left-pad': 'myLeftPad'},
411 | })
412 | ```
413 |
414 | ```tsx
415 | // server-rendered and/or client-side code that can run in the browser or Node:
416 | import * as React from 'react'
417 | import leftPad from 'left-pad'
418 | import {getMDXComponent} from 'mdx-bundler/client'
419 |
420 | function MDXPage({code}: {code: string}) {
421 | const Component = React.useMemo(
422 | () => getMDXComponent(result.code, {myLeftPad: leftPad}),
423 | [result.code, leftPad],
424 | )
425 | return (
426 |
427 |
428 |
429 | )
430 | }
431 | ```
432 |
433 | #### cwd
434 |
435 | Setting `cwd` (_current working directory_) to a directory will allow esbuild to
436 | resolve imports. This directory could be the directory the mdx content was read
437 | from or a directory that off-disk mdx should be _run_ in.
438 |
439 | _content/pages/demo.tsx_
440 |
441 | ```typescript
442 | import * as React from 'react'
443 |
444 | function Demo() {
445 | return
Neat demo!
446 | }
447 |
448 | export default Demo
449 | ```
450 |
451 | _src/build.ts_
452 |
453 | ```typescript
454 | import {bundleMDX} from 'mdx-bundler'
455 |
456 | const mdxSource = `
457 | ---
458 | title: Example Post
459 | published: 2021-02-13
460 | description: This is some description
461 | ---
462 |
463 | # Wahoo
464 |
465 | import Demo from './demo'
466 |
467 | Here's a **neat** demo:
468 |
469 |
470 | `.trim()
471 |
472 | const result = await bundleMDX({
473 | source: mdxSource,
474 | cwd: '/users/you/site/_content/pages',
475 | })
476 |
477 | const {code, frontmatter} = result
478 | ```
479 |
480 | #### grayMatterOptions
481 |
482 | This allows you to configure the
483 | [gray-matter options](https://github.com/jonschlinkert/gray-matter#options).
484 |
485 | Your function is passed the current gray-matter configuration for you to modify.
486 | Return your modified configuration object for gray matter.
487 |
488 | ```js
489 | bundleMDX({
490 | grayMatterOptions: options => {
491 | options.excerpt = true
492 |
493 | return options
494 | },
495 | })
496 | ```
497 |
498 | #### bundleDirectory & bundlePath
499 |
500 | This allows you to set the output directory for the bundle and the public URL to
501 | the directory. If one option is set the other must be as well.
502 |
503 | _The Javascript bundle is not written to this directory and is still returned as
504 | a string from `bundleMDX`._
505 |
506 | This feature is best used with tweaks to `mdxOptions` and `esbuildOptions`. In
507 | the example below `.png` files are written to the disk and then served from
508 | `/file/`.
509 |
510 | This allows you to store assets with your MDX and then have esbuild process them
511 | like anything else.
512 |
513 | _It is recommended that each bundle has its own `bundleDirectory` so that
514 | multiple bundles don't overwrite each others assets._
515 |
516 | ```ts
517 | const {code} = await bundleMDX({
518 | file: '/path/to/site/content/file.mdx',
519 | cwd: '/path/to/site/content',
520 | bundleDirectory: '/path/to/site/public/file',
521 | bundlePath: '/file/',
522 | mdxOptions: options => {
523 | options.remarkPlugins = [remarkMdxImages]
524 |
525 | return options
526 | },
527 | esbuildOptions: options => {
528 | options.loader = {
529 | ...options.loader,
530 | '.png': 'file',
531 | }
532 |
533 | return options
534 | },
535 | })
536 | ```
537 |
538 | ### Returns
539 |
540 | `bundleMDX` returns a promise for an object with the following properties.
541 |
542 | - `code` - The bundle of your mdx as a `string`.
543 | - `frontmatter` - The frontmatter `object` from gray-matter.
544 | - `matter` - The whole
545 | [object returned by gray-matter](https://github.com/jonschlinkert/gray-matter#returned-object)
546 |
547 | ### Types
548 |
549 | `mdx-bundler` supplies complete typings within its own package.
550 |
551 | `bundleMDX` has a single type parameter which is the type of your frontmatter.
552 | It defaults to `{[key: string]: any}` and must be an object. This is then used
553 | to type the returned `frontmatter` and the frontmatter passed to
554 | `esbuildOptions` and `mdxOptions`.
555 |
556 | ```ts
557 | const {frontmatter} = bundleMDX<{title: string}>({source})
558 |
559 | frontmatter.title // has type string
560 | ```
561 |
562 | ### Component Substitution
563 |
564 | MDX Bundler passes on
565 | [MDX's ability to substitute components](https://mdxjs.com/docs/using-mdx/#components)
566 | through the `components` prop on the component returned by `getMDXComponent`.
567 |
568 | Here's an example that removes _p_ tags from around images.
569 |
570 | ```tsx
571 | import * as React from 'react'
572 | import {getMDXComponent} from 'mdx-bundler/client'
573 |
574 | const Paragraph: React.FC = props => {
575 | if (typeof props.children !== 'string' && props.children.type === 'img') {
576 | return <>{props.children}>
577 | }
578 |
579 | return
580 | }
581 |
582 | function MDXPage({code}: {code: string}) {
583 | const Component = React.useMemo(() => getMDXComponent(code), [code])
584 |
585 | return (
586 |
587 |
588 |
589 | )
590 | }
591 | ```
592 |
593 | ### Frontmatter and const
594 |
595 | You can reference frontmatter meta or consts in the mdx content.
596 |
597 | ```mdx
598 | ---
599 | title: Example Post
600 | ---
601 |
602 | export const exampleImage = 'https://example.com/image.jpg'
603 |
604 | # {frontmatter.title}
605 |
606 |
607 | ```
608 |
609 | ### Accessing named exports
610 |
611 | You can use `getMDXExport` instead of `getMDXComponent` to treat the mdx file as
612 | a module instead of just a component. It takes the same arguments that
613 | `getMDXComponent` does.
614 |
615 | ```mdx
616 | ---
617 | title: Example Post
618 | ---
619 |
620 | export const toc = [{depth: 1, value: 'The title'}]
621 |
622 | # The title
623 | ```
624 |
625 | ```js
626 | import * as React from 'react'
627 | import {getMDXExport} from 'mdx-bundler/client'
628 |
629 | function MDXPage({code}: {code: string}) {
630 | const mdxExport = getMDXExport(code)
631 | console.log(mdxExport.toc) // [ { depth: 1, value: 'The title' } ]
632 |
633 | const Component = React.useMemo(() => mdxExport.default, [code])
634 |
635 | return
636 | }
637 | ```
638 |
639 | ### Image Bundling
640 |
641 | With the [cwd](#cwd) and the remark plugin
642 | [remark-mdx-images](https://www.npmjs.com/package/remark-mdx-images) you can
643 | bundle images in your mdx!
644 |
645 | There are two loaders in esbuild that can be used here. The easiest is `dataurl`
646 | which outputs the images as inline data urls in the returned code.
647 |
648 | ```js
649 | import {remarkMdxImages} from 'remark-mdx-images'
650 |
651 | const {code} = await bundleMDX({
652 | source: mdxSource,
653 | cwd: '/users/you/site/_content/pages',
654 | mdxOptions: options => {
655 | options.remarkPlugins = [...(options.remarkPlugins ?? []), remarkMdxImages]
656 |
657 | return options
658 | },
659 | esbuildOptions: options => {
660 | options.loader = {
661 | ...options.loader,
662 | '.png': 'dataurl',
663 | }
664 |
665 | return options
666 | },
667 | })
668 | ```
669 |
670 | The `file` loader requires a little more configuration to get working. With the
671 | `file` loader your images are copied to the output directory so esbuild needs to
672 | be set to write files and needs to know where to put them plus the url of the
673 | folder to be used in image sources.
674 |
675 | > Each call to `bundleMDX` is isolated from the others. If you set the directory
676 | > the same for everything `bundleMDX` will overwrite images without warning. As
677 | > a result each _bundle_ needs its own output directory.
678 |
679 | ```js
680 | // For the file `_content/pages/about.mdx`
681 |
682 | const {code} = await bundleMDX({
683 | source: mdxSource,
684 | cwd: '/users/you/site/_content/pages',
685 | mdxOptions: options => {
686 | options.remarkPlugins = [...(options.remarkPlugins ?? []), remarkMdxImages]
687 |
688 | return options
689 | },
690 | esbuildOptions: options => {
691 | // Set the `outdir` to a public location for this bundle.
692 | options.outdir = '/users/you/site/public/img/about'
693 | options.loader = {
694 | ...options.loader,
695 | // Tell esbuild to use the `file` loader for pngs
696 | '.png': 'file',
697 | }
698 | // Set the public path to /img/about
699 | options.publicPath = '/img/about'
700 |
701 | // Set write to true so that esbuild will output the files.
702 | options.write = true
703 |
704 | return options
705 | },
706 | })
707 | ```
708 |
709 | ### Bundling a file.
710 |
711 | If your MDX file is on your disk you can save some time and code by having
712 | `mdx-bundler` read the file for you. Instead of supplying a `source` string you
713 | can set `file` to the path of the MDX on disk. Set `cwd` to its folder so that
714 | relative imports work.
715 |
716 | ```js
717 | import {bundleMDX} from 'mdx-bundler'
718 |
719 | const {code, frontmatter} = await bundleMDX({
720 | file: '/users/you/site/content/file.mdx',
721 | cwd: '/users/you/site/content/',
722 | })
723 | ```
724 |
725 | ### Custom Components in Downstream Files
726 |
727 | To make sure custom components are accessible in downstream MDX files, you
728 | can use the `MDXProvider` from `@mdx-js/react` to pass custom components
729 | to your nested imports.
730 |
731 | ```
732 | npm install --save @mdx-js/react
733 | ```
734 |
735 | ```tsx
736 | const globals = {
737 | '@mdx-js/react': {
738 | varName: 'MdxJsReact',
739 | namedExports: ['useMDXComponents'],
740 | defaultExport: false,
741 | },
742 | };
743 | const { code } = bundleMDX({
744 | source,
745 | globals,
746 | mdxOptions(options: Record) {
747 | return {
748 | ...options,
749 | providerImportSource: '@mdx-js/react',
750 | };
751 | }
752 | });
753 | ```
754 |
755 | From there, you send the `code` to your client, and then:
756 |
757 | ```tsx
758 | import { MDXProvider, useMDXComponents } from '@mdx-js/react';
759 | const MDX_GLOBAL_CONFIG = {
760 | MdxJsReact: {
761 | useMDXComponents,
762 | },
763 | };
764 | export const MDXComponent: React.FC<{
765 | code: string;
766 | frontmatter: Record;
767 | }> = ({ code }) => {
768 | const Component = useMemo(
769 | () => getMDXComponent(code, MDX_GLOBAL_CONFIG),
770 | [code],
771 | );
772 |
773 | return (
774 |
{children}
}}>
775 |
776 |
777 | );
778 | };
779 | ```
780 |
781 | ### Known Issues
782 |
783 | ### Other JSX runtimes
784 | JSX runtimes mentioned [here](https://mdxjs.com/docs/getting-started/#jsx) are guaranteed to be supported, however any JSX runtime should work without problem, as long as they export their own jsx runtime. For example, `hono` is not mentioned here, but as it has `react` compatible API, it can be used without any issues.
785 |
786 | To do so, you will have to pass a configuration object and use JSX Component factory.
787 | ```tsx
788 | const getMDX = (source) => {
789 | return bundleMDX({
790 | source,
791 | jsxConfig: {
792 | jsxLib: {
793 | varName: 'HonoJSX',
794 | package: 'hono/jsx',
795 | },
796 | jsxDom: {
797 | varName: 'HonoDOM',
798 | package: 'hono/jsx/dom',
799 | },
800 | jsxRuntime: {
801 | varName: '_jsx_runtime',
802 | package: 'hono/jsx/jsx-runtime',
803 | },
804 | }
805 | });
806 | }
807 |
808 | // ...
809 |
810 | import { getMDXComponent } from "mdx-bundler/client/jsx";
811 |
812 | import * as HonoJSX from "hono/jsx";
813 | import * as HonoDOM from "hono/jsx/dom";
814 | import * as _jsx_runtime from "hono/jsx/jsx-runtime";
815 | const jsxConfig = {
816 | HonoJSX,
817 | HonoDOM,
818 | _jsx_runtime
819 | };
820 |
821 | export const MDXComponent: React.FC<{
822 | code: string;
823 | }> = ({ code }) => {
824 | const Component = useMemo(
825 | () => getMDXComponent(code, jsxConfig),
826 | [code],
827 | );
828 |
829 | return (
830 |
{children}
}} />
831 | );
832 | };
833 | ```
834 |
835 | To use it with others, adjust `jsxConfig` passed to bundler.
836 | ```ts
837 | const jsxConfig = {
838 | jsxLib: {
839 | varName: 'HonoJSX',
840 | package: 'hono/jsx',
841 | },
842 | jsxDom: {
843 | varName: 'HonoDOM',
844 | package: 'hono/jsx/dom',
845 | },
846 | jsxRuntime: {
847 | varName: '_jsx_runtime',
848 | package: 'hono/jsx/jsx-runtime',
849 | },
850 | }
851 | ```
852 | and to `getMDXComponent`
853 | ```ts
854 | const jsxConfig = { HonoJSX, HonoDOM, _jsx_runtime };
855 | ```
856 |
857 |
858 | #### Cloudflare Workers
859 |
860 | We'd _love_ for this to work in cloudflare workers. Unfortunately cloudflare workers
861 | have two limitations that prevent `mdx-bundler` from working in that
862 | environment:
863 |
864 | 1. Workers can't run binaries. `bundleMDX` uses `esbuild` (a binary) to bundle
865 | your MDX code.
866 | 2. Workers can't run `eval` or similar. `getMDXComponent` evaluates the bundled
867 | code using `new Function`.
868 |
869 | One workaround to this is to put your mdx-bundler related code in a different
870 | environment and call that environment from within the Cloudflare worker. IMO,
871 | this defeats the purpose of using Cloudflare workers. Another potential
872 | workaround is to use WASM from within the worker. There is
873 | [`esbuild-wasm`](https://esbuild.github.io/getting-started/#install-the-wasm-version)
874 | but there are some issues with that package explained at that link. Then there's
875 | [`wasm-jseval`](https://github.com/maple3142/wasm-jseval), but I couldn't get
876 | that to run code that was output from `mdx-bundler` without error.
877 |
878 | If someone would like to dig into this, that would be stellar, but unfortunately
879 | it's unlikely I'll ever work on it.
880 |
881 | #### Next.JS esbuild ENOENT
882 |
883 | esbuild relies on `__dirname` to work out where is executable is, Next.JS and
884 | Webpack can sometimes break this and esbuild needs to be told manually where to
885 | look.
886 |
887 | Adding the following code before your `bundleMDX` will point esbuild directly at
888 | the correct executable for your platform.
889 |
890 | ```js
891 | import path from 'path'
892 |
893 | if (process.platform === 'win32') {
894 | process.env.ESBUILD_BINARY_PATH = path.join(
895 | process.cwd(),
896 | 'node_modules',
897 | 'esbuild',
898 | 'esbuild.exe',
899 | )
900 | } else {
901 | process.env.ESBUILD_BINARY_PATH = path.join(
902 | process.cwd(),
903 | 'node_modules',
904 | 'esbuild',
905 | 'bin',
906 | 'esbuild',
907 | )
908 | }
909 | ```
910 |
911 | More information on this issue can be found
912 | [in this article](https://www.arcath.net/2021/03/mdx-bundler#esbuild-executable).
913 |
914 | ## Inspiration
915 |
916 | As I was rewriting [kentcdodds.com](https://kentcdodds.com) to
917 | [remix](https://remix.run), I decided I wanted to keep my blog posts as MDX, but
918 | I didn't want to have to compile them all at build time or be required to
919 | redeploy every time I fix a typo. So I made this which allows my server to
920 | compile on demand.
921 |
922 | ## Other Solutions
923 |
924 | There's [next-mdx-remote](https://github.com/hashicorp/next-mdx-remote) but it's
925 | more of an mdx-compiler than a bundler (can't bundle your mdx for dependencies).
926 | Also it's focused on Next.js whereas this is meta-framework agnostic.
927 |
928 | ## Issues
929 |
930 | _Looking to contribute? Look for the [Good First Issue][good-first-issue]
931 | label._
932 |
933 | ### 🐛 Bugs
934 |
935 | Please file an issue for bugs, missing documentation, or unexpected behavior.
936 |
937 | [**See Bugs**][bugs]
938 |
939 | ### 💡 Feature Requests
940 |
941 | Please file an issue to suggest new features. Vote on feature requests by adding
942 | a 👍. This helps maintainers prioritize what to work on.
943 |
944 | [**See Feature Requests**][requests]
945 |
946 | ## Contributors ✨
947 |
948 | Thanks goes to these people ([emoji key][emojis]):
949 |
950 |
951 |
952 |
953 |
989 |
990 |
991 |
992 |
993 |
994 |
995 | This project follows the [all-contributors][all-contributors] specification.
996 | Contributions of any kind welcome!
997 |
998 | ## LICENSE
999 |
1000 | MIT
1001 |
1002 |
1003 | [npm]: https://www.npmjs.com
1004 | [node]: https://nodejs.org
1005 | [build-badge]: https://img.shields.io/github/workflow/status/kentcdodds/mdx-bundler/validate?logo=github&style=flat-square
1006 | [build]: https://github.com/kentcdodds/mdx-bundler/actions?query=workflow%3Avalidate
1007 | [coverage-badge]: https://img.shields.io/codecov/c/github/kentcdodds/mdx-bundler.svg?style=flat-square
1008 | [coverage]: https://codecov.io/github/kentcdodds/mdx-bundler
1009 | [version-badge]: https://img.shields.io/npm/v/mdx-bundler.svg?style=flat-square
1010 | [package]: https://www.npmjs.com/package/mdx-bundler
1011 | [downloads-badge]: https://img.shields.io/npm/dm/mdx-bundler.svg?style=flat-square
1012 | [npmtrends]: https://www.npmtrends.com/mdx-bundler
1013 | [license-badge]: https://img.shields.io/npm/l/mdx-bundler.svg?style=flat-square
1014 | [license]: https://github.com/kentcdodds/mdx-bundler/blob/main/LICENSE
1015 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
1016 | [prs]: https://makeapullrequest.com
1017 | [coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
1018 | [coc]: https://github.com/kentcdodds/mdx-bundler/blob/main/CODE_OF_CONDUCT.md
1019 | [emojis]: https://github.com/all-contributors/all-contributors#emoji-key
1020 | [all-contributors]: https://github.com/all-contributors/all-contributors
1021 | [all-contributors-badge]: https://img.shields.io/github/all-contributors/kentcdodds/mdx-bundler?color=orange&style=flat-square
1022 | [bugs]: https://github.com/kentcdodds/mdx-bundler/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Acreated-desc+label%3Abug
1023 | [requests]: https://github.com/kentcdodds/mdx-bundler/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement
1024 | [good-first-issue]: https://github.com/kentcdodds/mdx-bundler/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement+label%3A%22good+first+issue%22
1025 | [node-gyp]: https://github.com/nodejs/node-gyp#installation
1026 |
1027 |
--------------------------------------------------------------------------------
/client/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../dist/client'
2 |
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/client/index.js')
2 |
--------------------------------------------------------------------------------
/client/jsx.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../dist/client/jsx'
--------------------------------------------------------------------------------
/client/jsx.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/client/jsx')
2 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "commonjs",
3 | "main": "./index.js",
4 | "types": "./index.d.ts",
5 | "exports": {
6 | "react": {
7 | "types": "./react.d.ts",
8 | "default": "./react.js"
9 | },
10 | "jsx": {
11 | "types": "./jsx.d.ts",
12 | "default": "./jsx.js"
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/client/react.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../dist/client/react'
--------------------------------------------------------------------------------
/client/react.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/client/index.js')
2 |
--------------------------------------------------------------------------------
/other/150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kentcdodds/mdx-bundler/1fac27347d7139dab27bcd96d0a89d8432a44fce/other/150.png
--------------------------------------------------------------------------------
/other/MAINTAINING.md:
--------------------------------------------------------------------------------
1 | # Maintaining
2 |
3 |
4 |
5 |
6 | **Table of Contents**
7 |
8 | - [Code of Conduct](#code-of-conduct)
9 | - [Issues](#issues)
10 | - [Pull Requests](#pull-requests)
11 | - [Release](#release)
12 | - [Thanks!](#thanks)
13 |
14 |
15 |
16 | This is documentation for maintainers of this project.
17 |
18 | ## Code of Conduct
19 |
20 | Please review, understand, and be an example of it. Violations of the code of
21 | conduct are taken seriously, even (especially) for maintainers.
22 |
23 | ## Issues
24 |
25 | We want to support and build the community. We do that best by helping people
26 | learn to solve their own problems. We have an issue template and hopefully most
27 | folks follow it. If it's not clear what the issue is, invite them to create a
28 | minimal reproduction of what they're trying to accomplish or the bug they think
29 | they've found.
30 |
31 | Once it's determined that a code change is necessary, point people to
32 | [makeapullrequest.com](https://makeapullrequest.com) and invite them to make a
33 | pull request. If they're the one who needs the feature, they're the one who can
34 | build it. If they need some hand holding and you have time to lend a hand,
35 | please do so. It's an investment into another human being, and an investment
36 | into a potential maintainer.
37 |
38 | Remember that this is open source, so the code is not yours, it's ours. If
39 | someone needs a change in the codebase, you don't have to make it happen
40 | yourself. Commit as much time to the project as you want/need to. Nobody can ask
41 | any more of you than that.
42 |
43 | ## Pull Requests
44 |
45 | As a maintainer, you're fine to make your branches on the main repo or on your
46 | own fork. Either way is fine.
47 |
48 | When we receive a pull request, a GitHub Action is kicked off automatically (see
49 | the `.github/workflows/validate.yml` for what runs in the Action). We avoid
50 | merging anything that breaks the GitHub Action.
51 |
52 | Please review PRs and focus on the code rather than the individual. You never
53 | know when this is someone's first ever PR and we want their experience to be as
54 | positive as possible, so be uplifting and constructive.
55 |
56 | When you merge the pull request, 99% of the time you should use the
57 | [Squash and merge](https://help.github.com/articles/merging-a-pull-request/)
58 | feature. This keeps our git history clean, but more importantly, this allows us
59 | to make any necessary changes to the commit message so we release what we want
60 | to release. See the next section on Releases for more about that.
61 |
62 | ## Release
63 |
64 | Our releases are automatic. They happen whenever code lands into `main`. A
65 | GitHub Action gets kicked off and if it's successful, a tool called
66 | [`semantic-release`](https://github.com/semantic-release/semantic-release) is
67 | used to automatically publish a new release to npm as well as a changelog to
68 | GitHub. It is only able to determine the version and whether a release is
69 | necessary by the git commit messages. With this in mind, **please brush up on
70 | [the commit message convention][commit] which drives our releases.**
71 |
72 | > One important note about this: Please make sure that commit messages do NOT
73 | > contain the words "BREAKING CHANGE" in them unless we want to push a major
74 | > version. I've been burned by this more than once where someone will include
75 | > "BREAKING CHANGE: None" and it will end up releasing a new major version. Not
76 | > a huge deal honestly, but kind of annoying...
77 |
78 | ## Thanks!
79 |
80 | Thank you so much for helping to maintain this project!
81 |
82 |
83 | [commit]: https://github.com/conventional-changelog-archived-repos/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md
84 |
85 |
--------------------------------------------------------------------------------
/other/USERS.md:
--------------------------------------------------------------------------------
1 | # Users
2 |
3 | If you or your company uses this project, add your name to this list! Eventually
4 | we may have a website to showcase these (wanna build it!?)
5 |
6 | > No users have been added yet!
7 |
8 |
13 |
--------------------------------------------------------------------------------
/other/cjs-ify.js:
--------------------------------------------------------------------------------
1 | // This file exists because we want to develop our package with Native ESM
2 | // but distribute our package as CommonJS. We need to use native ESM because
3 | // several deps use ESM and it's just easier to integrate with them using native
4 | // ESM. But we want to expose CommonJS because our package consumers aren't ready
5 | // to consume native ESM packages yet...
6 |
7 | // This is hopefully temporary...
8 | import fs from 'fs'
9 | import url from 'url'
10 | import path from 'path'
11 |
12 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
13 | const distPath = path.join(__dirname, '../dist')
14 | const pkgPath = path.join(distPath, 'package.json')
15 |
16 | const cjsPkgInfo = {
17 | type: 'commonjs',
18 | main: './index.js',
19 | types: './index.d.ts',
20 | }
21 | fs.writeFileSync(pkgPath, JSON.stringify(cjsPkgInfo))
22 |
23 | // when babel compiles this file, it renames it from `.cjs` to `.js` but our
24 | // code imports it with the extension (becuase during dev we're native ESM so we
25 | // have to) and it's easier to update the extension than it would be to update
26 | // the import in the code during the build.
27 | fs.renameSync(
28 | path.join(distPath, 'dirname-messed-up.js'),
29 | path.join(distPath, 'dirname-messed-up.cjs'),
30 | )
31 |
--------------------------------------------------------------------------------
/other/manual-releases.md:
--------------------------------------------------------------------------------
1 | # manual-releases
2 |
3 | This project has an automated release set up. So things are only released when
4 | there are useful changes in the code that justify a release. But sometimes
5 | things get messed up one way or another and we need to trigger the release
6 | ourselves. When this happens, simply bump the number below and commit that with
7 | the following commit message based on your needs:
8 |
9 | **Major**
10 |
11 | ```
12 | fix(release): manually release a major version
13 |
14 | There was an issue with a major release, so this manual-releases.md
15 | change is to release a new major version.
16 |
17 | Reference: #
18 |
19 | BREAKING CHANGE:
20 | ```
21 |
22 | **Minor**
23 |
24 | ```
25 | feat(release): manually release a minor version
26 |
27 | There was an issue with a minor release, so this manual-releases.md
28 | change is to release a new minor version.
29 |
30 | Reference: #
31 | ```
32 |
33 | **Patch**
34 |
35 | ```
36 | fix(release): manually release a patch version
37 |
38 | There was an issue with a patch release, so this manual-releases.md
39 | change is to release a new patch version.
40 |
41 | Reference: #
42 | ```
43 |
44 | The number of times we've had to do a manual release is: 0
45 |
--------------------------------------------------------------------------------
/other/sample-component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import image from './150.png'
4 |
5 | /** @type React.FC */
6 | export const Sample = () => {
7 | return (
8 |