Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for compiling jsx #17

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions __test__/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,53 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`compile > should compile jsx 1`] = `
"<h1 id=\\"compile-jsx\\">
Compile JSX<a aria-hidden=\\"true\\" href=\\"#compile-jsx\\">#</a>
</h1>
"
`;

exports[`compile > should compile jsx 2`] = `
"import { jsxDEV as _jsxDEV } from \\"react/jsx-dev-runtime\\";
import { useMDXComponents as _provideComponents } from \\"@mdx-js/react\\";
function _createMdxContent(props) {
const _components = Object.assign({
h1: \\"h1\\",
a: \\"a\\"
}, _provideComponents(), props.components);
return _jsxDEV(_components.h1, {
id: \\"compile-jsx\\",
children: [
\\"Compile JSX\\",
_jsxDEV(_components.a, {
className: \\"header-anchor\\",
\\"aria-hidden\\": \\"true\\",
href: \\"#compile-jsx\\",
children: \\"#\\"
}, undefined, false, {
fileName: \\"xxx.mdx\\"
}, this)
]
}, undefined, true, {
fileName: \\"xxx.mdx\\",
lineNumber: 1,
columnNumber: 1
}, this);
}
function MDXContent(props = {}) {
const { wrapper: MDXLayout } = Object.assign({}, _provideComponents(), props.components);
return MDXLayout ? _jsxDEV(MDXLayout, Object.assign({}, props, {
children: _jsxDEV(_createMdxContent, props, undefined, false, {
fileName: \\"xxx.mdx\\"
}, this)
}), undefined, false, {
fileName: \\"xxx.mdx\\"
}, this) : _createMdxContent(props);
}
export default MDXContent;
"
`;

exports[`compile > should render container content correctly 1`] = `
"<div>
<div>TIP</div>
Expand Down Expand Up @@ -102,7 +150,7 @@ export default MDXContent;

exports[`compile > should render container title in mdx correctly 1`] = `
"<h2 id=\\"custom-title\\">
Container Title<a aria-hidden=\\"true\\" href=\\"#custom-title\\">#</a>
Container Title <a aria-hidden=\\"true\\" href=\\"#custom-title\\">#</a>
</h2>
<div>
<div>Custom Title</div>
Expand Down Expand Up @@ -141,7 +189,7 @@ function _createMdxContent(props) {
p: \\"p\\",
code: \\"code\\"
}, _provideComponents(), props.components);
return <><_components.h2 id=\\"custom-title\\">{\\"Container Title\\"}<_components.a className=\\"header-anchor\\" aria-hidden=\\"true\\" href=\\"#custom-title\\">{\\"#\\"}</_components.a></_components.h2>{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}</_components.div><_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"\\\\nThis is a \\"}<_components.code>{\\"block\\"}</_components.code>{\\" of \\"}<_components.code>{\\"Custom Title\\"}</_components.code>{\\"\\\\n\\"}</_components.p></_components.div></_components.div>{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}</_components.div><_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"\\\\nThis is a \\"}<_components.code>{\\"block\\"}</_components.code>{\\" of \\"}<_components.code>{\\"Custom Title\\"}</_components.code>{\\"\\\\n\\"}</_components.p></_components.div></_components.div>{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}</_components.div><_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"This is a \\"}<_components.code>{\\"block\\"}</_components.code>{\\" of \\"}<_components.code>{\\"Custom Title\\"}</_components.code>{\\"\\\\n\\"}</_components.p></_components.div></_components.div>{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}</_components.div><_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"This is a \\"}<_components.code>{\\"block\\"}</_components.code>{\\" of \\"}<_components.code>{\\"Custom Title\\"}</_components.code>{\\"\\\\n\\"}</_components.p></_components.div></_components.div></>;
return <><_components.h2 id=\\"custom-title\\">{\\"Container Title \\"}<_components.a className=\\"header-anchor\\" aria-hidden=\\"true\\" href=\\"#custom-title\\">{\\"#\\"}</_components.a></_components.h2>{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}</_components.div><_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"\\\\nThis is a \\"}<_components.code>{\\"block\\"}</_components.code>{\\" of \\"}<_components.code>{\\"Custom Title\\"}</_components.code>{\\"\\\\n\\"}</_components.p></_components.div></_components.div>{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}</_components.div><_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"\\\\nThis is a \\"}<_components.code>{\\"block\\"}</_components.code>{\\" of \\"}<_components.code>{\\"Custom Title\\"}</_components.code>{\\"\\\\n\\"}</_components.p></_components.div></_components.div>{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}</_components.div><_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"This is a \\"}<_components.code>{\\"block\\"}</_components.code>{\\" of \\"}<_components.code>{\\"Custom Title\\"}</_components.code>{\\"\\\\n\\"}</_components.p></_components.div></_components.div>{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}</_components.div><_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"This is a \\"}<_components.code>{\\"block\\"}</_components.code>{\\" of \\"}<_components.code>{\\"Custom Title\\"}</_components.code>{\\"\\\\n\\"}</_components.p></_components.div></_components.div></>;
}
function MDXContent(props = {}) {
const { wrapper: MDXLayout } = Object.assign({}, _provideComponents(), props.components);
Expand Down
1 change: 1 addition & 0 deletions __test__/compile-jsx.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Compile JSX
13 changes: 13 additions & 0 deletions __test__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,17 @@ describe("compile", () => {
expect(formatHTML(html)).toMatchSnapshot();
expect(result).toMatchSnapshot();
});

test("should compile jsx", async (t) => {
let { code: result, html } = await compile({
value: readFileSync(path.join(__dirname, "./compile-jsx.mdx"), "utf8"),
filepath: "xxx.mdx",
development: true,
root: "xxx",
jsx: false
});

expect(formatHTML(html)).toMatchSnapshot();
expect(result).toMatchSnapshot();
});
});
39 changes: 13 additions & 26 deletions crates/binding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub struct CompileOptions {
pub filepath: String,
pub development: bool,
pub root: String,
pub jsx: Option<bool>,
}

impl From<TocItem> for Toc {
Expand Down Expand Up @@ -89,47 +90,33 @@ impl Task for Compiler {
}

pub struct Compiler {
value: String,
filepath: String,
development: bool,
root: String,
options: CompileOptions,
}

impl Compiler {
pub fn new(value: String, filepath: String, development: bool, root: String) -> Self {
Self {
value,
filepath,
development,
root,
}
pub fn new(options: CompileOptions) -> Self {
Self { options }
}

fn compile(&mut self) -> CompileResult {
mdx_rs::compile(&self.value, &self.filepath, self.development, &self.root)
mdx_rs::compile(mdx_rs::CompileOptions {
value: self.options.value.clone(),
filepath: self.options.filepath.clone(),
development: self.options.development,
root: self.options.root.clone(),
jsx: self.options.jsx.unwrap_or(true),
})
}
}

/// Turn MDX into JavaScript.
#[napi(ts_return_type = "Promise<Output>")]
pub fn compile(options: CompileOptions) -> AsyncTask<Compiler> {
let CompileOptions {
value,
filepath,
development,
root,
} = options;
AsyncTask::new(Compiler::new(value, filepath, development, root))
AsyncTask::new(Compiler::new(options))
}

#[napi]
pub fn compile_sync(options: CompileOptions) -> Output {
let CompileOptions {
value,
filepath,
development,
root,
} = options;
let mut compiler = Compiler::new(value, filepath, development, root);
let mut compiler = Compiler::new(options);
compiler.compile().into()
}
61 changes: 44 additions & 17 deletions crates/mdx_rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ use crate::{
swc_util_build_jsx::{swc_util_build_jsx, Options as BuildOptions},
};
use hast;
use hast_util_to_swc::Program;
use markdown::{to_mdast, Constructs, Location, ParseOptions};
use mdx_plugin_container::mdx_plugin_container;
use mdx_plugin_external_link::mdx_plugin_external_link;
Expand All @@ -40,9 +39,32 @@ use mdx_plugin_html::mdx_plugin_html;
use mdx_plugin_normalize_link::mdx_plugin_normalize_link;
use mdx_plugin_toc::{mdx_plugin_toc, TocItem};

pub use crate::configuration::{MdxConstructs, MdxParseOptions, Options};
pub use crate::mdx_plugin_recma_document::JsxRuntime;

pub struct CompileOptions {
pub value: String,
/// The root directory of the project.
pub root: String,
/// File path to the source file.
pub filepath: String,
/// Whether to add extra information to error messages in generated code
pub development: bool,
/// Whether to keep JSX (default: `true`).
pub jsx: bool,
}

impl Default for CompileOptions {
fn default() -> Self {
Self {
value: "".to_string(),
root: "".to_string(),
filepath: "".to_string(),
development: true,
jsx: true,
}
}
}

pub struct CompileResult {
pub code: String,
pub links: Vec<String>,
Expand All @@ -52,12 +74,15 @@ pub struct CompileResult {
pub frontmatter: String,
}

pub fn compile(
value: &String,
filepath: &String,
development: bool,
root: &String,
) -> CompileResult {
pub fn compile(options: CompileOptions) -> CompileResult {
let CompileOptions {
value,
filepath,
development,
root,
jsx,
} = options;

let is_mdx = filepath.ends_with(".mdx");
let parse_options = ParseOptions {
constructs: Constructs {
Expand Down Expand Up @@ -92,6 +117,7 @@ pub fn compile(
development,
provider_import_source: Some("@mdx-js/react".to_string()),
};
let build_options: BuildOptions = BuildOptions { development };
let location = Location::new(value.as_bytes());
let mut mdast = to_mdast(value.as_str(), &parse_options).unwrap_or_else(|error| {
eprintln!("File: {:?}\nError: {:?}", filepath, error);
Expand All @@ -107,7 +133,7 @@ pub fn compile(
mdx_plugin_header_anchor(&mut hast);
mdx_plugin_container(&mut hast);
mdx_plugin_external_link(&mut hast);
let links = mdx_plugin_normalize_link(&mut hast, root, filepath);
let links = mdx_plugin_normalize_link(&mut hast, &root, &filepath);
let html = mdx_plugin_html(&hast);
let mut program = hast_util_to_swc(&hast, Some(filepath.to_string()), Some(&location))
.unwrap_or_else(|error| {
Expand All @@ -129,8 +155,11 @@ pub fn compile(
},
);
mdx_plugin_recma_jsx_rewrite(&mut program, &rewrite_options, Some(&location));
// We keep the origin jsx here.
// swc_util_build_jsx(&mut program, &build_options, Some(&location)).unwrap();

if !jsx {
swc_util_build_jsx(&mut program, &build_options, Some(&location)).unwrap();
}

let code = serialize(&mut program.module, Some(&program.comments));
CompileResult {
code,
Expand All @@ -147,11 +176,9 @@ mod tests {
use super::*;
#[test]
fn test_collect_title_in_mdast() {
compile(
&"## Container Title {#custom-title}".to_string(),
&"".to_string(),
true,
&"".to_string(),
);
compile(CompileOptions {
value: "## Container Title {#custom-title}".to_string(),
..Default::default()
});
}
}
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface CompileOptions {
filepath: string
development: boolean
root: string
jsx?: boolean
}
/** Turn MDX into JavaScript. */
export function compile(options: CompileOptions): Promise<Output>
Expand Down
8 changes: 6 additions & 2 deletions tasks/benchmark/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ extern crate mdx_rs;
extern crate pico_args;

use criterion::{BenchmarkId, Criterion, Throughput};
use mdx_rs::compile;
use mdx_rs::{compile, CompileOptions};
use pico_args::Arguments;
use std::fs::File;
use std::io::prelude::*;
Expand Down Expand Up @@ -34,7 +34,11 @@ pub fn main() {
&contents,
|b, source_text| {
b.iter_with_large_drop(|| {
compile(&source_text, &"".to_string(), false, &"".to_string());
compile(CompileOptions {
value: source_text.to_string(),
development: false,
..Default::default()
});
})
},
);
Expand Down