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

Experimental Plugins #8

Open
wants to merge 4 commits 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
108 changes: 108 additions & 0 deletions examples/plugins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
extern crate mdxjs;

use markdown::mdast;
use mdxjs::hast;
use mdxjs::{HastNode, MdastNode, Options, PluginOptions, RecmaProgram};
use std::rc::Rc;
use swc_core::common::{Span, SyntaxContext};
use swc_core::ecma::ast as estree;
use swc_core::ecma::atoms::JsWord;

/// Example that compiles the example MDX document from <https://mdxjs.com>
/// to JavaScript.
fn main() -> Result<(), String> {
println!(
"{}",
mdxjs::compile_with_plugins(
"# test",
&Options {
..Default::default()
},
&PluginOptions {
experimental_mdast_transforms: Some(vec![Rc::new(|root: &mut MdastNode| {
mdast_visit_mut(root, |n| {
if let mdast::Node::Text(text) = n {
text.value = "Hello World!".into();
}
});
Ok(())
})]),
experimental_hast_transforms: Some(vec![Rc::new(|root: &mut HastNode| {
hast_visit_mut(root, |n| {
if let hast::Node::Element(e) = n {
if e.tag_name == "h1" {
e.tag_name = "h2".into();
}
};
});
Ok(())
})]),
experimental_recma_transforms: Some(vec![Rc::new(|program: &mut RecmaProgram| {
let body = &mut program.module.body;
body.push(estree::ModuleItem::Stmt(estree::Stmt::Expr(
estree::ExprStmt {
expr: Box::new(estree::Expr::Ident(estree::Ident::from((
JsWord::from("hello"),
SyntaxContext::empty(),
)))),
span: Span::default(),
},
)));
Ok(())
})])
}
)?
);

Ok(())
}

fn mdast_visit_mut<Visitor>(node: &mut mdast::Node, visitor: Visitor)
where
Visitor: FnMut(&mut mdast::Node),
{
mdast_visit_mut_impl(node, visitor);
}

fn mdast_visit_mut_impl<Visitor>(node: &mut mdast::Node, mut visitor: Visitor) -> Visitor
where
Visitor: FnMut(&mut mdast::Node),
{
visitor(node);

if let Some(children) = node.children_mut() {
let mut index = 0;
while index < children.len() {
let child = &mut children[index];
visitor = mdast_visit_mut_impl(child, visitor);
index += 1;
}
}

visitor
}

fn hast_visit_mut<Visitor>(node: &mut hast::Node, visitor: Visitor)
where
Visitor: FnMut(&mut hast::Node),
{
hast_visit_mut_impl(node, visitor);
}

fn hast_visit_mut_impl<Visitor>(node: &mut hast::Node, mut visitor: Visitor) -> Visitor
where
Visitor: FnMut(&mut hast::Node),
{
visitor(node);

if let Some(children) = node.children_mut() {
let mut index = 0;
while index < children.len() {
let child = &mut children[index];
visitor = hast_visit_mut_impl(child, visitor);
index += 1;
}
}

visitor
}
27 changes: 27 additions & 0 deletions src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
//! Configuration.

use crate::mdx_plugin_recma_document::JsxRuntime;
use std::rc::Rc;

pub use crate::hast::Node as HastNode;
pub use crate::hast_util_to_swc::Program as RecmaProgram;
pub use markdown::mdast::Node as MdastNode;

/// Like `Constructs` from `markdown-rs`.
///
Expand Down Expand Up @@ -156,6 +161,28 @@ impl MdxParseOptions {
}
}

type MdastPlugin = Rc<dyn Fn(&mut MdastNode) -> Result<(), String> + 'static>;

type HastPlugin = Rc<dyn Fn(&mut HastNode) -> Result<(), String> + 'static>;

type RecmaPlugin = Rc<dyn Fn(&mut RecmaProgram) -> Result<(), String> + 'static>;

pub struct PluginOptions {
pub experimental_mdast_transforms: Option<Vec<MdastPlugin>>,
pub experimental_hast_transforms: Option<Vec<HastPlugin>>,
pub experimental_recma_transforms: Option<Vec<RecmaPlugin>>,
}

impl Default for PluginOptions {
fn default() -> Self {
Self {
experimental_mdast_transforms: None,
experimental_hast_transforms: None,
experimental_recma_transforms: None,
}
}
}

/// Configuration (optional).
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serializable", derive(serde::Serialize, serde::Deserialize))]
Expand Down
2 changes: 1 addition & 1 deletion src/hast_util_to_swc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use swc_core::ecma::ast::{
pub const MAGIC_EXPLICIT_MARKER: u32 = 1337;

/// Result.
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Program {
/// File path.
pub path: Option<String>,
Expand Down
46 changes: 42 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
#![allow(clippy::cast_precision_loss)]

extern crate markdown;

mod configuration;
mod hast;
pub mod hast;
mod hast_util_to_swc;
mod mdast_util_to_hast;
mod mdx_plugin_recma_document;
Expand All @@ -32,7 +33,9 @@ use crate::{
};
use markdown::{to_mdast, Constructs, Location, ParseOptions};

pub use crate::configuration::{MdxConstructs, MdxParseOptions, Options};
pub use crate::configuration::{
HastNode, MdastNode, MdxConstructs, MdxParseOptions, Options, PluginOptions, RecmaProgram,
};
pub use crate::mdx_plugin_recma_document::JsxRuntime;

/// Turn MDX into JavaScript.
Expand All @@ -53,6 +56,20 @@ pub use crate::mdx_plugin_recma_document::JsxRuntime;
/// This project errors for many different reasons, such as syntax errors in
/// the MDX format or misconfiguration.
pub fn compile(value: &str, options: &Options) -> Result<String, String> {
compile_with_plugins(value, options, &PluginOptions::default())
}

/// Turn MDX into JavaScript using the specified Plugins
///
/// ## Errors
///
/// This project errors for many different reasons, such as syntax errors in
/// the MDX format or misconfiguration.
pub fn compile_with_plugins(
value: &str,
options: &Options,
plugins: &PluginOptions,
) -> Result<String, String> {
let parse_options = ParseOptions {
constructs: Constructs {
attention: options.parse.constructs.attention,
Expand Down Expand Up @@ -111,12 +128,33 @@ pub fn compile(value: &str, options: &Options) -> Result<String, String> {
};

let location = Location::new(value.as_bytes());
let mdast = to_mdast(value, &parse_options)?;
let hast = mdast_util_to_hast(&mdast);
let mut mdast = to_mdast(value, &parse_options)?;

if let Some(mdast_plugins) = &plugins.experimental_mdast_transforms {
for plugin in mdast_plugins {
plugin(&mut mdast)?;
}
}

let mut hast = mdast_util_to_hast(&mdast);

if let Some(hast_plugins) = &plugins.experimental_hast_transforms {
for plugin in hast_plugins {
plugin(&mut hast)?;
}
}

let mut program = hast_util_to_swc(&hast, options.filepath.clone(), Some(&location))?;

mdx_plugin_recma_document(&mut program, &document_options, Some(&location))?;
mdx_plugin_recma_jsx_rewrite(&mut program, &rewrite_options, Some(&location));

if let Some(recma_plugins) = &plugins.experimental_recma_transforms {
for plugin in recma_plugins {
plugin(&mut program)?;
}
}

if !options.jsx {
swc_util_build_jsx(&mut program, &build_options, Some(&location))?;
}
Expand Down