Skip to content

Commit

Permalink
refactor: worker parser plugin (#6881)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahabhgk authored Jun 21, 2024
1 parent 3153b2a commit 20e10f5
Show file tree
Hide file tree
Showing 15 changed files with 258 additions and 434 deletions.
180 changes: 0 additions & 180 deletions crates/rspack_core/src/dependency/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,183 +156,3 @@ impl std::fmt::Debug for DependencyCondition {
}
}
}

// TODO: should move to rspack_plugin_javascript once we drop old treeshaking
pub mod needs_refactor {
use once_cell::sync::Lazy;
use regex::Regex;
use swc_core::{
common::{EqIgnoreSpan, Spanned, SyntaxContext, DUMMY_SP},
ecma::{
ast::{
Expr, ExprOrSpread, Id, Ident, ImportDecl, Lit, MemberExpr, MemberProp, MetaPropExpr,
MetaPropKind, ModuleExportName, NewExpr,
},
atoms::Atom,
},
};

use crate::SpanExt;

static IMPORT_META: Lazy<Expr> = Lazy::new(|| {
Expr::Member(MemberExpr {
span: DUMMY_SP,
obj: Box::new(Expr::MetaProp(MetaPropExpr {
span: DUMMY_SP,
kind: MetaPropKind::ImportMeta,
})),
prop: MemberProp::Ident(Ident {
span: DUMMY_SP,
sym: "url".into(),
optional: false,
}),
})
});

pub fn match_new_url(new_expr: &NewExpr) -> Option<(u32, u32, String)> {
fn is_import_meta_url(expr: &Expr) -> bool {
Ident::within_ignored_ctxt(|| expr.eq_ignore_span(&IMPORT_META))
}

if matches!(&*new_expr.callee, Expr::Ident(Ident { sym, .. }) if sym == "URL")
&& let Some(args) = &new_expr.args
&& let (Some(first), Some(second)) = (args.first(), args.get(1))
&& let (
ExprOrSpread {
spread: None,
expr: box Expr::Lit(Lit::Str(path)),
},
ExprOrSpread {
spread: None,
expr: box expr,
},
) = (first, second)
&& is_import_meta_url(expr)
{
return Some((
path.span.real_lo(),
expr.span().real_hi(),
path.value.to_string(),
));
}
None
}

pub static WORKER_FROM_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^(.+?)(\(\))?\s+from\s+(.+)$").expect("invalid regex"));

#[derive(Debug, Default)]
pub struct WorkerSyntaxList {
variables: Vec<WorkerSyntax>,
globals: Vec<WorkerSyntax>,
}

impl WorkerSyntaxList {
pub fn push(&mut self, syntax: WorkerSyntax) {
if syntax.ctxt.is_some() {
self.variables.push(syntax);
} else {
self.globals.push(syntax);
}
}

fn find_worker_syntax(&self, ident: &Ident) -> Option<&WorkerSyntax> {
(self.variables.iter().chain(self.globals.iter())).find(|s| s.matches(ident))
}

pub fn match_new_worker(&self, new_expr: &NewExpr) -> bool {
matches!(&*new_expr.callee, Expr::Ident(ident) if self.find_worker_syntax(ident).is_some())
}
}

impl Extend<WorkerSyntax> for WorkerSyntaxList {
fn extend<T: IntoIterator<Item = WorkerSyntax>>(&mut self, iter: T) {
for i in iter {
self.push(i);
}
}
}

#[derive(Debug, PartialEq, Eq)]
pub struct WorkerSyntax {
word: Atom,
ctxt: Option<SyntaxContext>,
}

impl WorkerSyntax {
pub fn new(word: Atom, ctxt: Option<SyntaxContext>) -> Self {
Self { word, ctxt }
}

pub fn matches(&self, ident: &Ident) -> bool {
if let Some(ctxt) = self.ctxt {
let (word, id_ctxt) = ident.to_id();
word == self.word && id_ctxt == ctxt
} else {
self.word == ident.sym
}
}
}

pub const DEFAULT_WORKER_SYNTAX: &[&str] =
&["Worker", "SharedWorker", "Worker from worker_threads"];

pub fn init_worker_syntax_scanner(
syntax: &'static [&'static str],
caps: &mut Vec<(&str, &str)>,
list: &mut WorkerSyntaxList,
) {
for s in syntax {
if let Some(captures) = WORKER_FROM_REGEX.captures(s)
&& let Some(ids) = captures.get(1)
&& let Some(source) = captures.get(3)
{
caps.push((ids.as_str(), source.as_str()));
} else {
list.push(WorkerSyntax::new(Atom::from(*s), None))
}
}
}

pub fn collect_from_import_decl(
caps: &[(&str, &str)],
decl: &ImportDecl,
list: &mut WorkerSyntaxList,
) {
let source = &*decl.src.value;
let found = caps
.iter()
.filter(|cap| cap.1 == source)
.flat_map(|cap| {
if cap.0 == "default" {
decl
.specifiers
.iter()
.filter_map(|spec| spec.as_default())
.map(|spec| spec.local.to_id())
.collect::<Vec<Id>>()
} else {
decl
.specifiers
.iter()
.filter_map(|spec| {
spec.as_named().filter(|named| {
if let Some(imported) = &named.imported {
let s = match imported {
ModuleExportName::Ident(s) => &s.sym,
ModuleExportName::Str(s) => &s.value,
};
s == cap.0
} else {
&*named.local.sym == cap.0
}
})
})
.map(|spec| spec.local.to_id())
.collect::<Vec<Id>>()
}
})
.map(|pair| WorkerSyntax::new(pair.0, Some(pair.1)));
list.extend(found);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::sync::Arc;

use itertools::Itertools;
use rspack_core::diagnostics::map_box_diagnostics_to_module_parse_diagnostics;
use rspack_core::needs_refactor::WorkerSyntaxList;
use rspack_core::rspack_sources::{BoxSource, ReplaceSource, Source, SourceExt};
use rspack_core::{
render_init_fragments, AsyncDependenciesBlockIdentifier, BuildMetaExportsType, ChunkGraph,
Expand Down Expand Up @@ -164,7 +163,6 @@ impl ParserAndGenerator for JavaScriptParserAndGenerator {
});

let mut path_ignored_spans = PathIgnoredSpans::default();
let mut worker_syntax_list = WorkerSyntaxList::default();

let ScanDependenciesResult {
mut dependencies,
Expand All @@ -177,7 +175,6 @@ impl ParserAndGenerator for JavaScriptParserAndGenerator {
scan_dependencies(
&fm,
program,
&mut worker_syntax_list,
resource_data,
compiler_options,
module_type,
Expand Down
3 changes: 2 additions & 1 deletion crates/rspack_plugin_javascript/src/parser_plugin/drive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,10 @@ impl JavascriptParserPlugin for JavaScriptParserPluginDrive {
&self,
parser: &mut JavascriptParser,
expr: &swc_core::ecma::ast::NewExpr,
for_name: &str,
) -> Option<bool> {
for plugin in &self.plugins {
let res = plugin.new_expression(parser, expr);
let res = plugin.new_expression(parser, expr, for_name);
// `SyncBailHook`
if res.is_some() {
return res;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ fn get_non_optional_member_chain_from_member(member: &MemberExpr, mut count: i32

pub struct HarmonyImportDependencyParserPlugin;

const HARMONY_SPECIFIER_TAG: &str = "_identifier__harmony_specifier_tag__";
pub const HARMONY_SPECIFIER_TAG: &str = "_identifier__harmony_specifier_tag__";

#[derive(Debug, Clone)]
struct HarmonySpecifierData {
name: Atom,
source: Atom,
ids: Vec<Atom>,
source_order: i32,
pub struct HarmonySpecifierData {
pub name: Atom,
pub source: Atom,
pub ids: Vec<Atom>,
pub source_order: i32,
}

impl TagInfoData for HarmonySpecifierData {
Expand Down
3 changes: 0 additions & 3 deletions crates/rspack_plugin_javascript/src/parser_plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ mod url_plugin;
mod use_strict_plugin;
mod webpack_included_plugin;
mod worker_plugin;
/// TODO: should move to rspack_plugin_javascript once we drop old treeshaking
mod worker_syntax_plugin;

pub(crate) use self::api_plugin::APIPlugin;
pub(crate) use self::check_var_decl::CheckVarDeclaratorIdent;
Expand Down Expand Up @@ -57,7 +55,6 @@ pub(crate) use self::url_plugin::URLPlugin;
pub(crate) use self::use_strict_plugin::UseStrictPlugin;
pub(crate) use self::webpack_included_plugin::WebpackIsIncludedPlugin;
pub(crate) use self::worker_plugin::WorkerPlugin;
pub(crate) use self::worker_syntax_plugin::WorkerSyntaxScanner;

pub static JS_DEFAULT_KEYWORD: once_cell::sync::Lazy<swc_core::atoms::Atom> =
once_cell::sync::Lazy::new(|| swc_core::atoms::atom!("default"));
7 changes: 6 additions & 1 deletion crates/rspack_plugin_javascript/src/parser_plugin/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,12 @@ pub trait JavascriptParserPlugin {
None
}

fn new_expression(&self, _parser: &mut JavascriptParser, _expr: &NewExpr) -> Option<bool> {
fn new_expression(
&self,
_parser: &mut JavascriptParser,
_expr: &NewExpr,
_for_name: &str,
) -> Option<bool> {
None
}

Expand Down
47 changes: 42 additions & 5 deletions crates/rspack_plugin_javascript/src/parser_plugin/url_plugin.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,59 @@
use rspack_core::SpanExt;
use swc_core::{
common::Spanned,
ecma::ast::{Expr, ExprOrSpread, MetaPropKind, NewExpr},
};

use super::JavascriptParserPlugin;
use crate::{dependency::URLDependency, visitors::JavascriptParser};

pub fn get_url_request(
parser: &mut JavascriptParser,
expr: &NewExpr,
) -> Option<(String, u32, u32)> {
if let Some(args) = &expr.args
&& let Some(ExprOrSpread {
spread: None,
expr: arg1,
}) = args.first()
&& let Some(ExprOrSpread {
spread: None,
expr: box Expr::Member(arg2),
}) = args.get(1)
{
let chain = parser.extract_member_expression_chain(arg2);
if let Some(meta) = chain.object.as_meta_prop()
&& matches!(meta.kind, MetaPropKind::ImportMeta)
&& chain.members.len() == 1
&& matches!(chain.members.first(), Some(member) if member == "url")
{
return parser
.evaluate_expression(arg1)
.as_string()
.map(|req| (req, arg1.span().real_lo(), arg2.span().real_hi()));
}
}
None
}

pub struct URLPlugin {
pub relative: bool,
}

impl JavascriptParserPlugin for URLPlugin {
fn can_rename(&self, _parser: &mut JavascriptParser, for_name: &str) -> Option<bool> {
(for_name == "URL").then_some(true)
}

fn new_expression(
&self,
parser: &mut JavascriptParser,
expr: &swc_core::ecma::ast::NewExpr,
expr: &NewExpr,
for_name: &str,
) -> Option<bool> {
if parser.worker_syntax_list.match_new_worker(expr) {
// skip `new Worker(new Url,)`
None
} else if let Some((start, end, request)) = rspack_core::needs_refactor::match_new_url(expr) {
if for_name == "URL"
&& let Some((request, start, end)) = get_url_request(parser, expr)
{
parser.dependencies.push(Box::new(URLDependency::new(
start,
end,
Expand Down
Loading

1 comment on commit 20e10f5

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ“ Ran ecosystem CI: Open

suite result
modernjs ❌ failure
_selftest βœ… success
nx βœ… success
rspress ❌ failure
rsbuild ❌ failure
compat βœ… success
examples ❌ failure

Please sign in to comment.