Skip to content

Commit

Permalink
feat: define plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
h-a-n-a committed May 31, 2024
1 parent 06990bb commit 79d188c
Show file tree
Hide file tree
Showing 313 changed files with 3,493 additions and 1,577 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

47 changes: 41 additions & 6 deletions crates/rspack_core/src/dependency/runtime_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub fn export_from_import(
id: &DependencyId,
is_call: bool,
call_context: bool,
asi_safe: Option<bool>,
) -> String {
let TemplateContext {
runtime_requirements,
Expand Down Expand Up @@ -132,10 +133,20 @@ pub fn export_from_import(
if is_call {
return format!("{import_var}_default(){}", property_access(export_name, 1));
} else {
return format!(
"({import_var}_default(){})",
property_access(export_name, 1)
);
return if let Some(asi_safe) = asi_safe {
match asi_safe {
true => format!(
"({import_var}_default(){})",
property_access(export_name, 1)
),
false => format!(
";({import_var}_default(){})",
property_access(export_name, 1)
),
}
} else {
format!("{import_var}_default.a{}", property_access(export_name, 1))
};
}
}
ExportsType::DefaultOnly | ExportsType::DefaultWithNamed => {
Expand Down Expand Up @@ -172,7 +183,23 @@ pub fn export_from_import(
)
.boxed(),
);
return format!("/*#__PURE__*/ ({import_var}_namespace_cache || ({import_var}_namespace_cache = {}({import_var}{})))", RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT, if matches!(exports_type, ExportsType::DefaultOnly) { "" } else { ", 2" });
let prefix = if let Some(asi_safe) = asi_safe {
match asi_safe {
true => "",
false => ";",
}
} else {
"Object"
};
return format!(
"/*#__PURE__*/ {prefix}({import_var}_namespace_cache || ({import_var}_namespace_cache = {}({import_var}{})))",
RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT,
if matches!(exports_type, ExportsType::DefaultOnly) {
""
} else {
", 2"
}
);
}
}

Expand Down Expand Up @@ -206,8 +233,16 @@ pub fn export_from_import(
"".to_string()
};
let property = property_access(&*used_name, 0);
let access = format!("{import_var}{comment}{property}");
if is_call && !call_context {
format!("(0, {import_var}{comment}{property})")
if let Some(asi_safe) = asi_safe {
match asi_safe {
true => format!("(0,{access})"),
false => format!(";(0,{access})"),
}
} else {
format!("Object({access})")
}
} else {
format!("{import_var}{comment}{property}")
}
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_loader_swc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ plugin = [
[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
base64 = { version = "0.22" }
dashmap = { workspace = true }
either = "1"
jsonc-parser = { version = "0.23.0", features = ["serde"] }
Expand All @@ -34,4 +35,5 @@ serde_json = { workspace = true }
swc_config = { workspace = true }
swc_core = { workspace = true, features = ["base", "ecma_ast", "common"] }
tokio = { workspace = true }
url = "2.5.0"
xxhash-rust = { workspace = true, features = ["xxh32"] }
164 changes: 161 additions & 3 deletions crates/rspack_loader_swc/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@
* Copyright (c)
*/
use std::env;
use std::fs::File;
use std::path::Path;
use std::{path::PathBuf, sync::Arc};

use anyhow::{anyhow, Context, Error};
use anyhow::{anyhow, bail, Context, Error};
use base64::prelude::*;
use dashmap::DashMap;
use jsonc_parser::parse_to_serde_value;
use rspack_ast::javascript::{Ast as JsAst, Context as JsAstContext, Program as JsProgram};
use serde_json::error::Category;
use swc_config::config_types::BoolOr;
use swc_config::merge::Merge;
use swc_core::base::config::{
BuiltInput, Config, ConfigFile, IsModule, JsMinifyCommentOption, Rc, RootMode,
BuiltInput, Config, ConfigFile, InputSourceMap, IsModule, JsMinifyCommentOption, Rc, RootMode,
};
use swc_core::base::SwcComments;
use swc_core::base::{sourcemap, SwcComments};
use swc_core::common::comments::{Comment, CommentKind, Comments};
use swc_core::common::errors::{Handler, HANDLER};
use swc_core::common::sync::Lazy;
Expand All @@ -37,6 +39,7 @@ use swc_core::{
base::{config::Options, try_with_handler},
common::Globals,
};
use url::Url;

fn minify_file_comments(
comments: &SingleThreadedComments,
Expand Down Expand Up @@ -396,6 +399,161 @@ impl SwcCompiler {
program
}

pub fn input_source_map(
&self,
input_src_map: &InputSourceMap,
) -> Result<Option<sourcemap::SourceMap>, Error> {
let fm = &self.fm;
let name = &self.fm.name;

let read_inline_sourcemap =
|data_url: Option<&str>| -> Result<Option<sourcemap::SourceMap>, Error> {
match data_url {
Some(data_url) => {
let url = Url::parse(data_url)
.with_context(|| format!("failed to parse inline source map url\n{}", data_url))?;

let idx = match url.path().find("base64,") {
Some(v) => v,
None => {
bail!("failed to parse inline source map: not base64: {:?}", url)
}
};

let content = url.path()[idx + "base64,".len()..].trim();

let res = BASE64_STANDARD
.decode(content.as_bytes())
.context("failed to decode base64-encoded source map")?;

Ok(Some(sourcemap::SourceMap::from_slice(&res).context(
"failed to read input source map from inlined base64 encoded \
string",
)?))
}
None => {
bail!("failed to parse inline source map: `sourceMappingURL` not found")
}
}
};

let read_file_sourcemap =
|data_url: Option<&str>| -> Result<Option<sourcemap::SourceMap>, Error> {
match &name {
FileName::Real(filename) => {
let dir = match filename.parent() {
Some(v) => v,
None => {
bail!("unexpected: root directory is given as a input file")
}
};

let map_path = match data_url {
Some(data_url) => {
let mut map_path = dir.join(data_url);
if !map_path.exists() {
// Old behavior. This check would prevent
// regressions.
// Perhaps it shouldn't be supported. Sometimes
// developers don't want to expose their source
// code.
// Map files are for internal troubleshooting
// convenience.
map_path = PathBuf::from(format!("{}.map", filename.display()));
if !map_path.exists() {
bail!(
"failed to find input source map file {:?} in \
{:?} file",
map_path.display(),
filename.display()
)
}
}

Some(map_path)
}
None => {
// Old behavior.
let map_path = PathBuf::from(format!("{}.map", filename.display()));
if map_path.exists() {
Some(map_path)
} else {
None
}
}
};

match map_path {
Some(map_path) => {
let path = map_path.display().to_string();
let file = File::open(&path);

// Old behavior.
let file = file?;

Ok(Some(sourcemap::SourceMap::from_reader(file).with_context(
|| {
format!(
"failed to read input source map
from file at {}",
path
)
},
)?))
}
None => Ok(None),
}
}
_ => Ok(None),
}
};

let read_sourcemap = || -> Option<sourcemap::SourceMap> {
let s = "sourceMappingURL=";
let idx = fm.src.rfind(s);

let data_url = idx.map(|idx| {
let data_idx = idx + s.len();
if let Some(end) = fm.src[data_idx..].find('\n').map(|i| i + data_idx + 1) {
&fm.src[data_idx..end]
} else {
&fm.src[data_idx..]
}
});

match read_inline_sourcemap(data_url) {
Ok(r) => r,
Err(_err) => {
// Load original source map if possible
match read_file_sourcemap(data_url) {
Ok(v) => v,
Err(_) => {
// tracing::error!("failed to read input source map: {:?}", err);
None
}
}
}
}
};

// Load original source map
match input_src_map {
InputSourceMap::Bool(false) => Ok(None),
InputSourceMap::Bool(true) => Ok(read_sourcemap()),
InputSourceMap::Str(ref s) => {
if s == "inline" {
Ok(read_sourcemap())
} else {
// Load source map passed by user
Ok(Some(
sourcemap::SourceMap::from_slice(s.as_bytes())
.context("failed to read input source map from user-provided sourcemap")?,
))
}
}
}
}

pub fn comments(&self) -> &SingleThreadedComments {
&self.comments
}
Expand Down
57 changes: 26 additions & 31 deletions crates/rspack_loader_swc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ use std::default::Default;
use compiler::{IntoJsAst, SwcCompiler};
use options::SwcCompilerOptionsWithAdditional;
pub use options::SwcLoaderJsOptions;
use rspack_ast::RspackAst;
use rspack_core::{
rspack_sources::SourceMap, LoaderRunnerContext, LoadersShouldAlwaysGiveContent, Mode,
};
use rspack_core::{rspack_sources::SourceMap, LoaderRunnerContext, Mode};
use rspack_error::{error, AnyhowError, Diagnostic, Result};
use rspack_loader_runner::{Identifiable, Identifier, Loader, LoaderContext};
use rspack_plugin_javascript::ast::{self, SourceMapConfig};
Expand All @@ -21,6 +18,8 @@ use rspack_util::source_map::SourceMapKind;
use swc_config::{config_types::MergingOption, merge::Merge};
use swc_core::base::config::SourceMapsConfig;
use swc_core::base::config::{InputSourceMap, OutputCharset, TransformConfig};
use swc_core::ecma::visit::VisitWith;
use transformer::IdentCollector;

#[derive(Debug)]
pub struct SwcLoader {
Expand Down Expand Up @@ -70,6 +69,8 @@ impl Loader<LoaderRunnerContext> for SwcLoader {
swc_options.config.input_source_map = Some(InputSourceMap::Str(source_map))
}
}
swc_options.filename = resource_path.to_string_lossy().to_string();
swc_options.source_file_name = Some(resource_path.to_string_lossy().to_string());

if swc_options.config.jsc.target.is_some() && swc_options.config.env.is_some() {
loader_context.emit_diagnostic(Diagnostic::warn(
Expand Down Expand Up @@ -113,9 +114,13 @@ impl Loader<LoaderRunnerContext> for SwcLoader {
})
.map_err(AnyhowError::from)?;

let codegen_options = ast::CodegenOptions {
let input_source_map = c
.input_source_map(&built.input_source_map)
.map_err(|e| error!(e.to_string()))?;
let mut codegen_options = ast::CodegenOptions {
target: Some(built.target),
minify: Some(built.minify),
input_source_map: input_source_map.as_ref(),
ascii_only: built
.output
.charset
Expand All @@ -130,34 +135,24 @@ impl Loader<LoaderRunnerContext> for SwcLoader {
inline_script: Some(false),
keep_comments: Some(true),
};
let program = tokio::task::block_in_place(|| c.transform(built).map_err(AnyhowError::from))?;
let ast = c.into_js_ast(program);

// If swc-loader is the latest loader available,
// then loader produces AST, which could be used as an optimization.
if loader_context.loader_index() == 0
&& (loader_context
.current_loader()
.composed_index_by_identifier(&self.identifier)
.map(|idx| idx == 0)
.unwrap_or(true))
&& !loader_context
.additional_data
.contains::<&LoadersShouldAlwaysGiveContent>()
{
loader_context
.additional_data
.insert(RspackAst::JavaScript(ast));
loader_context.additional_data.insert(codegen_options);
loader_context.content = Some("".to_owned().into())
} else {
let TransformOutput { code, map } = ast::stringify(&ast, codegen_options)?;
loader_context.content = Some(code.into());
loader_context.source_map = map
.map(|m| SourceMap::from_json(&m))
.transpose()
.map_err(|e| error!(e.to_string()))?;
let program = tokio::task::block_in_place(|| c.transform(built).map_err(AnyhowError::from))?;
if source_map_kind.enabled() {
let mut v = IdentCollector {
names: Default::default(),
};
program.visit_with(&mut v);
codegen_options.source_map_config.names = v.names;
}
let ast = c.into_js_ast(program);
let TransformOutput { code, map } = ast::stringify(&ast, codegen_options)?;

loader_context.content = Some(code.into());
let map = map
.map(|m| SourceMap::from_json(&m))
.transpose()
.map_err(|e| error!(e.to_string()))?;
loader_context.source_map = map;

Ok(())
}
Expand Down
Loading

0 comments on commit 79d188c

Please sign in to comment.