diff --git a/Cargo.lock b/Cargo.lock index d7d746d..baabf17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64-simd" version = "0.7.0" @@ -104,6 +110,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bytecheck" version = "0.6.11" @@ -176,6 +191,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -219,6 +243,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "cssparser" version = "0.29.6" @@ -318,6 +352,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dtoa" version = "1.0.9" @@ -391,6 +435,16 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -462,6 +516,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "is-macro" version = "0.3.0" @@ -792,7 +856,9 @@ dependencies = [ "swc_ecma_ast", "swc_ecma_codegen", "swc_ecma_parser", + "swc_ecma_transforms_base", "swc_ecma_visit", + "swc_ecmascript", ] [[package]] @@ -1145,6 +1211,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "ryu-js" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f" + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1243,6 +1315,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "simd-abstraction" version = "0.7.1" @@ -1410,6 +1493,31 @@ dependencies = [ "url", ] +[[package]] +name = "swc_config" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba1c7a40d38f9dd4e9a046975d3faf95af42937b34b2b963be4d8f01239584b" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "swc_config_macro", +] + +[[package]] +name = "swc_config_macro" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5b5aaca9a0082be4515f0fbbecc191bf5829cd25b5b9c0a2810f6a2bb0d6829" +dependencies = [ + "pmutil", + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.31", +] + [[package]] name = "swc_ecma_ast" version = "0.110.0" @@ -1478,6 +1586,116 @@ dependencies = [ "typed-arena", ] +[[package]] +name = "swc_ecma_transforms" +version = "0.225.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579c71cbf89eb244bb1ce890360ef53e472fbd90a3bcd15d7d983c5d596d730d" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_typescript", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_base" +version = "0.134.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d22969bd30d953cd5d217dda6d50d0f2fc15e058c373cfdb9971a2f455e457" +dependencies = [ + "better_scoped_tls", + "bitflags 2.4.0", + "indexmap", + "once_cell", + "phf 0.10.1", + "rustc-hash", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_utils", + "swc_ecma_visit", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_macros" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188eab297da773836ef5cf2af03ee5cca7a563e1be4b146f8141452c28cc690" +dependencies = [ + "pmutil", + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.31", +] + +[[package]] +name = "swc_ecma_transforms_react" +version = "0.180.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64686f3792449f6987859f94c9451fb03be8edc6284119887a419b70686b564" +dependencies = [ + "base64", + "dashmap", + "indexmap", + "once_cell", + "serde", + "sha-1", + "string_enum", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms_base", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_typescript" +version = "0.184.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0773e1eb2b46a4157a3279cd850bd643577c50dc0fbae01f4fed8f36085335e6" +dependencies = [ + "ryu-js", + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_react", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_utils" +version = "0.124.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ded5277c882211a7271e285863bfec54715eafd9b2b4fd7421635310c9d3e4" +dependencies = [ + "indexmap", + "num_cpus", + "once_cell", + "rustc-hash", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_visit", + "tracing", + "unicode-id", +] + [[package]] name = "swc_ecma_visit" version = "0.96.0" @@ -1492,6 +1710,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "swc_ecmascript" +version = "0.235.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e0e91dd82505839db27ae3731679aa005d07a493cffffa5650a93433d137eb4" +dependencies = [ + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms", +] + [[package]] name = "swc_eq_ignore_macros" version = "0.1.2" @@ -1651,6 +1880,12 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/Cargo.toml b/Cargo.toml index b4314ef..af9df2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,8 @@ swc_ecma_ast = {version = "0.110.0"} swc_ecma_codegen = "0.146.0" swc_ecma_parser = "0.141.0" swc_ecma_visit = "0.96.0" +swc_ecmascript = {version = "0.235.1", features = ["transforms", "typescript"]} +swc_ecma_transforms_base = "0.134.0" [build-dependencies] napi-build = "2.0.1" diff --git a/__test__/index.spec.mjs.md b/__test__/index.spec.mjs.md index 3078e0c..33e6711 100644 --- a/__test__/index.spec.mjs.md +++ b/__test__/index.spec.mjs.md @@ -11,7 +11,7 @@ Generated by [AVA](https://avajs.dev). `import React from 'react';␊ import './Mod.scss';␊ function Cc() {␊ - return (
␊ + return
␊ ␊ 成员123: 4000+␊ ␊ @@ -25,11 +25,11 @@ Generated by [AVA](https://avajs.dev). ␊
␊ ␊ -
);␊ + ;␊ }␊ export default class Mod extends React.Component {␊ getDom() {␊ - return (
␊ + return
␊ ␊ 成员123: 4000+␊ ␊ @@ -43,10 +43,10 @@ Generated by [AVA](https://avajs.dev). ␊
␊ ␊ -
);␊ + ;␊ }␊ render() {␊ - return (
␊ ␊ -
);␊ + ;␊ }␊ - }␊ + } // function Mod() {␊ + // return (␊ + //
␊ + //
␊ + // ␊ + // 超能芭比 5分钟前查看团购␊ + //
␊ + //
␊ + // ␊ + //
␊ + // 巴拉巴拉小魔仙␊ + // 成员: 4000+␊ + //
␊ + //
␊ + // ␊ + // slslsl-jsj␊ + // 复制␊ + //
␊ + //
␊ + //
␊ + // ␊ + // 团长介绍:售前售后进群售前售后进群售前售后进群售前售后进群VXklsidohh...␊ + // ␊ + // ␊ + //
␊ + //
␊ + // )␊ + // }␊ ` diff --git a/__test__/index.spec.mjs.snap b/__test__/index.spec.mjs.snap index c83d5bf..34cd99d 100644 Binary files a/__test__/index.spec.mjs.snap and b/__test__/index.spec.mjs.snap differ diff --git a/src/document.rs b/src/document.rs index b0e4210..46b10b4 100644 --- a/src/document.rs +++ b/src/document.rs @@ -4,11 +4,12 @@ use ego_tree::Tree; use swc_common::{ errors::{ColorConfig, Handler}, sync::Lrc, - SourceMap, + SourceMap, Mark, comments::SingleThreadedComments, Globals, GLOBALS, }; -use swc_ecma_ast::{EsVersion, Module}; +use swc_ecma_ast::{EsVersion, Program}; use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig}; -use swc_ecma_visit::VisitWith; +use swc_ecma_visit::{VisitWith, FoldWith}; +use swc_ecma_transforms_base::{fixer::fixer, hygiene::hygiene, resolver}; use crate::{ scraper::{ElementRef, Node, Selector}, @@ -17,7 +18,7 @@ use crate::{ pub struct JSXDocument { pub tree: Tree, - pub module: Option, + pub program: Option, pub jsx_record: Option, } @@ -25,20 +26,17 @@ impl JSXDocument { pub fn new() -> Self { JSXDocument { tree: Tree::new(Node::Document), - module: None, + program: None, jsx_record: None, } } - pub fn parse(&mut self, jsx: String) { - // 初始化 swc 的 SourceMap - let cm: Lrc = Default::default(); + pub fn parse(&mut self, jsx: String, cm: Lrc, comments: &SingleThreadedComments) { // 初始化 swc 的错误处理器 let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(cm.clone())); // 将 JSX 代码转换为 SourceFile let fm = cm.new_source_file(swc_common::FileName::Anon, jsx); - // 初始化 swc 的词法分析器 let lexer = Lexer::new( Syntax::Typescript(TsConfig { @@ -48,23 +46,31 @@ impl JSXDocument { }), EsVersion::Es2019, StringInput::from(&*fm), - None, + Some(comments), ); // 初始化 swc 的语法分析器 let mut parser = Parser::new_from(lexer); for e in parser.take_errors() { e.into_diagnostic(&handler).emit(); } - - let module = parser - .parse_module() - .map_err(|e| e.into_diagnostic(&handler).emit()) - .expect("解析 JSX 失败"); - let mut jsx_record: JSXRecord = HashMap::new(); - let mut vistor = AstVisitor::new(&module, &mut self.tree, &mut jsx_record); - module.visit_with(&mut vistor); - self.module = Some(module); - self.jsx_record = Some(jsx_record); + let program = parser + .parse_program() + .map_err(|e| e.into_diagnostic(&handler).emit()) + .expect("解析 JSX 失败"); + + let globals = Globals::default(); + GLOBALS.set(&globals, || { + let unresolved_mark = Mark::new(); + let top_level_mark = Mark::new(); + let program = program.fold_with(&mut resolver(unresolved_mark, top_level_mark, true)); + let program = program.fold_with(&mut hygiene()); + let program = program.fold_with(&mut fixer(Some(comments))); + let mut jsx_record: JSXRecord = HashMap::new(); + let mut vistor = AstVisitor::new(&program, &mut self.tree, &mut jsx_record); + program.visit_with(&mut vistor); + self.program = Some(program); + self.jsx_record = Some(jsx_record); + }); } pub fn select<'a>(&self, selector: &'a Selector) -> Vec { diff --git a/src/lib.rs b/src/lib.rs index 418bd9d..3ae8411 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ use std::{cell::RefCell, rc::Rc}; -use swc_common::{comments::SingleThreadedComments, SourceMap}; +use swc_common::{comments::SingleThreadedComments, SourceMap, sync::Lrc}; use swc_ecma_codegen::{text_writer::JsWriter, Emitter}; use crate::{document::JSXDocument, style_parser::StyleParser, style_write::StyleWrite}; @@ -20,8 +20,10 @@ mod visitor; #[napi] pub fn parse(component: String, styles: Vec) -> String { // 解析组件文件 + let cm: Lrc = Default::default(); + let comments = SingleThreadedComments::default(); let mut document = JSXDocument::new(); - document.parse(component); + document.parse(component, cm.clone(), &comments); // 解析样式文件 let css = styles.join("\n"); @@ -29,16 +31,13 @@ pub fn parse(component: String, styles: Vec) -> String { style_parser.parse(&css); let style_record = style_parser.calc(); - let module = Rc::new(RefCell::new(document.module.as_ref().unwrap().clone())); + let program = Rc::new(RefCell::new(document.program.as_ref().unwrap().clone())); let jsx_record = Rc::new(RefCell::new(document.jsx_record.as_ref().unwrap().clone())); let style_record = Rc::new(RefCell::new(style_record)); - let mut style_write = StyleWrite::new(module.clone(), jsx_record.clone(), style_record.clone()); + let mut style_write = StyleWrite::new(program.clone(), jsx_record.clone(), style_record.clone()); style_write.write(); // ast 转代码 - let cm = Rc::new(SourceMap::default()); - let comments = SingleThreadedComments::default(); - let mut buf = Vec::new(); { let writer = Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None)); @@ -48,7 +47,7 @@ pub fn parse(component: String, styles: Vec) -> String { wr: writer, comments: Some(&comments), }; - emitter.emit_module(&module.borrow()).unwrap(); + emitter.emit_program(&program.borrow()).unwrap(); } let code = String::from_utf8(buf).unwrap().replace("\r\n", "\n"); code diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5bcbcea --- /dev/null +++ b/src/main.rs @@ -0,0 +1,49 @@ +use std::{fs, rc::Rc, cell::RefCell}; + +use swc_common::{SourceMap, comments::SingleThreadedComments, sync::Lrc}; +use swc_ecma_codegen::{text_writer::JsWriter, Emitter}; + +use crate::{document::JSXDocument, style_parser::StyleParser, style_write::StyleWrite}; + +mod document; +mod scraper; +mod utils; +mod visitor; +mod style_parser; +mod style_write; + +fn main() { + // 使用 swc 解析 JSX + let jsx = fs::read_to_string("__test__/fixure/mod.jsx").unwrap(); + let css = fs::read_to_string("__test__/fixure/Mod.scss").unwrap(); + let cm: Lrc = Default::default(); + let comments = SingleThreadedComments::default(); + let mut document = JSXDocument::new(); + document.parse(jsx, cm.clone(), &comments); + + println!(); + let mut style_parser = StyleParser::new(&document); + style_parser.parse(&css); + let style_record = style_parser.calc(); + + // println!("{:?}", style_record) + let program = Rc::new(RefCell::new(document.program.as_ref().unwrap().clone())); + let jsx_record = Rc::new(RefCell::new(document.jsx_record.as_ref().unwrap().clone())); + let style_record = Rc::new(RefCell::new(style_record)); + let mut style_write = StyleWrite::new(program.clone(), jsx_record.clone(), style_record.clone()); + style_write.write(); + + // ast 转代码 + let mut buf = vec![]; + { + let mut emitter = Emitter { + cfg: swc_ecma_codegen::Config::default(), + cm: cm.clone(), + comments: Some(&comments), + wr: JsWriter::new(cm.clone(), "\n", &mut buf, None), + }; + emitter.emit_program(&program.borrow()).unwrap(); + } + let code = String::from_utf8(buf).unwrap().replace("\r\n", "\n"); + println!("{}", code); +} diff --git a/src/style_write.rs b/src/style_write.rs index 7ac2655..4a75ab8 100644 --- a/src/style_write.rs +++ b/src/style_write.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; use ego_tree::NodeId; -use swc_ecma_ast::Module; +use swc_ecma_ast::Program; use swc_ecma_visit::VisitMutWith; use crate::{ @@ -10,14 +10,14 @@ use crate::{ }; pub struct StyleWrite<'i> { - pub module: Rc>, + pub module: Rc>, pub jsx_record: Rc>, pub style_record: Rc>>>, } impl<'i> StyleWrite<'i> { pub fn new( - module: Rc>, + module: Rc>, jsx_record: Rc>, style_record: Rc>>>, ) -> Self { diff --git a/src/visitor.rs b/src/visitor.rs index a9b0f4c..3aaf152 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -12,7 +12,7 @@ use swc_common::{Span, DUMMY_SP}; use swc_ecma_ast::{ Callee, ClassDecl, ClassMember, DefaultDecl, ExportDefaultDecl, ExportDefaultExpr, Expr, FnDecl, Function, Ident, JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXElement, - JSXElementChild, JSXElementName, JSXExpr, KeyValueProp, Lit, MemberProp, Module, Prop, PropName, + JSXElementChild, JSXElementName, JSXExpr, KeyValueProp, Lit, MemberProp, Program, Prop, PropName, PropOrSpread, Stmt, Str, }; use swc_ecma_visit::{ @@ -52,14 +52,14 @@ fn recursion_sub_tree<'a>(node: &NodeRef, current: &mut NodeMut<'a, Node>) pub struct JSXVisitor<'a> { pub tree: &'a mut Tree, - pub module: &'a Module, + pub module: &'a Program, pub jsx_record: &'a mut JSXRecord, pub root_node: Option, pub current_node: Option, } impl<'a> JSXVisitor<'a> { - pub fn new(tree: &'a mut Tree, module: &'a Module, jsx_record: &'a mut JSXRecord) -> Self { + pub fn new(tree: &'a mut Tree, module: &'a Program, jsx_record: &'a mut JSXRecord) -> Self { JSXVisitor { tree, module, @@ -295,7 +295,7 @@ pub enum SearchType { } pub struct JSXFragmentVisitor<'a> { - pub module: &'a Module, + pub module: &'a Program, pub tree: Tree, pub jsx_record: &'a mut JSXRecord, pub search_fn: &'a str, @@ -304,7 +304,7 @@ pub struct JSXFragmentVisitor<'a> { impl<'a> JSXFragmentVisitor<'a> { pub fn new( - module: &'a Module, + module: &'a Program, jsx_record: &'a mut JSXRecord, search_fn: &'a str, search_type: SearchType, @@ -377,13 +377,13 @@ impl<'a> Visit for JSXFragmentVisitor<'a> { pub struct AstVisitor<'a> { pub export_default_name: Option, - pub module: &'a Module, + pub module: &'a Program, pub tree: &'a mut Tree, pub jsx_record: &'a mut JSXRecord, } impl<'a> AstVisitor<'a> { - pub fn new(module: &'a Module, tree: &'a mut Tree, jsx_record: &'a mut JSXRecord) -> Self { + pub fn new(module: &'a Program, tree: &'a mut Tree, jsx_record: &'a mut JSXRecord) -> Self { AstVisitor { export_default_name: None, module,