From 2aae01c406819e5e59e5d5ce2a1d5556ac4f0cc8 Mon Sep 17 00:00:00 2001 From: codpoe Date: Tue, 9 Jan 2024 18:05:22 +0800 Subject: [PATCH] feat: compile jsx --- __test__/__snapshots__/index.spec.ts.snap | 52 ++++++++++++++++++- __test__/compile-jsx.mdx | 1 + __test__/index.spec.ts | 13 +++++ crates/binding/src/lib.rs | 39 +++++---------- crates/mdx_rs/src/lib.rs | 61 ++++++++++++++++------- index.d.ts | 1 + tasks/benchmark/src/main.rs | 8 ++- 7 files changed, 128 insertions(+), 47 deletions(-) create mode 100644 __test__/compile-jsx.mdx diff --git a/__test__/__snapshots__/index.spec.ts.snap b/__test__/__snapshots__/index.spec.ts.snap index dd36f43..c98feef 100644 --- a/__test__/__snapshots__/index.spec.ts.snap +++ b/__test__/__snapshots__/index.spec.ts.snap @@ -1,5 +1,53 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`compile > should compile jsx 1`] = ` +"

+ Compile JSX# +

+" +`; + +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`] = ` "
TIP
@@ -102,7 +150,7 @@ export default MDXContent; exports[`compile > should render container title in mdx correctly 1`] = ` "

- Container Title# + Container Title #

Custom Title
@@ -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\\">{\\"#\\"}{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}<_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"\\\\nThis is a \\"}<_components.code>{\\"block\\"}{\\" of \\"}<_components.code>{\\"Custom Title\\"}{\\"\\\\n\\"}{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}<_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"\\\\nThis is a \\"}<_components.code>{\\"block\\"}{\\" of \\"}<_components.code>{\\"Custom Title\\"}{\\"\\\\n\\"}{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}<_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"This is a \\"}<_components.code>{\\"block\\"}{\\" of \\"}<_components.code>{\\"Custom Title\\"}{\\"\\\\n\\"}{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}<_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"This is a \\"}<_components.code>{\\"block\\"}{\\" of \\"}<_components.code>{\\"Custom Title\\"}{\\"\\\\n\\"}; + return <><_components.h2 id=\\"custom-title\\">{\\"Container Title \\"}<_components.a className=\\"header-anchor\\" aria-hidden=\\"true\\" href=\\"#custom-title\\">{\\"#\\"}{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}<_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"\\\\nThis is a \\"}<_components.code>{\\"block\\"}{\\" of \\"}<_components.code>{\\"Custom Title\\"}{\\"\\\\n\\"}{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}<_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"\\\\nThis is a \\"}<_components.code>{\\"block\\"}{\\" of \\"}<_components.code>{\\"Custom Title\\"}{\\"\\\\n\\"}{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}<_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"This is a \\"}<_components.code>{\\"block\\"}{\\" of \\"}<_components.code>{\\"Custom Title\\"}{\\"\\\\n\\"}{\\"\\\\n\\"}<_components.div className=\\"rspress-directive tip\\"><_components.div className=\\"rspress-directive-title\\">{\\"Custom Title\\"}<_components.div className=\\"rspress-directive-content\\"><_components.p>{\\"This is a \\"}<_components.code>{\\"block\\"}{\\" of \\"}<_components.code>{\\"Custom Title\\"}{\\"\\\\n\\"}; } function MDXContent(props = {}) { const { wrapper: MDXLayout } = Object.assign({}, _provideComponents(), props.components); diff --git a/__test__/compile-jsx.mdx b/__test__/compile-jsx.mdx new file mode 100644 index 0000000..ae1933a --- /dev/null +++ b/__test__/compile-jsx.mdx @@ -0,0 +1 @@ +# Compile JSX diff --git a/__test__/index.spec.ts b/__test__/index.spec.ts index bee3a74..bf3f07b 100644 --- a/__test__/index.spec.ts +++ b/__test__/index.spec.ts @@ -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(); + }); }); diff --git a/crates/binding/src/lib.rs b/crates/binding/src/lib.rs index 62b8aed..74407d6 100644 --- a/crates/binding/src/lib.rs +++ b/crates/binding/src/lib.rs @@ -36,6 +36,7 @@ pub struct CompileOptions { pub filepath: String, pub development: bool, pub root: String, + pub jsx: Option, } impl From for Toc { @@ -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")] pub fn compile(options: CompileOptions) -> AsyncTask { - 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() } diff --git a/crates/mdx_rs/src/lib.rs b/crates/mdx_rs/src/lib.rs index f9c6ea2..955e8cb 100644 --- a/crates/mdx_rs/src/lib.rs +++ b/crates/mdx_rs/src/lib.rs @@ -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; @@ -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, @@ -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 { @@ -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); @@ -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| { @@ -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, @@ -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() + }); } } diff --git a/index.d.ts b/index.d.ts index d0e112c..0b71cbd 100644 --- a/index.d.ts +++ b/index.d.ts @@ -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 diff --git a/tasks/benchmark/src/main.rs b/tasks/benchmark/src/main.rs index 5689f43..1d16fd8 100644 --- a/tasks/benchmark/src/main.rs +++ b/tasks/benchmark/src/main.rs @@ -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::*; @@ -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() + }); }) }, );