Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: worker parser plugin #6881

Merged
merged 3 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading