aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_utils/to_document.rs
diff options
context:
space:
mode:
authorLibravatar Titus Wormer <tituswormer@gmail.com>2022-10-11 16:27:38 +0200
committerLibravatar Titus Wormer <tituswormer@gmail.com>2022-10-11 16:27:38 +0200
commite484d1ecc5e405259767c0fd84072226fee40b71 (patch)
tree71d2a2f67870052aa026d0087dfb06cb96c40e17 /tests/test_utils/to_document.rs
parent73d8609565b808ac73df5ac34e6d4f7f23c25ad6 (diff)
downloadmarkdown-rs-e484d1ecc5e405259767c0fd84072226fee40b71.tar.gz
markdown-rs-e484d1ecc5e405259767c0fd84072226fee40b71.tar.bz2
markdown-rs-e484d1ecc5e405259767c0fd84072226fee40b71.zip
Refactor test utilities to improve names
Diffstat (limited to 'tests/test_utils/to_document.rs')
-rw-r--r--tests/test_utils/to_document.rs658
1 files changed, 0 insertions, 658 deletions
diff --git a/tests/test_utils/to_document.rs b/tests/test_utils/to_document.rs
deleted file mode 100644
index 938df1b..0000000
--- a/tests/test_utils/to_document.rs
+++ /dev/null
@@ -1,658 +0,0 @@
-extern crate swc_ecma_ast;
-use crate::test_utils::{
- micromark_swc_utils::{bytepos_to_point, prefix_error_with_point, span_to_position},
- to_swc::Program,
-};
-use micromark::{
- unist::{Point, Position},
- Location,
-};
-
-/// JSX runtimes.
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
-pub enum JsxRuntime {
- /// Automatic runtime.
- ///
- /// With the automatic runtime, some module is expected to exist somewhere.
- /// That modules is expected to expose a certain API.
- /// The compiler adds an import of that module and compiles JSX away to
- /// function calls that use that API.
- #[default]
- Automatic,
- /// Classic runtime.
- ///
- /// With the classic runtime, you define two values yourself in each file,
- /// which are expected to work a certain way.
- /// The compiler compiles JSX away to function calls using those two values.
- Classic,
-}
-
-/// Configuration.
-#[derive(Debug, PartialEq, Eq)]
-pub struct Options {
- /// Pragma for JSX (used in classic runtime).
- ///
- /// Default: `React.createElement`.
- pub pragma: Option<String>,
- /// Pragma for JSX fragments (used in classic runtime).
- ///
- /// Default: `React.Fragment`.
- pub pragma_frag: Option<String>,
- /// Where to import the identifier of `pragma` from (used in classic runtime).
- ///
- /// Default: `react`.
- pub pragma_import_source: Option<String>,
- /// Place to import automatic JSX runtimes from (used in automatic runtime).
- ///
- /// Default: `react`.
- pub jsx_import_source: Option<String>,
- /// JSX runtime to use.
- ///
- /// Default: `automatic`.
- pub jsx_runtime: Option<JsxRuntime>,
-}
-
-impl Default for Options {
- /// Use the automatic JSX runtime with React.
- fn default() -> Self {
- Self {
- pragma: None,
- pragma_frag: None,
- pragma_import_source: None,
- jsx_import_source: None,
- jsx_runtime: Some(JsxRuntime::default()),
- }
- }
-}
-
-#[allow(dead_code)]
-pub fn to_document(
- mut program: Program,
- options: &Options,
- location: Option<&Location>,
-) -> Result<Program, String> {
- // New body children.
- let mut replacements = vec![];
-
- // Inject JSX configuration comment.
- if let Some(runtime) = &options.jsx_runtime {
- let mut pragmas = vec![];
- let react = &"react".into();
- let create_element = &"React.createElement".into();
- let fragment = &"React.Fragment".into();
-
- if *runtime == JsxRuntime::Automatic {
- pragmas.push("@jsxRuntime automatic".into());
- pragmas.push(format!(
- "@jsxImportSource {}",
- if let Some(jsx_import_source) = &options.jsx_import_source {
- jsx_import_source
- } else {
- react
- }
- ));
- } else {
- pragmas.push("@jsxRuntime classic".into());
- pragmas.push(format!(
- "@jsx {}",
- if let Some(pragma) = &options.pragma {
- pragma
- } else {
- create_element
- }
- ));
- pragmas.push(format!(
- "@jsxFrag {}",
- if let Some(pragma_frag) = &options.pragma_frag {
- pragma_frag
- } else {
- fragment
- }
- ));
- }
-
- if !pragmas.is_empty() {
- program.comments.insert(
- 0,
- swc_common::comments::Comment {
- kind: swc_common::comments::CommentKind::Block,
- text: pragmas.join(" ").into(),
- span: swc_common::DUMMY_SP,
- },
- );
- }
- }
-
- // Inject an import in the classic runtime for the pragma (and presumably,
- // fragment).
- if options.jsx_runtime == Some(JsxRuntime::Classic) {
- let pragma = if let Some(pragma) = &options.pragma {
- pragma
- } else {
- "React"
- };
- let sym = pragma.split('.').next().expect("first item always exists");
-
- replacements.push(swc_ecma_ast::ModuleItem::ModuleDecl(
- swc_ecma_ast::ModuleDecl::Import(swc_ecma_ast::ImportDecl {
- specifiers: vec![swc_ecma_ast::ImportSpecifier::Named(
- swc_ecma_ast::ImportNamedSpecifier {
- local: swc_ecma_ast::Ident {
- sym: sym.into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- },
- imported: None,
- span: swc_common::DUMMY_SP,
- is_type_only: false,
- },
- )],
- src: Box::new(swc_ecma_ast::Str {
- value: (if let Some(source) = &options.pragma_import_source {
- source.clone()
- } else {
- "react".into()
- })
- .into(),
- span: swc_common::DUMMY_SP,
- raw: None,
- }),
- type_only: false,
- asserts: None,
- span: swc_common::DUMMY_SP,
- }),
- ));
- }
-
- // Find the `export default`, the JSX expression, and leave the rest as it
- // is.
- let mut input = program.module.body.split_off(0);
- input.reverse();
- let mut layout = false;
- let mut layout_position = None;
- let content = true;
-
- while let Some(module_item) = input.pop() {
- match module_item {
- // ```js
- // export default props => <>{props.children}</>
- // ```
- //
- // Treat it as an inline layout declaration.
- //
- // In estree, the below two are the same node (`ExportDefault`).
- swc_ecma_ast::ModuleItem::ModuleDecl(swc_ecma_ast::ModuleDecl::ExportDefaultDecl(
- decl,
- )) => {
- if layout {
- return Err(create_double_layout_message(
- bytepos_to_point(&decl.span.lo, location).as_ref(),
- layout_position.as_ref(),
- ));
- }
-
- layout = true;
- layout_position = span_to_position(&decl.span, location);
- match decl.decl {
- swc_ecma_ast::DefaultDecl::Class(cls) => {
- replacements.push(create_layout_decl(swc_ecma_ast::Expr::Class(cls)))
- }
- swc_ecma_ast::DefaultDecl::Fn(func) => {
- replacements.push(create_layout_decl(swc_ecma_ast::Expr::Fn(func)))
- }
- swc_ecma_ast::DefaultDecl::TsInterfaceDecl(_) => {
- return Err(
- prefix_error_with_point(
- "Cannot use TypeScript interface declarations as default export in MDX files. The default export is reserved for a layout, which must be a component".into(),
- bytepos_to_point(&decl.span.lo, location).as_ref()
- )
- );
- }
- }
- }
- swc_ecma_ast::ModuleItem::ModuleDecl(swc_ecma_ast::ModuleDecl::ExportDefaultExpr(
- expr,
- )) => {
- if layout {
- return Err(create_double_layout_message(
- bytepos_to_point(&expr.span.lo, location).as_ref(),
- layout_position.as_ref(),
- ));
- }
-
- layout = true;
- layout_position = span_to_position(&expr.span, location);
- replacements.push(create_layout_decl(*expr.expr));
- }
- // ```js
- // export {a, b as c} from 'd'
- // export {a, b as c}
- // ```
- swc_ecma_ast::ModuleItem::ModuleDecl(swc_ecma_ast::ModuleDecl::ExportNamed(
- mut named_export,
- )) => {
- // SWC is currently crashing when generating code, w/o source
- // map, if an actual location is set on this node.
- named_export.span = swc_common::DUMMY_SP;
-
- let mut index = 0;
- let mut id = None;
-
- while index < named_export.specifiers.len() {
- let mut take = false;
- // Note: the `swc_ecma_ast::ExportSpecifier::Default`
- // branch of this looks interesting, but as far as I
- // understand it *is not* valid ES.
- // `export a from 'b'` is a syntax error, even in SWC.
- if let swc_ecma_ast::ExportSpecifier::Named(named) =
- &named_export.specifiers[index]
- {
- if let Some(swc_ecma_ast::ModuleExportName::Ident(ident)) = &named.exported
- {
- if ident.sym.as_ref() == "default" {
- // For some reason the AST supports strings
- // instead of identifiers.
- // Looks like some TC39 proposal. Ignore for now
- // and only do things if this is an ID.
- if let swc_ecma_ast::ModuleExportName::Ident(ident) = &named.orig {
- if layout {
- return Err(create_double_layout_message(
- bytepos_to_point(&ident.span.lo, location).as_ref(),
- layout_position.as_ref(),
- ));
- }
- layout = true;
- layout_position = span_to_position(&ident.span, location);
- take = true;
- id = Some(ident.clone());
- }
- }
- }
- }
-
- if take {
- named_export.specifiers.remove(index);
- } else {
- index += 1;
- }
- }
-
- if let Some(id) = id {
- let source = named_export.src.clone();
-
- // If there was just a default export, we can drop the original node.
- if !named_export.specifiers.is_empty() {
- // Pass through.
- replacements.push(swc_ecma_ast::ModuleItem::ModuleDecl(
- swc_ecma_ast::ModuleDecl::ExportNamed(named_export),
- ));
- }
-
- // It’s an `export {x} from 'y'`, so generate an import.
- if let Some(source) = source {
- replacements.push(swc_ecma_ast::ModuleItem::ModuleDecl(
- swc_ecma_ast::ModuleDecl::Import(swc_ecma_ast::ImportDecl {
- specifiers: vec![swc_ecma_ast::ImportSpecifier::Named(
- swc_ecma_ast::ImportNamedSpecifier {
- local: swc_ecma_ast::Ident {
- sym: "MDXLayout".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- },
- imported: Some(swc_ecma_ast::ModuleExportName::Ident(id)),
- span: swc_common::DUMMY_SP,
- is_type_only: false,
- },
- )],
- src: source,
- type_only: false,
- asserts: None,
- span: swc_common::DUMMY_SP,
- }),
- ))
- }
- // It’s an `export {x}`, so generate a variable declaration.
- else {
- replacements.push(create_layout_decl(swc_ecma_ast::Expr::Ident(id)));
- }
- } else {
- // Pass through.
- replacements.push(swc_ecma_ast::ModuleItem::ModuleDecl(
- swc_ecma_ast::ModuleDecl::ExportNamed(named_export),
- ));
- }
- }
- swc_ecma_ast::ModuleItem::ModuleDecl(swc_ecma_ast::ModuleDecl::Import(mut x)) => {
- // SWC is currently crashing when generating code, w/o source
- // map, if an actual location is set on this node.
- x.span = swc_common::DUMMY_SP;
- // Pass through.
- replacements.push(swc_ecma_ast::ModuleItem::ModuleDecl(
- swc_ecma_ast::ModuleDecl::Import(x),
- ));
- }
- swc_ecma_ast::ModuleItem::ModuleDecl(swc_ecma_ast::ModuleDecl::ExportDecl(_))
- | swc_ecma_ast::ModuleItem::ModuleDecl(swc_ecma_ast::ModuleDecl::ExportAll(_))
- | swc_ecma_ast::ModuleItem::ModuleDecl(swc_ecma_ast::ModuleDecl::TsImportEquals(_))
- | swc_ecma_ast::ModuleItem::ModuleDecl(swc_ecma_ast::ModuleDecl::TsExportAssignment(
- _,
- ))
- | swc_ecma_ast::ModuleItem::ModuleDecl(swc_ecma_ast::ModuleDecl::TsNamespaceExport(
- _,
- )) => {
- // Pass through.
- replacements.push(module_item);
- }
- swc_ecma_ast::ModuleItem::Stmt(swc_ecma_ast::Stmt::Expr(expr_stmt)) => {
- match *expr_stmt.expr {
- swc_ecma_ast::Expr::JSXElement(elem) => {
- replacements.append(&mut create_mdx_content(
- Some(swc_ecma_ast::Expr::JSXElement(elem)),
- layout,
- ));
- }
- swc_ecma_ast::Expr::JSXFragment(mut frag) => {
- // Unwrap if possible.
- if frag.children.len() == 1 {
- let item = frag.children.pop().unwrap();
-
- if let swc_ecma_ast::JSXElementChild::JSXElement(elem) = item {
- replacements.append(&mut create_mdx_content(
- Some(swc_ecma_ast::Expr::JSXElement(elem)),
- layout,
- ));
- continue;
- }
-
- frag.children.push(item)
- }
-
- replacements.append(&mut create_mdx_content(
- Some(swc_ecma_ast::Expr::JSXFragment(frag)),
- layout,
- ));
- }
- _ => {
- // Pass through.
- replacements.push(swc_ecma_ast::ModuleItem::Stmt(
- swc_ecma_ast::Stmt::Expr(expr_stmt),
- ));
- }
- }
- }
- swc_ecma_ast::ModuleItem::Stmt(stmt) => {
- replacements.push(swc_ecma_ast::ModuleItem::Stmt(stmt));
- }
- }
- }
-
- // Generate an empty component.
- if !content {
- replacements.append(&mut create_mdx_content(None, layout));
- }
-
- // ```jsx
- // export default MDXContent
- // ```
- replacements.push(swc_ecma_ast::ModuleItem::ModuleDecl(
- swc_ecma_ast::ModuleDecl::ExportDefaultExpr(swc_ecma_ast::ExportDefaultExpr {
- expr: Box::new(swc_ecma_ast::Expr::Ident(swc_ecma_ast::Ident {
- sym: "MDXContent".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- })),
- span: swc_common::DUMMY_SP,
- }),
- ));
-
- program.module.body = replacements;
-
- Ok(program)
-}
-
-/// Create a content component.
-fn create_mdx_content(
- expr: Option<swc_ecma_ast::Expr>,
- has_internal_layout: bool,
-) -> Vec<swc_ecma_ast::ModuleItem> {
- // ```jsx
- // <MDXLayout {...props}>xxx</MDXLayout>
- // ```
- let mut result = swc_ecma_ast::Expr::JSXElement(Box::new(swc_ecma_ast::JSXElement {
- opening: swc_ecma_ast::JSXOpeningElement {
- name: swc_ecma_ast::JSXElementName::Ident(swc_ecma_ast::Ident {
- sym: "MDXLayout".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- }),
- attrs: vec![swc_ecma_ast::JSXAttrOrSpread::SpreadElement(
- swc_ecma_ast::SpreadElement {
- dot3_token: swc_common::DUMMY_SP,
- expr: Box::new(swc_ecma_ast::Expr::Ident(swc_ecma_ast::Ident {
- sym: "props".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- })),
- },
- )],
- self_closing: false,
- type_args: None,
- span: swc_common::DUMMY_SP,
- },
- closing: Some(swc_ecma_ast::JSXClosingElement {
- name: swc_ecma_ast::JSXElementName::Ident(swc_ecma_ast::Ident {
- sym: "MDXLayout".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- }),
- span: swc_common::DUMMY_SP,
- }),
- // ```jsx
- // <_createMdxContent {...props} />
- // ```
- children: vec![swc_ecma_ast::JSXElementChild::JSXElement(Box::new(
- swc_ecma_ast::JSXElement {
- opening: swc_ecma_ast::JSXOpeningElement {
- name: swc_ecma_ast::JSXElementName::Ident(swc_ecma_ast::Ident {
- sym: "_createMdxContent".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- }),
- attrs: vec![swc_ecma_ast::JSXAttrOrSpread::SpreadElement(
- swc_ecma_ast::SpreadElement {
- dot3_token: swc_common::DUMMY_SP,
- expr: Box::new(swc_ecma_ast::Expr::Ident(swc_ecma_ast::Ident {
- sym: "props".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- })),
- },
- )],
- self_closing: true,
- type_args: None,
- span: swc_common::DUMMY_SP,
- },
- closing: None,
- children: vec![],
- span: swc_common::DUMMY_SP,
- },
- ))],
- span: swc_common::DUMMY_SP,
- }));
-
- if !has_internal_layout {
- // ```jsx
- // MDXLayout ? <MDXLayout>xxx</MDXLayout> : _createMdxContent(props)
- // ```
- result = swc_ecma_ast::Expr::Cond(swc_ecma_ast::CondExpr {
- test: Box::new(swc_ecma_ast::Expr::Ident(swc_ecma_ast::Ident {
- sym: "MDXLayout".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- })),
- cons: Box::new(result),
- alt: Box::new(swc_ecma_ast::Expr::Call(swc_ecma_ast::CallExpr {
- callee: swc_ecma_ast::Callee::Expr(Box::new(swc_ecma_ast::Expr::Ident(
- swc_ecma_ast::Ident {
- sym: "_createMdxContent".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- },
- ))),
- args: vec![swc_ecma_ast::ExprOrSpread {
- spread: None,
- expr: Box::new(swc_ecma_ast::Expr::Ident(swc_ecma_ast::Ident {
- sym: "props".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- })),
- }],
- type_args: None,
- span: swc_common::DUMMY_SP,
- })),
- span: swc_common::DUMMY_SP,
- });
- }
-
- // ```jsx
- // function _createMdxContent(props) {
- // return xxx
- // }
- // ```
- let create_mdx_content = swc_ecma_ast::ModuleItem::Stmt(swc_ecma_ast::Stmt::Decl(
- swc_ecma_ast::Decl::Fn(swc_ecma_ast::FnDecl {
- ident: swc_ecma_ast::Ident {
- sym: "_createMdxContent".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- },
- declare: false,
- function: Box::new(swc_ecma_ast::Function {
- params: vec![swc_ecma_ast::Param {
- pat: swc_ecma_ast::Pat::Ident(swc_ecma_ast::BindingIdent {
- id: swc_ecma_ast::Ident {
- sym: "props".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- },
- type_ann: None,
- }),
- decorators: vec![],
- span: swc_common::DUMMY_SP,
- }],
- decorators: vec![],
- body: Some(swc_ecma_ast::BlockStmt {
- stmts: vec![swc_ecma_ast::Stmt::Return(swc_ecma_ast::ReturnStmt {
- arg: Some(Box::new(expr.unwrap_or({
- swc_ecma_ast::Expr::Lit(swc_ecma_ast::Lit::Null(swc_ecma_ast::Null {
- span: swc_common::DUMMY_SP,
- }))
- }))),
- span: swc_common::DUMMY_SP,
- })],
- span: swc_common::DUMMY_SP,
- }),
- is_generator: false,
- is_async: false,
- type_params: None,
- return_type: None,
- span: swc_common::DUMMY_SP,
- }),
- }),
- ));
-
- // ```jsx
- // function MDXContent(props = {}) {
- // return <MDXLayout>xxx</MDXLayout>
- // }
- // ```
- let mdx_content = swc_ecma_ast::ModuleItem::Stmt(swc_ecma_ast::Stmt::Decl(
- swc_ecma_ast::Decl::Fn(swc_ecma_ast::FnDecl {
- ident: swc_ecma_ast::Ident {
- sym: "MDXContent".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- },
- declare: false,
- function: Box::new(swc_ecma_ast::Function {
- params: vec![swc_ecma_ast::Param {
- pat: swc_ecma_ast::Pat::Assign(swc_ecma_ast::AssignPat {
- left: Box::new(swc_ecma_ast::Pat::Ident(swc_ecma_ast::BindingIdent {
- id: swc_ecma_ast::Ident {
- sym: "props".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- },
- type_ann: None,
- })),
- right: Box::new(swc_ecma_ast::Expr::Object(swc_ecma_ast::ObjectLit {
- props: vec![],
- span: swc_common::DUMMY_SP,
- })),
- span: swc_common::DUMMY_SP,
- type_ann: None,
- }),
- decorators: vec![],
- span: swc_common::DUMMY_SP,
- }],
- decorators: vec![],
- body: Some(swc_ecma_ast::BlockStmt {
- stmts: vec![swc_ecma_ast::Stmt::Return(swc_ecma_ast::ReturnStmt {
- arg: Some(Box::new(result)),
- span: swc_common::DUMMY_SP,
- })],
- span: swc_common::DUMMY_SP,
- }),
- is_generator: false,
- is_async: false,
- type_params: None,
- return_type: None,
- span: swc_common::DUMMY_SP,
- }),
- }),
- ));
-
- vec![create_mdx_content, mdx_content]
-}
-
-/// Create a layout, inside the document.
-fn create_layout_decl(expr: swc_ecma_ast::Expr) -> swc_ecma_ast::ModuleItem {
- // ```jsx
- // const MDXLayout = xxx
- // ```
- swc_ecma_ast::ModuleItem::Stmt(swc_ecma_ast::Stmt::Decl(swc_ecma_ast::Decl::Var(Box::new(
- swc_ecma_ast::VarDecl {
- kind: swc_ecma_ast::VarDeclKind::Const,
- declare: false,
- decls: vec![swc_ecma_ast::VarDeclarator {
- name: swc_ecma_ast::Pat::Ident(swc_ecma_ast::BindingIdent {
- id: swc_ecma_ast::Ident {
- sym: "MDXLayout".into(),
- optional: false,
- span: swc_common::DUMMY_SP,
- },
- type_ann: None,
- }),
- init: Some(Box::new(expr)),
- span: swc_common::DUMMY_SP,
- definite: false,
- }],
- span: swc_common::DUMMY_SP,
- },
- ))))
-}
-
-/// Create an error message about multiple layouts.
-fn create_double_layout_message(at: Option<&Point>, previous: Option<&Position>) -> String {
- prefix_error_with_point(
- format!(
- "Cannot specify multiple layouts{}",
- if let Some(previous) = previous {
- format!(" (previous: {:?})", previous)
- } else {
- "".into()
- }
- ),
- at,
- )
-}