Skip to content

Commit

Permalink
feat: produce source maps
Browse files Browse the repository at this point in the history
  • Loading branch information
phoenix-ru committed May 9, 2024
1 parent 007f8a8 commit 07e1e26
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 30 deletions.
28 changes: 22 additions & 6 deletions crates/fervid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! </p>
//!
//! ```
//! use swc_core::ecma::ast::Expr;
//! use swc_core::{common::FileName, ecma::ast::Expr};
//!
//! let input = r#"
//! <template><div>hello world</div></template>
Expand Down Expand Up @@ -43,7 +43,7 @@
//! );
//!
//! // (Optional) Stringify the code
//! let compiled_code = fervid_codegen::CodegenContext::stringify(input, &sfc_module, false);
//! let compiled_code = fervid_codegen::CodegenContext::stringify(input, &sfc_module, FileName::Custom("input.vue".into()), false, false);
//! ```
extern crate lazy_static;
Expand All @@ -57,12 +57,12 @@ use fervid_codegen::CodegenContext;
pub use fervid_core::*;
use fervid_parser::SfcParser;
use fervid_transform::{style::should_transform_style_block, transform_sfc, TransformSfcOptions};
use fxhash::FxHasher32;
use std::{
borrow::Cow,
hash::{Hash, Hasher},
};
use swc_core::ecma::ast::Expr;
use fxhash::FxHasher32;
use swc_core::{common::FileName, ecma::ast::Expr};

// TODO Add severity to errors
// TODO Better structs
Expand All @@ -89,6 +89,12 @@ pub struct CompileOptions<'o> {
// Configure what tags/attributes to transform into asset url imports,
// or disable the transform altogether with `false`.
// transformAssetUrls?: AssetURLOptions | AssetURLTagConfig | boolean;

// script
pub gen_default_as: Option<Cow<'o, str>>,

// fervid-specific
pub source_map: Option<bool>,
}

pub struct CompileResult {
Expand All @@ -97,6 +103,7 @@ pub struct CompileResult {
pub errors: Vec<CompileError>,
pub styles: Vec<CompileEmittedStyle>,
pub other_assets: Vec<CompileEmittedAsset>,
pub source_map: Option<String>,
}

pub struct CompileEmittedStyle {
Expand Down Expand Up @@ -160,7 +167,14 @@ pub fn compile(source: &str, options: CompileOptions) -> Result<CompileResult, C
transform_result.setup_fn,
);

let code = CodegenContext::stringify(&source, &sfc_module, false);
// Convert AST to string
let (code, source_map) = CodegenContext::stringify(
&source,
&sfc_module,
FileName::Custom(options.filename.to_string()),
options.source_map.unwrap_or(false),
false,
);

let styles = transform_result
.style_blocks
Expand Down Expand Up @@ -192,6 +206,7 @@ pub fn compile(source: &str, options: CompileOptions) -> Result<CompileResult, C
errors: all_errors,
styles,
other_assets,
source_map,
})
}

Expand Down Expand Up @@ -244,7 +259,8 @@ pub fn compile_sync_naive(source: &str, is_prod: bool) -> Result<String, String>
transform_result.setup_fn,
);

let compiled_code = CodegenContext::stringify(&source, &sfc_module, false);
let (compiled_code, _map) =
CodegenContext::stringify(&source, &sfc_module, FileName::Anon, false, false);

Ok(compiled_code)
}
2 changes: 1 addition & 1 deletion crates/fervid_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ license = "Apache-2.0"
fervid_core = { path="../fervid_core", version = "0.1" }
fervid_transform = { path="../fervid_transform", version="0.1" }
lazy_static = { workspace = true }
swc_core = { workspace = true , features = ["ecma_ast"] }
swc_core = { workspace = true, features = ["ecma_ast", "ecma_visit", "common_sourcemap"] }
swc_ecma_codegen = { workspace = true }
fxhash = { workspace = true }
smallvec = { workspace = true }
Expand Down
131 changes: 110 additions & 21 deletions crates/fervid_codegen/src/control_flow/sfc.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use fervid_core::{FervidAtom, SfcTemplateBlock, TemplateGenerationMode};
use swc_core::{
common::{FileName, SourceMap, DUMMY_SP},
ecma::ast::{
ArrowExpr, BindingIdent, BlockStmt, BlockStmtOrExpr, Decl, ExportDefaultExpr, Expr,
Function, Ident, ImportDecl, MethodProp, Module, ModuleDecl, ModuleItem, ObjectLit, Param,
Pat, Prop, PropName, PropOrSpread, ReturnStmt, Stmt, Str, VarDecl, VarDeclKind,
},
atoms::Atom, common::{
collections::AHashMap, source_map::SourceMapGenConfig, BytePos, FileName, SourceMap,
DUMMY_SP,
}, ecma::{
ast::{
ArrowExpr, BindingIdent, BlockStmt, BlockStmtOrExpr, Decl, ExportDefaultExpr, Expr,
Function, Ident, ImportDecl, MethodProp, Module, ModuleDecl, ModuleItem, ObjectLit,
Param, Pat, Prop, PropName, PropOrSpread, ReturnStmt, Stmt, Str, VarDecl, VarDeclKind,
},
visit::{noop_visit_type, Visit, VisitWith},
}
};
use swc_ecma_codegen::{text_writer::JsWriter, Emitter, Node};

Expand Down Expand Up @@ -318,25 +323,109 @@ impl CodegenContext {
}
}

pub fn stringify(source: &str, item: &impl Node, minify: bool) -> String {
pub fn stringify<T>(
source: &str,
module: &T,
filename: FileName,
generate_source_map: bool,
minify: bool,
) -> (String, Option<String>)
where
T: Node + VisitWith<IdentCollector>,
{
// Emitting the result requires some setup with SWC
let cm: swc_core::common::sync::Lrc<SourceMap> = Default::default();
cm.new_source_file(FileName::Custom("test.ts".to_owned()), source.to_owned());
let mut buff: Vec<u8> = Vec::new();
let writer: JsWriter<&mut Vec<u8>> = JsWriter::new(cm.clone(), "\n", &mut buff, None);

let mut emitter_cfg = swc_ecma_codegen::Config::default();
emitter_cfg.minify = minify;

let mut emitter = Emitter {
cfg: emitter_cfg,
comments: None,
wr: writer,
cm,
cm.new_source_file(filename.to_owned(), source.to_owned());

let mut source_map_buf = vec![];

let generated = {
let mut buff: Vec<u8> = Vec::new();
let src_map = if generate_source_map {
Some(&mut source_map_buf)
} else {
None
};
let writer: JsWriter<&mut Vec<u8>> =
JsWriter::new(cm.clone(), "\n", &mut buff, src_map);

let mut emitter_cfg = swc_ecma_codegen::Config::default();
emitter_cfg.minify = minify;

let mut emitter = Emitter {
cfg: emitter_cfg,
comments: None,
wr: writer,
cm: cm.clone(),
};

module.emit_with(&mut emitter).expect("Failed to emit");
String::from_utf8(buff).expect("Invalid UTF-8")
};

let _ = item.emit_with(&mut emitter);
let map = if generate_source_map {
let source_map_names = {
let mut v = IdentCollector {
names: Default::default(),
};

module.visit_with(&mut v);

v.names
};

let map = cm.build_source_map_with_config(
&source_map_buf,
None,
SourceMapConfig {
source_file_name: Some(filename.to_string().as_str()),
names: &source_map_names,
},
);
let mut buf = vec![];

map.to_writer(&mut buf).expect("Failed to write source map");
Some(String::from_utf8(buf).expect("Invalid UTF-8 in source map"))
} else {
None
};

(generated, map)
}
}

struct SourceMapConfig<'a> {
source_file_name: Option<&'a str>,
names: &'a AHashMap<BytePos, FervidAtom>,
}

impl SourceMapGenConfig for SourceMapConfig<'_> {
fn file_name_to_source(&self, f: &FileName) -> String {
if let Some(file_name) = self.source_file_name {
return file_name.to_string();
}

f.to_string()
}

fn inline_sources_content(&self, _f: &FileName) -> bool {
true
}

fn name_for_bytepos(&self, pos: BytePos) -> Option<&str> {
self.names.get(&pos).map(|v| &**v)
}
}

// Adapted from `swc_compiler_base`
pub struct IdentCollector {
pub names: AHashMap<BytePos, Atom>,
}

impl Visit for IdentCollector {
noop_visit_type!();

String::from_utf8(buff).unwrap()
fn visit_ident(&mut self, ident: &Ident) {
self.names.insert(ident.span.lo, ident.sym.clone());
}
}
2 changes: 2 additions & 0 deletions crates/fervid_farmfe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ impl Plugin for FarmPluginVueFervid {
id: param.module_id.clone().into(),
is_prod: Some(true),
ssr: None,
gen_default_as: None,
source_map: None
},
);

Expand Down
5 changes: 5 additions & 0 deletions crates/fervid_napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export interface FervidJsCompilerOptionsScript {
* Default: true
*/
hoistStatic?: boolean
/** Produce source maps */
sourceMap?: boolean
}
export interface FervidJsCompilerOptionsStyle {
/** Ignored */
Expand All @@ -53,12 +55,15 @@ export interface FervidCompileOptions {
id: string
/** Filename is used for automatic component name inference and self-referential imports */
filename: string
/** Generate a const instead of default export */
genDefaultAs?: string
}
export interface CompileResult {
code: string
styles: Array<Style>
errors: Array<SerializedError>
customBlocks: Array<CustomBlock>
sourceMap?: string
}
export interface Style {
code: string
Expand Down
5 changes: 4 additions & 1 deletion crates/fervid_napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,17 @@ impl FervidJsCompiler {
filename: Cow::Borrowed(&options.filename),
id: Cow::Borrowed(&options.id),
is_prod: self.options.is_production,
ssr: self.options.ssr
ssr: self.options.ssr,
gen_default_as: options.gen_default_as.as_ref().map(|v| Cow::Borrowed(v.as_str())),
source_map: self.options.source_map
};

let native_compile_result =
compile(source, compile_options).map_err(|e| Error::from_reason(e.to_string()))?;

Ok(CompileResult {
code: native_compile_result.code,
source_map: native_compile_result.source_map,
custom_blocks: native_compile_result
.other_assets
.into_iter()
Expand Down
6 changes: 5 additions & 1 deletion crates/fervid_napi/src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ pub struct FervidJsCompilerOptionsScript {
/// - Only enabled when one `<script setup>` exists.
/// Default: true
pub hoist_static: Option<bool>,
/// Produce source maps
pub source_map: Option<bool>
}

#[napi(object)]
Expand All @@ -73,9 +75,10 @@ pub struct FervidJsCompilerOptionsStyle {
pub struct FervidCompileOptions {
/// Scope ID for prefixing injected CSS variables
pub id: String,

/// Filename is used for automatic component name inference and self-referential imports
pub filename: String,
/// Generate a const instead of default export
pub gen_default_as: Option<String>,
}

#[napi(object)]
Expand All @@ -84,6 +87,7 @@ pub struct CompileResult {
pub styles: Vec<Style>,
pub errors: Vec<SerializedError>,
pub custom_blocks: Vec<CustomBlock>,
pub source_map: Option<String>
}

#[napi(object)]
Expand Down

0 comments on commit 07e1e26

Please sign in to comment.