Skip to content

Commit

Permalink
feat: support sourcemap (#77)
Browse files Browse the repository at this point in the history
* feat: support sourcemap

* chore: fix spell check and add changeset

* chore: support generating sourcemap for only mutable resources
  • Loading branch information
wre232114 authored Mar 14, 2023
1 parent a2b260d commit e76619a
Show file tree
Hide file tree
Showing 28 changed files with 425 additions and 50 deletions.
24 changes: 13 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion crates/compiler/src/update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,10 @@ impl Compiler {
let resources =
render_and_generate_update_resource(&updated_module_ids, &diff_result, &self.context)?;

let boundaries = find_hmr_boundaries::find_hmr_boundaries(&updated_module_ids, &self.context);
// find the boundaries
let boundaries = find_hmr_boundaries::find_hmr_boundaries(&updated_module_ids, &self.context);

// TODO: support sourcemap for hmr. and should generate the hmr update response body in rust side.
Ok(UpdateResult {
added_module_ids: diff_result.added_modules.into_iter().collect(),
updated_module_ids,
Expand Down
12 changes: 12 additions & 0 deletions crates/compiler/src/update/regenerate_resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub fn render_and_generate_update_resource(
ResourcePotId::new(String::from("__UPDATE_RESOURCE_POT__")),
ResourcePotType::Js,
);
update_resource_pot.immutable = true;

for added in &diff_result.added_modules {
update_resource_pot.add_module(added.clone());
Expand Down Expand Up @@ -70,6 +71,17 @@ pub fn render_and_generate_update_resource(
.find(|r| matches!(r.resource_type, ResourceType::Js))
.unwrap();

if context.config.sourcemap.is_all() {
// find sourceMappingUrl= and remove it
let str = String::from_utf8(js_resource.bytes).unwrap();
let mut lines = str.lines();
// remove the last line
lines.next_back();
let new_str = lines.collect::<Vec<_>>().join("\n");
return Ok(new_str);
}

// TODO: also return sourcemap
Ok(String::from_utf8(js_resource.bytes).unwrap())
}

Expand Down
2 changes: 1 addition & 1 deletion crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ downcast-rs = "1.2"
glob = "0.3"
swc_ecma_ast = { version = "0.96.5", features = ["rkyv-impl"] }
swc_ecma_parser = { version = "0.124.9" }
swc_common = { version = "0.29.31", features = ["concurrent"] }
swc_common = { version = "0.29.31", features = ["concurrent", "sourcemap"] }
swc_css_ast = { version = "0.134.4", features = ["rkyv-impl"] }
swc_html_ast = { version = "0.28.23", features = ["rkyv-impl"] }
56 changes: 54 additions & 2 deletions crates/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct Config {
pub runtime: RuntimeConfig,
pub script: ScriptConfig,
pub assets: AssetsConfig,
pub sourcemap: SourcemapConfig,
pub partial_bundling: PartialBundlingConfig,
pub lazy_compilation: bool,
}
Expand All @@ -40,6 +41,7 @@ impl Default for Config {
runtime: Default::default(),
script: Default::default(),
assets: Default::default(),
sourcemap: Default::default(),
partial_bundling: PartialBundlingConfig::default(),
lazy_compilation: true,
}
Expand All @@ -58,8 +60,10 @@ pub struct OutputConfig {
impl Default for OutputConfig {
fn default() -> Self {
Self {
filename: "[resourceName].[contentHash].[ext]".to_string(),
assets_filename: "[resourceName].[contentHash].[ext]".to_string(),
/// [resourceName].[contentHash].[ext]
filename: "[resourceName].[ext]".to_string(),
/// [resourceName].[contentHash].[ext]
assets_filename: "[resourceName].[ext]".to_string(),
public_path: "/".to_string(),
path: "dist".to_string(),
}
Expand Down Expand Up @@ -176,3 +180,51 @@ pub struct PartialBundlingConfig {
pub struct AssetsConfig {
pub include: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SourcemapConfig {
Bool(bool),
/// Generate inline sourcemap instead of a separate file for mutable resources.
#[serde(rename = "inline")]
Inline,
/// Generate sourcemap for all resources.
/// By default, sourcemap is generated only for resources that are mutable.
#[serde(rename = "all")]
All,
#[serde(rename = "all-inline")]
AllInline,
}

impl Default for SourcemapConfig {
fn default() -> Self {
Self::Bool(true)
}
}

impl SourcemapConfig {
pub fn enabled(&self) -> bool {
match self {
Self::Bool(b) => *b,
_ => true,
}
}
pub fn is_inline(&self) -> bool {
match self {
Self::Bool(b) if *b => true,
Self::Inline => true,
Self::All => false,
Self::AllInline => true,
_ => false,
}
}

pub fn is_all(&self) -> bool {
match self {
Self::Bool(_) => false,
Self::Inline => false,
Self::All => true,
Self::AllInline => true,
}
}
}
4 changes: 2 additions & 2 deletions crates/core/src/resource/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl ResourceType {
ResourceType::Js => "js".to_string(),
ResourceType::Css => "css".to_string(),
ResourceType::Html => "html".to_string(),
ResourceType::SourceMap => "map".to_string(),
ResourceType::SourceMap => "js.map".to_string(),
}
}

Expand All @@ -40,7 +40,7 @@ impl ResourceType {
ResourceType::Js => "script".to_string(),
ResourceType::Css => "link".to_string(),
ResourceType::Html => "html".to_string(),
ResourceType::SourceMap => "map".to_string(),
ResourceType::SourceMap => unreachable!(),
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion crates/plugin_html/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ impl Plugin for FarmPluginHtml {
mode: context.config.mode.clone(),
public_path: context.config.output.public_path.clone(),
define: context.config.define.clone(),
}
},
);

let resource_pot = resource_pot_map
Expand Down Expand Up @@ -328,6 +328,12 @@ pub fn get_dynamic_resources_map(

for r in rp.resources() {
let resource = resources_map.get(r).unwrap();

// Currently only support js and css
if !matches!(resource.resource_type, ResourceType::Js | ResourceType::Css) {
continue;
}

resources.push((resource.name.clone(), resource.resource_type.clone()));
}
} else {
Expand All @@ -337,6 +343,12 @@ pub fn get_dynamic_resources_map(
let resource = resources_map
.get(r)
.unwrap_or_else(|| panic!("{} not found", r));

// Currently only support js and css
if !matches!(resource.resource_type, ResourceType::Js | ResourceType::Css) {
continue;
}

resources.push((resource.name.clone(), resource.resource_type.clone()));
}

Expand Down
2 changes: 1 addition & 1 deletion crates/plugin_html/src/resources_injector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl ResourcesInjector {
}
_ => {
panic!(
"unknown supported type ({:?}) when injecting dynamic resources",
"unsupported type ({:?}) when injecting dynamic resources",
resource_type
)
}
Expand Down
8 changes: 5 additions & 3 deletions crates/plugin_runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub mod render_resource_pot;
/// All runtime module (including the runtime core and its plugins) will be suffixed as `.farm-runtime` to distinguish with normal script modules.
/// ```
pub struct FarmPluginRuntime {
// TODO move the runtime ast to context.meta.script
runtime_ast: Mutex<Option<SwcModule>>,
}

Expand Down Expand Up @@ -372,6 +373,7 @@ impl Plugin for FarmPluginRuntime {
runtime_ast,
context.config.script.target.clone(),
context.meta.script.cm.clone(),
None,
)
.map_err(|e| CompilationError::GenerateResourcesError {
name: resource_pot.id.to_string(),
Expand Down Expand Up @@ -412,10 +414,10 @@ impl Plugin for FarmPluginRuntime {
let call_entry = parse_module(
"farm-internal-call-entry-module",
&format!(
r#"const {} = globalThis || window || global || self;
const farmModuleSystem = {}.{};
r#"var {} = globalThis || window || global || self;
var farmModuleSystem = {}.{};
farmModuleSystem.bootstrap();
const entry = farmModuleSystem.require("{}").default;
var entry = farmModuleSystem.require("{}").default;
export default entry;"#,
FARM_GLOBAL_THIS,
FARM_GLOBAL_THIS,
Expand Down
44 changes: 39 additions & 5 deletions crates/plugin_script/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use farmfe_core::{
plugin::{
Plugin, PluginAnalyzeDepsHookParam, PluginFinalizeModuleHookParam, PluginHookContext,
PluginLoadHookParam, PluginLoadHookResult, PluginParseHookParam, PluginProcessModuleHookParam,
ResolveKind,
},
resource::{
resource_pot::{ResourcePot, ResourcePotType},
Expand All @@ -23,11 +22,12 @@ use farmfe_core::{
},
};
use farmfe_toolkit::{
fs::read_file_utf8,
fs::{read_file_utf8, transform_output_filename},
script::{
codegen_module, module_system_from_deps, module_type_from_id, parse_module,
swc_try_with::try_with, syntax_from_module_type,
},
sourcemap::swc_gen::build_source_map,
swc_ecma_transforms::{
resolver,
typescript::{strip, strip_with_jsx},
Expand Down Expand Up @@ -251,25 +251,59 @@ impl Plugin for FarmPluginScript {
) -> Result<Option<Vec<Resource>>> {
if matches!(resource_pot.resource_pot_type, ResourcePotType::Js) {
let ast = &resource_pot.meta.as_js().ast;
let buf = codegen_module(
let mut src_map_buf = vec![];

let mut buf = codegen_module(
ast,
context.config.script.target.clone(),
context.meta.script.cm.clone(),
Some(&mut src_map_buf),
)
.map_err(|e| CompilationError::GenerateResourcesError {
name: resource_pot.id.to_string(),
ty: resource_pot.resource_pot_type.clone(),
source: Some(Box::new(e)),
})?;
let sourcemap_filename = transform_output_filename(
context.config.output.filename.clone(),
&resource_pot.id.to_string(),
&buf,
&ResourceType::SourceMap.to_ext(),
);

if context.config.sourcemap.enabled()
&& (context.config.sourcemap.is_all() || !resource_pot.immutable)
{
// TODO: support inline sourcemap
let source_mapping_url = format!("\n//# sourceMappingURL={}", sourcemap_filename);
buf.append(&mut source_mapping_url.as_bytes().to_vec());
}

Ok(Some(vec![Resource {
let mut resources = vec![Resource {
bytes: buf,
name: resource_pot.id.to_string(),
emitted: false,
resource_type: ResourceType::Js,
resource_pot: resource_pot.id.clone(),
preserve_name: false,
}]))
}];

if context.config.sourcemap.enabled()
&& (context.config.sourcemap.is_all() || !resource_pot.immutable)
{
let src_map = build_source_map(&src_map_buf, context.meta.script.cm.clone(), ast);

resources.push(Resource {
bytes: src_map,
name: sourcemap_filename,
emitted: false,
resource_type: ResourceType::SourceMap,
resource_pot: resource_pot.id.clone(),
preserve_name: true,
});
}

Ok(Some(resources))
} else {
Ok(None)
}
Expand Down
12 changes: 10 additions & 2 deletions crates/plugin_script/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@ fn load_parse_and_analyze_deps() {
.generate_resources(&mut resource_pot, &context, &hook_context)
.unwrap()
.unwrap();
assert_eq!(resources.len(), 1);
assert_eq!(resources.len(), 2);

let code = String::from_utf8(resources[0].bytes.clone()).unwrap();

let lines: Vec<&str> = code.lines().collect();
assert_eq!(
lines,
Expand All @@ -145,9 +146,16 @@ fn load_parse_and_analyze_deps() {
"import b from \"./b\";",
"export * from \"./c\";",
"export { d } from \"./d\";",
"console.log(a, b);"
"console.log(a, b);",
"",
"//# sourceMappingURL=index.js.map"
]
);

// assert_eq!(
// &resources[1].bytes,
// "{\"version\":3,\"sources\":[\"any\"],\"sourcesContent\":[\"import a from './a';\\nimport b from './b';\\n\\nexport * from './c';\\nexport { d } from './d';\\n\\nconsole.log(a, b);\\n\"],\"names\":[\"a\",\"b\",\"d\",\"console\",\"log\"],\"mappings\":\"AAAA,OAAOA,OAAO,MAAM;AACpB,OAAOC,OAAO,MAAM;AAEpB,cAAc,MAAM;AACpB,SAASC,CAAC,QAAQ,MAAM;AAExBC,QAAQC,GAAG,CAACJ,GAAGC\"}".as_bytes()
// )
},
);
}
Loading

0 comments on commit e76619a

Please sign in to comment.