Skip to content

Commit 9ed76e6

Browse files
committed
initial commit
0 parents  commit 9ed76e6

File tree

8 files changed

+7026
-0
lines changed

8 files changed

+7026
-0
lines changed

README.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# `mdx.macro`
2+
3+
[![Babel Macro](https://img.shields.io/badge/babel--macro-%F0%9F%8E%A3-f5da55.svg?style=flat-square)](https://github.com/kentcdodds/babel-plugin-macros)
4+
5+
[![npm version](https://img.shields.io/badge/npm-0.2.5-brightgreen.svg)](https://github.com/weyert/mdx.macro)
6+
7+
A babel-macro for converting mdx into an inline component.
8+
9+
```markdown
10+
## This is some MDX source
11+
12+
<SomeComponent />
13+
14+
~~strikethrough~~
15+
```
16+
17+
```js
18+
import { mdx, imports } from 'mdx.macro'
19+
import { MDXTag } from '@mdx-js/tag'
20+
imports() // copies import statements from markdown file to here
21+
22+
const SomeMDXComponent = mdx('./markdown.md')
23+
24+
generates...
25+
26+
```js
27+
const SomeMDXComponent = ({ components, ...props }) => (
28+
<MDXTag name="wrapper" components={components}>
29+
<MDXTag name="h2" components={components}>{`This is some MDX source`}</MDXTag>{' '}
30+
<SomeComponent />{' '}
31+
<MDXTag name="p" components={components}>
32+
<MDXTag
33+
name="del"
34+
components={components}
35+
parentName="p"
36+
>
37+
{`strikethrough`}
38+
</MDXTag>
39+
</MDXTag>
40+
</MDXTag>
41+
)
42+
```
43+
44+
### Getting started
45+
46+
#### Set up an application
47+
48+
Recommended setup - set up an application from scratch
49+
50+
[yarn](https://yarnpkg.com/en/docs/cli/) or [npm](https://docs.npmjs.com/cli/install) can be used
51+
52+
create a package.json file
53+
```
54+
npm init
55+
56+
yarn init
57+
```
58+
59+
install webpack and webpack-cli as dev dependencies
60+
```
61+
npm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D
62+
63+
yarn add webpack webpack-cli webpack-dev-server html-webpack-plugin -D
64+
```
65+
66+
add to package.json
67+
```
68+
"scripts": {
69+
"start": "webpack-dev-server --mode development --open",
70+
"build": "webpack --mode production"
71+
},
72+
```
73+
74+
install and save react and react-dom
75+
```
76+
npm i react react-dom
77+
78+
yarn add react react-dom
79+
```
80+
81+
install and save the following dev dependencies
82+
```
83+
npm i @babel/core babel-loader @babel/preset-env @babel/preset-react -D
84+
85+
yarn add @babel/core babel-loader @babel/preset-env @babel/preset-react -D
86+
```
87+
88+
create a [webpack](https://webpack.js.org/guides/getting-started/#using-a-configuration) config. Example of a basic webpack config file:
89+
```
90+
const HtmlWebPackPlugin = require("html-webpack-plugin");
91+
92+
const htmlPlugin = new HtmlWebPackPlugin({
93+
template: "./src/index.html",
94+
filename: "./index.html"
95+
});
96+
97+
module.exports = {
98+
module: {
99+
rules: [
100+
{
101+
test: /\.js$/,
102+
exclude: /node_modules/,
103+
use: {
104+
loader: "babel-loader"
105+
}
106+
}
107+
]
108+
},
109+
plugins: [htmlPlugin]
110+
};
111+
```
112+
113+
create a .babelrc file and add the following presets
114+
```
115+
{
116+
"presets": ["@babel/preset-env", "@babel/preset-react"]
117+
}
118+
```
119+
120+
#### Install and save the following dev dependencies
121+
```
122+
npm i @innerfuse/mdx.macro babel-plugin-macros @mdx-js/tag -D
123+
124+
yarn add @innerfuse/mdx-macro babel-plugin-macros @mdx-js/tag -D
125+
```
126+
127+
#### Add [babel-plugin-macros](https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/user.md) to your babel config file
128+
```
129+
"plugins": [
130+
"babel-plugin-macros"
131+
]
132+
```
133+
134+
### Known Problems
135+
136+
If you use a React component which is not defined in the Javascript file and is not imported the application will stop working and you will get an error similar to `__jsxFilename not defined`. If this is the case ensure you have the component referred in the Markdown file is defined.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { mdx } from '@innerfuse/mdx.macro'
2+
3+
const SomeMDXComponent = mdx('./markdown.mdx')
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## This is some MDX source
2+
<SomeComponent />
3+
~~strikethrough~~
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const SomeMDXComponent = ({
2+
components,
3+
...props
4+
}) => <MDXTag name="wrapper" components={components}><MDXTag name="h2" components={components}>{`This is some MDX source`}</MDXTag>
5+
<SomeComponent />
6+
<MDXTag name="p" components={components}><MDXTag name="del" components={components} parentName="p">{`strikethrough`}</MDXTag></MDXTag></MDXTag>;

__tests__/mdx.macro.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const path = require('path')
2+
const pluginTester = require('babel-plugin-tester')
3+
const plugin = require('babel-plugin-macros')
4+
5+
pluginTester({
6+
plugin,
7+
snapshot: true,
8+
fixtures: path.join(__dirname, '__fixtures__'),
9+
})

mdx.macro.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
const { createMacro } = require("babel-plugin-macros");
2+
const mdx = require("@mdx-js/mdx");
3+
const restSpreadSyntax = require("babel-plugin-syntax-object-rest-spread");
4+
const jsxSyntax = require("babel-plugin-syntax-jsx");
5+
const path = require("path");
6+
const fs = require("fs");
7+
8+
module.exports = createMacro(MDX);
9+
10+
function MDX({ references, babel, state }) {
11+
const { mdx, imports } = references;
12+
const { types: t } = babel;
13+
14+
let mdxImports = [];
15+
mdx.forEach(referencePath => {
16+
if (referencePath.parentPath.type === "CallExpression") {
17+
const importStatements = requireRaw({ referencePath, state, babel });
18+
mdxImports = [...mdxImports, ...importStatements];
19+
} else {
20+
throw new Error(
21+
`This is not supported: \`${referencePath
22+
.findParent(babel.types.isExpression)
23+
.getSource()}\`. Please see the raw.macro documentation`
24+
);
25+
}
26+
});
27+
28+
// Process any imports and add them where `imports` was called from
29+
imports.forEach(reference => {
30+
let { ast, code } = babel.transform(
31+
[`import {MDXTag} from '@mdx-js/tag'`].concat(mdxImports).join("\n"),
32+
{
33+
ast: true
34+
}
35+
);
36+
reference.parentPath.replaceWithMultiple(
37+
ast.program.body.map(impNode => {
38+
return t.importDeclaration(impNode.specifiers, impNode.source);
39+
})
40+
);
41+
});
42+
}
43+
44+
function requireRaw({ referencePath, state, babel }) {
45+
const filename = state.file.opts.filename;
46+
const { types: t } = babel;
47+
const callExpressionPath = referencePath.parentPath;
48+
const dirname = path.dirname(filename);
49+
let rawPath;
50+
51+
try {
52+
rawPath = callExpressionPath.get("arguments")[0].evaluate().value;
53+
} catch (err) {
54+
// swallow error, print better error below
55+
}
56+
57+
if (rawPath === undefined) {
58+
throw new Error(
59+
`There was a problem evaluating the value of the argument for the code: ${callExpressionPath.getSource()}. ` +
60+
`If the value is dynamic, please make sure that its value is statically deterministic.`
61+
);
62+
}
63+
64+
const fullPath = path.resolve(dirname, rawPath);
65+
const fileContent = fs.readFileSync(fullPath, { encoding: "utf-8" });
66+
67+
let transformedFunction = mdx.sync(fileContent).replace("export default", "");
68+
const funcName = callExpressionPath.parent.id.name;
69+
70+
let mdxImports = [];
71+
transformedFunction = transformedFunction
72+
.split("\n")
73+
.map(line => {
74+
if (line.includes("import")) {
75+
mdxImports.push(line);
76+
return null;
77+
} else {
78+
return line;
79+
}
80+
})
81+
.filter(Boolean)
82+
.join("\n");
83+
84+
let { ast, code } = babel.transform(
85+
`const ${funcName} = ${transformedFunction}`,
86+
{
87+
plugins: [jsxSyntax, restSpreadSyntax],
88+
ast: true
89+
}
90+
);
91+
92+
callExpressionPath.replaceWith(
93+
t.arrowFunctionExpression(
94+
[
95+
// build out a function argument that looks like
96+
// ({ components, ...props })
97+
t.objectPattern([
98+
t.objectProperty(
99+
t.identifier("components"),
100+
t.identifier("components"),
101+
false,
102+
// set shorthand to true, otherwise it generates
103+
// ({ components: components })
104+
true
105+
),
106+
// spread in props
107+
t.restElement(t.identifier("props"))
108+
])
109+
],
110+
// :this_is_fine_dog:
111+
// ensure we grab the last function, in case babel
112+
// transforms the above code into more than one function
113+
ast.program.body[ast.program.body.length - 1].declarations[0].init.body
114+
)
115+
);
116+
117+
return mdxImports;
118+
}

0 commit comments

Comments
 (0)