From 413ef861ff1eec2e15b9458dd54b2ee2ebf1aa11 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 25 Oct 2024 18:45:40 +0800 Subject: [PATCH 1/4] emit output --- Cargo.lock | 160 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 +- js_code/const.js | 1 + js_code/index.js | 2 + out/bundle.js | 100 ++++++++++++++++++++++++++ src/Compiler.rs | 40 ----------- src/config.rs | 20 ++++++ src/demo.rs | 98 ++++++++++++++++++++++++++ src/main.rs | 112 +++++------------------------ src/rs_compiler.rs | 158 +++++++++++++++++++++++++++++++++++++++++ src/template.rs | 11 +++ src/transform.rs | 55 +++++++++++++++ templates/output.stpl | 92 ++++++++++++++++++++++++ test.js | 1 - 14 files changed, 719 insertions(+), 135 deletions(-) create mode 100644 js_code/const.js create mode 100644 js_code/index.js create mode 100644 out/bundle.js delete mode 100644 src/Compiler.rs create mode 100644 src/config.rs create mode 100644 src/demo.rs create mode 100644 src/rs_compiler.rs create mode 100644 src/template.rs create mode 100644 src/transform.rs create mode 100644 templates/output.stpl delete mode 100644 test.js diff --git a/Cargo.lock b/Cargo.lock index 95ba83d..cb10b7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,6 +122,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -153,6 +165,15 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "indexmap" version = "2.6.0" @@ -178,6 +199,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "itoap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8" + [[package]] name = "js-sys" version = "0.3.69" @@ -193,6 +220,17 @@ version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + [[package]] name = "lock_api" version = "0.4.12" @@ -578,6 +616,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "pathdiff" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" + [[package]] name = "petgraph" version = "0.6.5" @@ -690,7 +734,9 @@ dependencies = [ "oxc_semantic", "oxc_span", "oxc_traverse", + "pathdiff", "pico-args", + "sailfish", "serde_json", ] @@ -718,6 +764,44 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" +[[package]] +name = "sailfish" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d5cd6d4f24f3ab107e949ab424738cf55b03deddce3b184c46985d7b1394ef" +dependencies = [ + "itoap", + "ryu", + "sailfish-macros", + "version_check", +] + +[[package]] +name = "sailfish-compiler" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7254ec7b3651f7f723a9073153f5dcddc1f2bf1bf8d1b23ac71c236ef6360d2b" +dependencies = [ + "filetime", + "home", + "memchr", + "proc-macro2", + "quote", + "serde", + "syn", + "toml", +] + +[[package]] +name = "sailfish-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00812289fe1891c191cc2d9db461352fc410619e07ec2bb748faaa06412619d0" +dependencies = [ + "proc-macro2", + "sailfish-compiler", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -773,6 +857,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -839,6 +932,40 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tsify" version = "0.4.5" @@ -888,6 +1015,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "vsimd" version = "0.8.0" @@ -958,6 +1091,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -1021,3 +1172,12 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index ebff2d6..d9448fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,5 +14,7 @@ oxc_parser = "0.31.0" oxc_semantic = "0.31.0" oxc_span = {version = "0.31.0", features = ["serialize"]} oxc_traverse = "0.31.0" +pathdiff = "0.2.2" pico-args = "0.5.0" -serde_json = "1.0.129" \ No newline at end of file +sailfish = "0.9.0" +serde_json = "1.0.129" diff --git a/js_code/const.js b/js_code/const.js new file mode 100644 index 0000000..a2287a5 --- /dev/null +++ b/js_code/const.js @@ -0,0 +1 @@ +module.exports = 'hello' diff --git a/js_code/index.js b/js_code/index.js new file mode 100644 index 0000000..1ca76b7 --- /dev/null +++ b/js_code/index.js @@ -0,0 +1,2 @@ +const b = require('./const.js') +console.log(b) diff --git a/out/bundle.js b/out/bundle.js new file mode 100644 index 0000000..8b77b66 --- /dev/null +++ b/out/bundle.js @@ -0,0 +1,100 @@ +(function(modules) { + var installedModules = {}; + + // The require function + function __webpack_require__(moduleId) { + + // Check if module is in cache + if(installedModules[moduleId]) { + return installedModules[moduleId].exports; + } + // Create a new module (and put it into the cache) + var module = installedModules[moduleId] = { + i: moduleId, + l: false, + exports: {} + }; + + // Execute the module function + modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + + // Flag the module as loaded + module.l = true; + + // Return the exports of the module + return module.exports; + } + + + // expose the modules object (__webpack_modules__) + __webpack_require__.m = modules; + + // expose the module cache + __webpack_require__.c = installedModules; + + // define getter function for harmony exports + __webpack_require__.d = function(exports, name, getter) { + if(!__webpack_require__.o(exports, name)) { + Object.defineProperty(exports, name, { enumerable: true, get: getter }); + } + }; + + // define __esModule on exports + __webpack_require__.r = function(exports) { + if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + } + Object.defineProperty(exports, '__esModule', { value: true }); + }; + + // create a fake namespace object + // mode & 1: value is a module id, require it + // mode & 2: merge all properties of value into the ns + // mode & 4: return value when already ns object + // mode & 8|1: behave like require + __webpack_require__.t = function(value, mode) { + if(mode & 1) value = __webpack_require__(value); + if(mode & 8) return value; + if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; + var ns = Object.create(null); + __webpack_require__.r(ns); + Object.defineProperty(ns, 'default', { enumerable: true, value: value }); + if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); + return ns; + }; + + // getDefaultExport function for compatibility with non-harmony modules + __webpack_require__.n = function(module) { + var getter = module && module.__esModule ? + function getDefault() { return module['default']; } : + function getModuleExports() { return module; }; + __webpack_require__.d(getter, 'a', getter); + return getter; + }; + + // Object.prototype.hasOwnProperty.call + __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; + + // __webpack_public_path__ + __webpack_require__.p = ""; + + + // Load entry module and return exports + return __webpack_require__(__webpack_require__.s = "./index.js"); +}) +({ + + "./index.js": + (function(module, exports, __webpack_require__) { + eval(`const b = __webpack_require__("./const.js"); +console.log(b); +`); + }), + + "./const.js": + (function(module, exports, __webpack_require__) { + eval(`module.exports = "hello"; +`); + }), + +}); \ No newline at end of file diff --git a/src/Compiler.rs b/src/Compiler.rs deleted file mode 100644 index 2ad9042..0000000 --- a/src/Compiler.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::{env, fs, path::PathBuf}; - -pub struct Config { - entry: String, -} - -impl Config { - pub fn new(entry: String) -> Config { - Config { entry } - } -} - -pub struct Compiler { - config: Config, - root: PathBuf, -} - -impl Compiler { - pub fn new(config: Config) -> Compiler { - Compiler { - config, - root: env::current_dir().expect("msg"), - } - } - - fn get_source(&self, module_path: PathBuf) { - let content = - fs::read_to_string(module_path).expect("Should have been able to read the file"); - println!("{}", content); - } - - fn build_module(&self, module_path: PathBuf, is_entry: bool) { - self.get_source(module_path); - } - - pub fn run(&self) { - let resolved_entry = self.root.join(&self.config.entry); - self.build_module(resolved_entry, true); - } -} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..d5821c2 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,20 @@ +pub struct Output { + pub path: String, + pub filename: String, +} + +pub struct Config { + pub root: String, + pub entry: String, + pub output: Output, +} + +impl Config { + pub fn new(root: String, entry: String, output: Output) -> Config { + Config { + root, + entry, + output, + } + } +} diff --git a/src/demo.rs b/src/demo.rs new file mode 100644 index 0000000..dbd7bfa --- /dev/null +++ b/src/demo.rs @@ -0,0 +1,98 @@ +use itertools::Itertools; +use oxc_allocator::Allocator; +use oxc_ast::ast::*; +use oxc_codegen::{CodeGenerator, CodegenOptions}; +use oxc_parser::Parser; +use oxc_semantic::SemanticBuilder; +use oxc_span::SourceType; +use oxc_traverse::{traverse_mut, Traverse, TraverseCtx}; +use std::ops::DerefMut; +use std::{env, path::Path, sync::Arc}; + +struct MyTransform; + +impl<'a> Traverse<'a> for MyTransform { + fn enter_call_expression(&mut self, node: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { + if node.is_require_call() { + match &mut node.callee { + Expression::Identifier(identifier_reference) => { + identifier_reference.name = Atom::from("__webpack_require__") + } + _ => {} + } + + let argument: &mut Argument<'a> = &mut node.arguments.deref_mut()[0]; + // node.callee + match argument { + Argument::StringLiteral(string_literal) => { + string_literal.value = Atom::from("aaaaaaaaaaaaaaa") + } + _ => {} + } + } + } +} + +pub fn use_oxc() -> std::io::Result<()> { + let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string()); + let path = Path::new(&name); + let source_text = Arc::new(std::fs::read_to_string(path)?); + let source_type = SourceType::from_path(path).unwrap(); + + // Memory arena where Semantic and Parser allocate objects + let allocator = Allocator::default(); + + // 1 Parse the source text into an AST + let parser_ret = Parser::new(&allocator, &source_text, source_type).parse(); + if !parser_ret.errors.is_empty() { + let error_message: String = parser_ret + .errors + .into_iter() + .map(|error| format!("{:?}", error.with_source_code(Arc::clone(&source_text)))) + .join("\n"); + println!("Parsing failed:\n\n{error_message}",); + return Ok(()); + } + + let mut program = parser_ret.program; + + println!("Parse result"); + println!("{}", serde_json::to_string_pretty(&program).unwrap()); + + // 2 Semantic Analyze + let semantic = SemanticBuilder::new(&source_text) + .build_module_record(path, &program) + // Enable additional syntax checks not performed by the parser + .with_check_syntax_error(true) + .build(&program); + + if !semantic.errors.is_empty() { + let error_message: String = semantic + .errors + .into_iter() + .map(|error| format!("{:?}", error.with_source_code(Arc::clone(&source_text)))) + .join("\n"); + println!("Semantic analysis failed:\n\n{error_message}",); + } + let (symbols, scopes) = semantic.semantic.into_symbol_table_and_scope_tree(); + + println!("Semantic analyze result"); + println!("{:?}", symbols); + println!("{:?}", scopes); + + // 3 Transform + let t = &mut MyTransform; + traverse_mut(t, &allocator, &mut program, symbols, scopes); + + // 4 Generate Code + let new_code = CodeGenerator::new() + .with_options(CodegenOptions { + ..CodegenOptions::default() + }) + .build(&program) + .code; + + println!("{}", new_code); + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index b808318..4d6911a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,99 +1,25 @@ -#![allow(clippy::print_stdout)] -use itertools::Itertools; -use oxc_allocator::Allocator; -use oxc_ast::ast::*; -use oxc_codegen::{CodeGenerator, CodegenOptions}; -use oxc_parser::Parser; -use oxc_semantic::SemanticBuilder; -use oxc_span::SourceType; -use oxc_traverse::{traverse_mut, Traverse, TraverseCtx}; -use std::ops::DerefMut; -use std::{env, path::Path, sync::Arc}; +use std::{env, path::Path}; -struct MyTransform; +use config::{Config, Output}; +use rs_compiler::Compiler; -impl<'a> Traverse<'a> for MyTransform { - fn enter_call_expression(&mut self, node: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { - if node.is_require_call() { - match &mut node.callee { - Expression::Identifier(identifier_reference) => { - identifier_reference.name = Atom::from("__webpack_require__") - } - _ => {} - } - - let argument: &mut Argument<'a> = &mut node.arguments.deref_mut()[0]; - // node.callee - match argument { - Argument::StringLiteral(string_literal) => { - string_literal.value = Atom::from("aaaaaaaaaaaaaaa") - } - _ => {} - } - } - } -} +mod config; +mod demo; +mod rs_compiler; +mod template; +mod transform; fn main() -> std::io::Result<()> { - let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string()); - let path = Path::new(&name); - let source_text = Arc::new(std::fs::read_to_string(path)?); - let source_type = SourceType::from_path(path).unwrap(); - - // Memory arena where Semantic and Parser allocate objects - let allocator = Allocator::default(); - - // 1 Parse the source text into an AST - let parser_ret = Parser::new(&allocator, &source_text, source_type).parse(); - if !parser_ret.errors.is_empty() { - let error_message: String = parser_ret - .errors - .into_iter() - .map(|error| format!("{:?}", error.with_source_code(Arc::clone(&source_text)))) - .join("\n"); - println!("Parsing failed:\n\n{error_message}",); - return Ok(()); - } - - let mut program = parser_ret.program; - - println!("Parse result"); - println!("{}", serde_json::to_string_pretty(&program).unwrap()); - - // 2 Semantic Analyze - let semantic = SemanticBuilder::new(&source_text) - .build_module_record(path, &program) - // Enable additional syntax checks not performed by the parser - .with_check_syntax_error(true) - .build(&program); - - if !semantic.errors.is_empty() { - let error_message: String = semantic - .errors - .into_iter() - .map(|error| format!("{:?}", error.with_source_code(Arc::clone(&source_text)))) - .join("\n"); - println!("Semantic analysis failed:\n\n{error_message}",); - } - let (symbols, scopes) = semantic.semantic.into_symbol_table_and_scope_tree(); - - println!("Semantic analyze result"); - println!("{:?}", symbols); - println!("{:?}", scopes); - - // 3 Transform - let t = &mut MyTransform; - traverse_mut(t, &allocator, &mut program, symbols, scopes); - - // 4 Generate Code - let new_code = CodeGenerator::new() - .with_options(CodegenOptions { - ..CodegenOptions::default() - }) - .build(&program) - .code; - - println!("{}", new_code); - + let cwd = env::current_dir().unwrap(); + let config = Config::new( + cwd.join("js_code").to_str().unwrap().to_owned(), + "index.js".to_owned(), + Output { + path: "./out".to_owned(), + filename: "bundle.js".to_owned(), + }, + ); + let compiler = &mut Compiler::new(config); + compiler.run(); Ok(()) } diff --git a/src/rs_compiler.rs b/src/rs_compiler.rs new file mode 100644 index 0000000..6da4ee9 --- /dev/null +++ b/src/rs_compiler.rs @@ -0,0 +1,158 @@ +use itertools::Itertools; +use oxc_allocator::Allocator; +use oxc_codegen::{CodeGenerator, CodegenOptions}; +use oxc_parser::Parser; +use oxc_semantic::SemanticBuilder; +use oxc_span::SourceType; +use oxc_traverse::traverse_mut; +use pathdiff::diff_paths; +use sailfish::TemplateSimple; +use std::{ + cell::RefCell, + collections::HashMap, + env, + fs::{self, File}, + io::Write, + path::{Path, PathBuf}, + rc::Rc, + sync::Arc, + vec, +}; + +use crate::{config::Config, template::OutputTpl, transform::RsWebpackTransform}; + +pub struct Compiler { + config: Config, + entry_id: String, + root: String, + modules: HashMap, + assets: HashMap, +} + +impl Compiler { + pub fn new(config: Config) -> Compiler { + Compiler { + root: config.root.clone(), + entry_id: "".to_string(), + config, + modules: HashMap::new(), + assets: HashMap::new(), + } + } + + fn get_source(&self, module_path: &PathBuf) -> String { + let content = + fs::read_to_string(module_path).expect("Should have been able to read the file"); + content + } + + fn parse( + &self, + module_path: PathBuf, + parent_path: &Path, + ) -> (String, Rc>>) { + println!("module_path {:?}", &module_path); + let source_text = Arc::new(fs::read_to_string(&module_path).unwrap()); + let source_type = SourceType::from_path(&module_path).unwrap(); + // Memory arena where Semantic and Parser allocate objects + let allocator = Allocator::default(); + + // 1 Parse the source text into an AST + let parser_ret = Parser::new(&allocator, &source_text, source_type).parse(); + if !parser_ret.errors.is_empty() { + let error_message: String = parser_ret + .errors + .into_iter() + .map(|error| format!("{:?}", error.with_source_code(Arc::clone(&source_text)))) + .join("\n"); + panic!("Parsing failed:\n\n{error_message}"); + } + + let mut program = parser_ret.program; + + // 2 Semantic Analyze + let semantic = SemanticBuilder::new(&source_text) + .build_module_record(&module_path, &program) + // Enable additional syntax checks not performed by the parser + .with_check_syntax_error(true) + .build(&program); + + if !semantic.errors.is_empty() { + let error_message: String = semantic + .errors + .into_iter() + .map(|error| format!("{:?}", error.with_source_code(Arc::clone(&source_text)))) + .join("\n"); + println!("Semantic analysis failed:\n\n{error_message}",); + } + let (symbols, scopes) = semantic.semantic.into_symbol_table_and_scope_tree(); + + // 3 Transform + let dependencies = Rc::new(RefCell::new(vec![])); + let rs_webpack_transform = &mut RsWebpackTransform { + parent_path: parent_path.to_str().unwrap().to_string(), + dependencies: dependencies.clone(), + }; + traverse_mut( + rs_webpack_transform, + &allocator, + &mut program, + symbols, + scopes, + ); + + // 4 Generate Code + let new_code = CodeGenerator::new() + .with_options(CodegenOptions { + ..CodegenOptions::default() + }) + .build(&program) + .code; + + println!("{}", new_code); + (new_code, dependencies) + } + + fn build_module(&mut self, module_path: PathBuf, is_entry: bool) { + let diff_result = diff_paths(&module_path, &self.root).unwrap(); + let module_id = format!("./{}", diff_result.to_str().unwrap()); + let parent_path = Path::new(&module_id).parent().unwrap(); + + println!("{:?}", &module_id); + + if is_entry { + self.entry_id = module_id.clone() + } + + let (source_code, dependencies) = self.parse(module_path, parent_path); + self.modules.insert(module_id, source_code); + + for dep in dependencies.borrow().iter() { + let module_path = Path::new(&self.root).join(dep); + self.build_module(module_path, false); + } + } + + fn emit_file(&mut self) { + let main = Path::new(&self.config.output.path).join(&self.config.output.filename); + let ctx = OutputTpl { + entry_id: self.entry_id.as_str(), + modules: &self.modules, + }; + let code = ctx.render_once().unwrap(); + println!("{:?}", &main); + // 创建目录及其所有父目录 + let parent_dir = Path::new(&main).parent().expect("Invalid file path"); + fs::create_dir_all(parent_dir).expect("create dir error"); + let mut file = File::create(&main).expect("create output error"); + file.write_all(code.as_bytes()).expect("write output error"); + // fs::write(&main, &code).expect("write output error"); + self.assets.insert(main.to_str().unwrap().to_owned(), code); + } + + pub fn run(&mut self) { + let resolved_entry = Path::new(&self.root).join(&self.config.entry); + self.build_module(resolved_entry, true); + self.emit_file(); + } +} diff --git a/src/template.rs b/src/template.rs new file mode 100644 index 0000000..c2ac9c7 --- /dev/null +++ b/src/template.rs @@ -0,0 +1,11 @@ +use std::collections::HashMap; + +use sailfish::TemplateSimple; + +#[derive(TemplateSimple)] +#[template(path = "output.stpl")] + +pub struct OutputTpl<'a> { + pub entry_id: &'a str, + pub modules: &'a HashMap, +} diff --git a/src/transform.rs b/src/transform.rs new file mode 100644 index 0000000..2a8c444 --- /dev/null +++ b/src/transform.rs @@ -0,0 +1,55 @@ +use oxc_ast::ast::*; +use oxc_traverse::{Traverse, TraverseCtx}; +use std::{cell::RefCell, ops::DerefMut, path::Path, rc::Rc}; + +pub struct RsWebpackTransform { + pub parent_path: String, + pub dependencies: Rc>>, +} + +impl<'a> Traverse<'a> for RsWebpackTransform { + fn enter_call_expression(&mut self, node: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { + if node.is_require_call() { + match &mut node.callee { + Expression::Identifier(identifier_reference) => { + identifier_reference.name = Atom::from("__webpack_require__") + } + _ => {} + } + + let argument = &mut node.arguments.deref_mut()[0]; + + match argument { + Argument::StringLiteral(string_literal) => { + let module_name = string_literal.value.as_str().to_owned(); + let module_name_with_ext = format!( + "{}{}", + module_name, + match Path::new(&module_name) + .extension() + .and_then(|ext| ext.to_str()) + { + Some(_) => "", + None => ".js", + } + ); + let resolved_module_name = if self.parent_path == "." { + Path::new(&module_name_with_ext) + } else { + &Path::new(&self.parent_path).join(&module_name_with_ext) + }; + println!( + "resolved_module_name {:?} {:?} {:?} {:?}", + self.parent_path, module_name, module_name_with_ext, resolved_module_name + ); + self.dependencies + .borrow_mut() + .push(resolved_module_name.to_str().unwrap().to_string()); + + string_literal.value = ctx.ast.atom(resolved_module_name.to_str().unwrap()); + } + _ => {} + } + } + } +} diff --git a/templates/output.stpl b/templates/output.stpl new file mode 100644 index 0000000..462196e --- /dev/null +++ b/templates/output.stpl @@ -0,0 +1,92 @@ +(function(modules) { + var installedModules = {}; + + // The require function + function __webpack_require__(moduleId) { + + // Check if module is in cache + if(installedModules[moduleId]) { + return installedModules[moduleId].exports; + } + // Create a new module (and put it into the cache) + var module = installedModules[moduleId] = { + i: moduleId, + l: false, + exports: {} + }; + + // Execute the module function + modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + + // Flag the module as loaded + module.l = true; + + // Return the exports of the module + return module.exports; + } + + + // expose the modules object (__webpack_modules__) + __webpack_require__.m = modules; + + // expose the module cache + __webpack_require__.c = installedModules; + + // define getter function for harmony exports + __webpack_require__.d = function(exports, name, getter) { + if(!__webpack_require__.o(exports, name)) { + Object.defineProperty(exports, name, { enumerable: true, get: getter }); + } + }; + + // define __esModule on exports + __webpack_require__.r = function(exports) { + if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + } + Object.defineProperty(exports, '__esModule', { value: true }); + }; + + // create a fake namespace object + // mode & 1: value is a module id, require it + // mode & 2: merge all properties of value into the ns + // mode & 4: return value when already ns object + // mode & 8|1: behave like require + __webpack_require__.t = function(value, mode) { + if(mode & 1) value = __webpack_require__(value); + if(mode & 8) return value; + if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; + var ns = Object.create(null); + __webpack_require__.r(ns); + Object.defineProperty(ns, 'default', { enumerable: true, value: value }); + if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); + return ns; + }; + + // getDefaultExport function for compatibility with non-harmony modules + __webpack_require__.n = function(module) { + var getter = module && module.__esModule ? + function getDefault() { return module['default']; } : + function getModuleExports() { return module; }; + __webpack_require__.d(getter, 'a', getter); + return getter; + }; + + // Object.prototype.hasOwnProperty.call + __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; + + // __webpack_public_path__ + __webpack_require__.p = ""; + + + // Load entry module and return exports + return __webpack_require__(__webpack_require__.s = "<%- entry_id %>"); +}) +({ + <% for (key, value) in modules { %> + "<%- key %>": + (function(module, exports, __webpack_require__) { + eval(`<%- value %>`); + }), + <%}%> +}); \ No newline at end of file diff --git a/test.js b/test.js deleted file mode 100644 index 620348c..0000000 --- a/test.js +++ /dev/null @@ -1 +0,0 @@ -const b = require('./b.js') From 8d5dc66f06e59ce7720d5be9daec4bad510ae528 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Mon, 28 Oct 2024 14:50:32 +0800 Subject: [PATCH 2/4] rm out --- .gitignore | 3 +- out/bundle.js | 100 -------------------------------------------------- 2 files changed, 2 insertions(+), 101 deletions(-) delete mode 100644 out/bundle.js diff --git a/.gitignore b/.gitignore index ccb5166..c539a3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -.vscode \ No newline at end of file +.vscode +out \ No newline at end of file diff --git a/out/bundle.js b/out/bundle.js deleted file mode 100644 index 8b77b66..0000000 --- a/out/bundle.js +++ /dev/null @@ -1,100 +0,0 @@ -(function(modules) { - var installedModules = {}; - - // The require function - function __webpack_require__(moduleId) { - - // Check if module is in cache - if(installedModules[moduleId]) { - return installedModules[moduleId].exports; - } - // Create a new module (and put it into the cache) - var module = installedModules[moduleId] = { - i: moduleId, - l: false, - exports: {} - }; - - // Execute the module function - modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - - // Flag the module as loaded - module.l = true; - - // Return the exports of the module - return module.exports; - } - - - // expose the modules object (__webpack_modules__) - __webpack_require__.m = modules; - - // expose the module cache - __webpack_require__.c = installedModules; - - // define getter function for harmony exports - __webpack_require__.d = function(exports, name, getter) { - if(!__webpack_require__.o(exports, name)) { - Object.defineProperty(exports, name, { enumerable: true, get: getter }); - } - }; - - // define __esModule on exports - __webpack_require__.r = function(exports) { - if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { - Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); - } - Object.defineProperty(exports, '__esModule', { value: true }); - }; - - // create a fake namespace object - // mode & 1: value is a module id, require it - // mode & 2: merge all properties of value into the ns - // mode & 4: return value when already ns object - // mode & 8|1: behave like require - __webpack_require__.t = function(value, mode) { - if(mode & 1) value = __webpack_require__(value); - if(mode & 8) return value; - if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; - var ns = Object.create(null); - __webpack_require__.r(ns); - Object.defineProperty(ns, 'default', { enumerable: true, value: value }); - if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); - return ns; - }; - - // getDefaultExport function for compatibility with non-harmony modules - __webpack_require__.n = function(module) { - var getter = module && module.__esModule ? - function getDefault() { return module['default']; } : - function getModuleExports() { return module; }; - __webpack_require__.d(getter, 'a', getter); - return getter; - }; - - // Object.prototype.hasOwnProperty.call - __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; - - // __webpack_public_path__ - __webpack_require__.p = ""; - - - // Load entry module and return exports - return __webpack_require__(__webpack_require__.s = "./index.js"); -}) -({ - - "./index.js": - (function(module, exports, __webpack_require__) { - eval(`const b = __webpack_require__("./const.js"); -console.log(b); -`); - }), - - "./const.js": - (function(module, exports, __webpack_require__) { - eval(`module.exports = "hello"; -`); - }), - -}); \ No newline at end of file From 7e16c3549289680a977cb1d643667d949da25c37 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Mon, 28 Oct 2024 14:51:58 +0800 Subject: [PATCH 3/4] rename compiler --- src/{rs_compiler.rs => compiler.rs} | 0 src/main.rs | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{rs_compiler.rs => compiler.rs} (100%) diff --git a/src/rs_compiler.rs b/src/compiler.rs similarity index 100% rename from src/rs_compiler.rs rename to src/compiler.rs diff --git a/src/main.rs b/src/main.rs index 4d6911a..300ae83 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ use std::{env, path::Path}; +use compiler::Compiler; use config::{Config, Output}; -use rs_compiler::Compiler; +mod compiler; mod config; mod demo; -mod rs_compiler; mod template; mod transform; From ae1868d7d5285b2bf430dfd55c2125845a650810 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Mon, 28 Oct 2024 14:55:48 +0800 Subject: [PATCH 4/4] resolve some warns --- src/compiler.rs | 19 ++++--------------- src/main.rs | 4 ++-- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 6da4ee9..59632a2 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -10,8 +10,7 @@ use sailfish::TemplateSimple; use std::{ cell::RefCell, collections::HashMap, - env, - fs::{self, File}, + fs::{create_dir_all, read_to_string, File}, io::Write, path::{Path, PathBuf}, rc::Rc, @@ -40,19 +39,12 @@ impl Compiler { } } - fn get_source(&self, module_path: &PathBuf) -> String { - let content = - fs::read_to_string(module_path).expect("Should have been able to read the file"); - content - } - fn parse( &self, module_path: PathBuf, parent_path: &Path, ) -> (String, Rc>>) { - println!("module_path {:?}", &module_path); - let source_text = Arc::new(fs::read_to_string(&module_path).unwrap()); + let source_text = Arc::new(read_to_string(&module_path).unwrap()); let source_type = SourceType::from_path(&module_path).unwrap(); // Memory arena where Semantic and Parser allocate objects let allocator = Allocator::default(); @@ -140,14 +132,11 @@ impl Compiler { modules: &self.modules, }; let code = ctx.render_once().unwrap(); - println!("{:?}", &main); - // 创建目录及其所有父目录 + let parent_dir = Path::new(&main).parent().expect("Invalid file path"); - fs::create_dir_all(parent_dir).expect("create dir error"); + create_dir_all(parent_dir).expect("create dir error"); let mut file = File::create(&main).expect("create output error"); file.write_all(code.as_bytes()).expect("write output error"); - // fs::write(&main, &code).expect("write output error"); - self.assets.insert(main.to_str().unwrap().to_owned(), code); } pub fn run(&mut self) { diff --git a/src/main.rs b/src/main.rs index 300ae83..3091b9b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ -use std::{env, path::Path}; +use std::env; use compiler::Compiler; use config::{Config, Output}; mod compiler; mod config; -mod demo; +// mod demo; mod template; mod transform;