chore(rust): change indentation to tabs

The 4 spaces indentation uses too much screen width in the deep indentation of transforming, and with tabs people can configure their own visible width.
This commit is contained in:
Wout Mertens
2024-03-22 16:43:05 +01:00
parent bfd433d597
commit cf86daa15f
24 changed files with 5790 additions and 5791 deletions

View File

@@ -1,5 +1,5 @@
extern crate napi_build;
fn main() {
napi_build::setup();
napi_build::setup();
}

View File

@@ -15,27 +15,27 @@ static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
#[allow(clippy::needless_pass_by_value)]
#[js_function(1)]
fn transform_fs(ctx: CallContext) -> Result<JsUnknown> {
let opts = ctx.get::<JsObject>(0)?;
let config: qwik_core::TransformFsOptions = ctx.env.from_js_value(opts)?;
let opts = ctx.get::<JsObject>(0)?;
let config: qwik_core::TransformFsOptions = ctx.env.from_js_value(opts)?;
let result = qwik_core::transform_fs(config).unwrap();
ctx.env.to_js_value(&result)
let result = qwik_core::transform_fs(config).unwrap();
ctx.env.to_js_value(&result)
}
#[allow(clippy::needless_pass_by_value)]
#[js_function(1)]
fn transform_modules(ctx: CallContext) -> Result<JsUnknown> {
let opts = ctx.get::<JsObject>(0)?;
let config: qwik_core::TransformModulesOptions = ctx.env.from_js_value(opts)?;
let opts = ctx.get::<JsObject>(0)?;
let config: qwik_core::TransformModulesOptions = ctx.env.from_js_value(opts)?;
let result = qwik_core::transform_modules(config).unwrap();
ctx.env.to_js_value(&result)
let result = qwik_core::transform_modules(config).unwrap();
ctx.env.to_js_value(&result)
}
#[module_exports]
fn init(mut exports: JsObject) -> Result<()> {
exports.create_named_method("transform_fs", transform_fs)?;
exports.create_named_method("transform_modules", transform_modules)?;
exports.create_named_method("transform_fs", transform_fs)?;
exports.create_named_method("transform_modules", transform_modules)?;
Ok(())
Ok(())
}

View File

@@ -9,24 +9,24 @@ use path_absolutize::Absolutize;
use qwik_core::{transform_fs, EmitMode, EntryStrategy, MinifyMode, TransformFsOptions};
struct OptimizerInput {
glob: Option<String>,
manifest: Option<String>,
core_module: Option<String>,
scope: Option<String>,
src: PathBuf,
dest: PathBuf,
mode: EmitMode,
strategy: EntryStrategy,
transpile_ts: bool,
transpile_jsx: bool,
preserve_filenames: bool,
minify: MinifyMode,
sourcemaps: bool,
explicit_extensions: bool,
glob: Option<String>,
manifest: Option<String>,
core_module: Option<String>,
scope: Option<String>,
src: PathBuf,
dest: PathBuf,
mode: EmitMode,
strategy: EntryStrategy,
transpile_ts: bool,
transpile_jsx: bool,
preserve_filenames: bool,
minify: MinifyMode,
sourcemaps: bool,
explicit_extensions: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let matches = Command::new("qwik")
let matches = Command::new("qwik")
.version("1.0")
.arg_required_else_help(true)
.subcommand_required(true)
@@ -85,85 +85,85 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
)
.get_matches();
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level app
if let Some(matches) = matches.subcommand_matches("optimize") {
// "$ myapp test" was run
let strategy = match matches.value_of("strategy") {
Some("inline") => EntryStrategy::Inline,
Some("hook") => EntryStrategy::Hook,
Some("single") => EntryStrategy::Single,
Some("component") => EntryStrategy::Component,
Some("smart") | None => EntryStrategy::Smart,
_ => panic!("Invalid strategy option"),
};
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level app
if let Some(matches) = matches.subcommand_matches("optimize") {
// "$ myapp test" was run
let strategy = match matches.value_of("strategy") {
Some("inline") => EntryStrategy::Inline,
Some("hook") => EntryStrategy::Hook,
Some("single") => EntryStrategy::Single,
Some("component") => EntryStrategy::Component,
Some("smart") | None => EntryStrategy::Smart,
_ => panic!("Invalid strategy option"),
};
let minify = match matches.value_of("minify") {
Some("none") => MinifyMode::None,
Some("simplify") | None => MinifyMode::Simplify,
_ => panic!("Invalid minify option"),
};
let minify = match matches.value_of("minify") {
Some("none") => MinifyMode::None,
Some("simplify") | None => MinifyMode::Simplify,
_ => panic!("Invalid minify option"),
};
let mode = match matches.value_of("mode") {
Some("dev") => EmitMode::Dev,
Some("prod") => EmitMode::Prod,
Some("lib") | None => EmitMode::Lib,
_ => panic!("Invalid mode option"),
};
optimize(OptimizerInput {
src: matches.value_of_t_or_exit("src"),
dest: matches.value_of_t_or_exit("dest"),
manifest: matches.value_of("manifest").map(|s| s.into()),
core_module: matches.value_of("core_module").map(|s| s.into()),
scope: matches.value_of("scope").map(|s| s.into()),
mode,
glob: None,
strategy,
minify,
explicit_extensions: matches.is_present("extensions"),
transpile_jsx: !matches.is_present("no-jsx"),
transpile_ts: !matches.is_present("no-ts"),
preserve_filenames: matches.is_present("preserve-filenames"),
sourcemaps: matches.is_present("sourcemaps"),
})?;
}
Ok(())
let mode = match matches.value_of("mode") {
Some("dev") => EmitMode::Dev,
Some("prod") => EmitMode::Prod,
Some("lib") | None => EmitMode::Lib,
_ => panic!("Invalid mode option"),
};
optimize(OptimizerInput {
src: matches.value_of_t_or_exit("src"),
dest: matches.value_of_t_or_exit("dest"),
manifest: matches.value_of("manifest").map(|s| s.into()),
core_module: matches.value_of("core_module").map(|s| s.into()),
scope: matches.value_of("scope").map(|s| s.into()),
mode,
glob: None,
strategy,
minify,
explicit_extensions: matches.is_present("extensions"),
transpile_jsx: !matches.is_present("no-jsx"),
transpile_ts: !matches.is_present("no-ts"),
preserve_filenames: matches.is_present("preserve-filenames"),
sourcemaps: matches.is_present("sourcemaps"),
})?;
}
Ok(())
}
fn optimize(
optimizer_input: OptimizerInput,
optimizer_input: OptimizerInput,
) -> Result<qwik_core::TransformOutput, Box<dyn std::error::Error>> {
let current_dir = std::env::current_dir()?;
let src_dir = current_dir.join(optimizer_input.src).canonicalize()?;
let current_dir = std::env::current_dir()?;
let src_dir = current_dir.join(optimizer_input.src).canonicalize()?;
let result = transform_fs(TransformFsOptions {
src_dir: src_dir.to_string_lossy().to_string(),
vendor_roots: vec![],
glob: optimizer_input.glob,
source_maps: optimizer_input.sourcemaps,
minify: optimizer_input.minify,
transpile_jsx: optimizer_input.transpile_jsx,
transpile_ts: optimizer_input.transpile_ts,
preserve_filenames: optimizer_input.preserve_filenames,
entry_strategy: optimizer_input.strategy,
explicit_extensions: optimizer_input.explicit_extensions,
core_module: optimizer_input.core_module,
root_dir: None,
let result = transform_fs(TransformFsOptions {
src_dir: src_dir.to_string_lossy().to_string(),
vendor_roots: vec![],
glob: optimizer_input.glob,
source_maps: optimizer_input.sourcemaps,
minify: optimizer_input.minify,
transpile_jsx: optimizer_input.transpile_jsx,
transpile_ts: optimizer_input.transpile_ts,
preserve_filenames: optimizer_input.preserve_filenames,
entry_strategy: optimizer_input.strategy,
explicit_extensions: optimizer_input.explicit_extensions,
core_module: optimizer_input.core_module,
root_dir: None,
mode: optimizer_input.mode,
scope: optimizer_input.scope,
mode: optimizer_input.mode,
scope: optimizer_input.scope,
manual_chunks: None,
strip_exports: None,
strip_ctx_name: None,
strip_event_handlers: false,
reg_ctx_name: None,
is_server: None,
})?;
manual_chunks: None,
strip_exports: None,
strip_ctx_name: None,
strip_event_handlers: false,
reg_ctx_name: None,
is_server: None,
})?;
result.write_to_fs(
&current_dir.join(optimizer_input.dest).absolutize()?,
optimizer_input.manifest,
)?;
Ok(result)
result.write_to_fs(
&current_dir.join(optimizer_input.dest).absolutize()?,
optimizer_input.manifest,
)?;
Ok(result)
}

View File

@@ -7,7 +7,7 @@ use test::Bencher;
#[bench]
fn transform_todo_app(b: &mut Bencher) {
b.iter(|| {
b.iter(|| {
let code = r#"
import {
Fragment,

View File

@@ -10,56 +10,56 @@ use swc_ecmascript::ast;
use swc_ecmascript::visit::{VisitMut, VisitMutWith};
pub struct SideEffectVisitor<'a> {
global_collector: &'a GlobalCollect,
imports: HashSet<JsWord>,
path_data: &'a PathData,
src_dir: &'a Path,
global_collector: &'a GlobalCollect,
imports: HashSet<JsWord>,
path_data: &'a PathData,
src_dir: &'a Path,
}
impl<'a> SideEffectVisitor<'a> {
pub fn new(
global_collector: &'a GlobalCollect,
path_data: &'a PathData,
src_dir: &'a Path,
) -> Self {
Self {
global_collector,
path_data,
src_dir,
imports: HashSet::new(),
}
}
pub fn new(
global_collector: &'a GlobalCollect,
path_data: &'a PathData,
src_dir: &'a Path,
) -> Self {
Self {
global_collector,
path_data,
src_dir,
imports: HashSet::new(),
}
}
}
impl<'a> VisitMut for SideEffectVisitor<'a> {
fn visit_mut_import_decl(&mut self, node: &mut ast::ImportDecl) {
if node.src.value.starts_with('.') {
self.imports.insert(node.src.value.clone());
}
}
fn visit_mut_module(&mut self, node: &mut ast::Module) {
node.visit_mut_children_with(self);
let mut imports: Vec<_> = self.global_collector.imports.values().collect();
imports.sort_by_key(|i| i.source.clone());
fn visit_mut_import_decl(&mut self, node: &mut ast::ImportDecl) {
if node.src.value.starts_with('.') {
self.imports.insert(node.src.value.clone());
}
}
fn visit_mut_module(&mut self, node: &mut ast::Module) {
node.visit_mut_children_with(self);
let mut imports: Vec<_> = self.global_collector.imports.values().collect();
imports.sort_by_key(|i| i.source.clone());
for import in imports {
if import.source.starts_with('.') && !self.imports.contains(&import.source) {
let abs_dir = self.path_data.abs_dir.to_slash_lossy();
let relative = relative_path::RelativePath::new(&abs_dir);
let final_path = relative.join(import.source.as_ref()).normalize();
if final_path.starts_with(self.src_dir.to_str().unwrap()) {
node.body.insert(
0,
ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(ast::ImportDecl {
asserts: None,
span: DUMMY_SP,
specifiers: vec![],
type_only: false,
src: Box::new(ast::Str::from(import.source.clone())),
})),
);
}
}
}
}
for import in imports {
if import.source.starts_with('.') && !self.imports.contains(&import.source) {
let abs_dir = self.path_data.abs_dir.to_slash_lossy();
let relative = relative_path::RelativePath::new(&abs_dir);
let final_path = relative.join(import.source.as_ref()).normalize();
if final_path.starts_with(self.src_dir.to_str().unwrap()) {
node.body.insert(
0,
ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(ast::ImportDecl {
asserts: None,
span: DUMMY_SP,
specifiers: vec![],
type_only: false,
src: Box::new(ast::Str::from(import.source.clone())),
})),
);
}
}
}
}
}

View File

@@ -2,58 +2,58 @@ use swc_common::{Mark, Spanned};
use swc_ecmascript::ast;
use swc_ecmascript::visit::VisitMut;
pub struct Treeshaker {
pub marker: CleanMarker,
pub cleaner: CleanSideEffects,
pub marker: CleanMarker,
pub cleaner: CleanSideEffects,
}
pub struct CleanSideEffects {
pub did_drop: bool,
pub mark: Mark,
pub did_drop: bool,
pub mark: Mark,
}
pub struct CleanMarker {
pub mark: Mark,
pub mark: Mark,
}
impl Treeshaker {
pub fn new() -> Self {
let mark = Mark::new();
Self {
marker: CleanMarker { mark },
cleaner: CleanSideEffects {
did_drop: false,
mark,
},
}
}
pub fn new() -> Self {
let mark = Mark::new();
Self {
marker: CleanMarker { mark },
cleaner: CleanSideEffects {
did_drop: false,
mark,
},
}
}
}
impl VisitMut for CleanMarker {
fn visit_mut_module_item(&mut self, node: &mut ast::ModuleItem) {
if let ast::ModuleItem::Stmt(ast::Stmt::Expr(expr)) = node {
expr.span = expr.span.apply_mark(self.mark);
}
}
fn visit_mut_module_item(&mut self, node: &mut ast::ModuleItem) {
if let ast::ModuleItem::Stmt(ast::Stmt::Expr(expr)) = node {
expr.span = expr.span.apply_mark(self.mark);
}
}
}
impl VisitMut for CleanSideEffects {
fn visit_mut_module(&mut self, node: &mut ast::Module) {
let it = node.body.extract_if(|item| {
if item.span().has_mark(self.mark) {
return false;
}
match item {
ast::ModuleItem::Stmt(ast::Stmt::Expr(expr)) => match *expr.expr {
ast::Expr::New(_) | ast::Expr::Call(_) => {
self.did_drop = true;
true
}
_ => false,
},
_ => false,
}
});
// Consume the iterator to force the extraction.
for _ in it {}
}
fn visit_mut_module(&mut self, node: &mut ast::Module) {
let it = node.body.extract_if(|item| {
if item.span().has_mark(self.mark) {
return false;
}
match item {
ast::ModuleItem::Stmt(ast::Stmt::Expr(expr)) => match *expr.expr {
ast::Expr::New(_) | ast::Expr::Call(_) => {
self.did_drop = true;
true
}
_ => false,
},
_ => false,
}
});
// Consume the iterator to force the extraction.
for _ in it {}
}
}

View File

@@ -1,7 +1,7 @@
use crate::collector::{new_ident_from_id, GlobalCollect, Id, ImportKind};
use crate::parse::{
emit_source_code, might_need_handle_watch, HookAnalysis, PathData, TransformModule,
TransformOutput,
emit_source_code, might_need_handle_watch, HookAnalysis, PathData, TransformModule,
TransformOutput,
};
use crate::transform::{add_handle_watch, create_synthetic_named_import};
use crate::words::*;
@@ -18,416 +18,416 @@ use swc_ecmascript::ast;
use swc_ecmascript::utils::private_ident;
macro_rules! id {
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
}
pub struct NewModuleCtx<'a> {
pub expr: Box<ast::Expr>,
pub path: &'a PathData,
pub name: &'a str,
pub local_idents: &'a [Id],
pub scoped_idents: &'a [Id],
pub global: &'a GlobalCollect,
pub core_module: &'a JsWord,
pub is_entry: bool,
pub need_handle_watch: bool,
pub need_transform: bool,
pub explicit_extensions: bool,
pub leading_comments: SingleThreadedCommentsMap,
pub trailing_comments: SingleThreadedCommentsMap,
pub expr: Box<ast::Expr>,
pub path: &'a PathData,
pub name: &'a str,
pub local_idents: &'a [Id],
pub scoped_idents: &'a [Id],
pub global: &'a GlobalCollect,
pub core_module: &'a JsWord,
pub is_entry: bool,
pub need_handle_watch: bool,
pub need_transform: bool,
pub explicit_extensions: bool,
pub leading_comments: SingleThreadedCommentsMap,
pub trailing_comments: SingleThreadedCommentsMap,
}
pub fn new_module(ctx: NewModuleCtx) -> Result<(ast::Module, SingleThreadedComments), Error> {
let comments = SingleThreadedComments::from_leading_and_trailing(
ctx.leading_comments,
ctx.trailing_comments,
);
let max_cap = ctx.global.imports.len() + ctx.global.exports.len();
let mut module = ast::Module {
span: DUMMY_SP,
body: Vec::with_capacity(max_cap),
shebang: None,
};
let comments = SingleThreadedComments::from_leading_and_trailing(
ctx.leading_comments,
ctx.trailing_comments,
);
let max_cap = ctx.global.imports.len() + ctx.global.exports.len();
let mut module = ast::Module {
span: DUMMY_SP,
body: Vec::with_capacity(max_cap),
shebang: None,
};
let has_scoped_idents = ctx.need_transform && !ctx.scoped_idents.is_empty();
let use_lexical_scope = if has_scoped_idents {
let new_local = id!(private_ident!(&USE_LEXICAL_SCOPE.clone()));
module
.body
.push(create_synthetic_named_import(&new_local, ctx.core_module));
Some(new_local)
} else {
None
};
let has_scoped_idents = ctx.need_transform && !ctx.scoped_idents.is_empty();
let use_lexical_scope = if has_scoped_idents {
let new_local = id!(private_ident!(&USE_LEXICAL_SCOPE.clone()));
module
.body
.push(create_synthetic_named_import(&new_local, ctx.core_module));
Some(new_local)
} else {
None
};
for id in ctx.local_idents {
if let Some(import) = ctx.global.imports.get(id) {
let specifier = match import.kind {
ImportKind::Named => ast::ImportSpecifier::Named(ast::ImportNamedSpecifier {
is_type_only: false,
span: DUMMY_SP,
imported: if import.specifier == id.0 {
None
} else {
Some(ast::ModuleExportName::Ident(ast::Ident::new(
import.specifier.clone(),
DUMMY_SP,
)))
},
local: new_ident_from_id(id),
}),
ImportKind::Default => ast::ImportSpecifier::Default(ast::ImportDefaultSpecifier {
span: DUMMY_SP,
local: new_ident_from_id(id),
}),
ImportKind::All => ast::ImportSpecifier::Namespace(ast::ImportStarAsSpecifier {
span: DUMMY_SP,
local: new_ident_from_id(id),
}),
};
module
.body
.push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(
ast::ImportDecl {
span: DUMMY_SP,
type_only: false,
asserts: import.asserts.clone(),
src: Box::new(ast::Str {
span: DUMMY_SP,
value: fix_path(
&ctx.path.abs_dir,
&ctx.path.base_dir,
import.source.as_ref(),
)?,
raw: None,
}),
specifiers: vec![specifier],
},
)));
} else if let Some(export) = ctx.global.exports.get(id) {
let filename = if ctx.explicit_extensions {
&ctx.path.file_name
} else {
&ctx.path.file_stem
};
let imported = export
.as_ref()
.map(|e| ast::ModuleExportName::Ident(ast::Ident::new(e.clone(), DUMMY_SP)));
module
.body
.push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(
ast::ImportDecl {
span: DUMMY_SP,
type_only: false,
asserts: None,
src: Box::new(ast::Str {
span: DUMMY_SP,
value: fix_path(
&ctx.path.abs_dir,
&ctx.path.base_dir,
&format!("./{}", filename),
)?,
raw: None,
}),
specifiers: vec![ast::ImportSpecifier::Named(ast::ImportNamedSpecifier {
is_type_only: false,
span: DUMMY_SP,
imported,
local: new_ident_from_id(id),
})],
},
)));
}
}
for id in ctx.local_idents {
if let Some(import) = ctx.global.imports.get(id) {
let specifier = match import.kind {
ImportKind::Named => ast::ImportSpecifier::Named(ast::ImportNamedSpecifier {
is_type_only: false,
span: DUMMY_SP,
imported: if import.specifier == id.0 {
None
} else {
Some(ast::ModuleExportName::Ident(ast::Ident::new(
import.specifier.clone(),
DUMMY_SP,
)))
},
local: new_ident_from_id(id),
}),
ImportKind::Default => ast::ImportSpecifier::Default(ast::ImportDefaultSpecifier {
span: DUMMY_SP,
local: new_ident_from_id(id),
}),
ImportKind::All => ast::ImportSpecifier::Namespace(ast::ImportStarAsSpecifier {
span: DUMMY_SP,
local: new_ident_from_id(id),
}),
};
module
.body
.push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(
ast::ImportDecl {
span: DUMMY_SP,
type_only: false,
asserts: import.asserts.clone(),
src: Box::new(ast::Str {
span: DUMMY_SP,
value: fix_path(
&ctx.path.abs_dir,
&ctx.path.base_dir,
import.source.as_ref(),
)?,
raw: None,
}),
specifiers: vec![specifier],
},
)));
} else if let Some(export) = ctx.global.exports.get(id) {
let filename = if ctx.explicit_extensions {
&ctx.path.file_name
} else {
&ctx.path.file_stem
};
let imported = export
.as_ref()
.map(|e| ast::ModuleExportName::Ident(ast::Ident::new(e.clone(), DUMMY_SP)));
module
.body
.push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(
ast::ImportDecl {
span: DUMMY_SP,
type_only: false,
asserts: None,
src: Box::new(ast::Str {
span: DUMMY_SP,
value: fix_path(
&ctx.path.abs_dir,
&ctx.path.base_dir,
&format!("./{}", filename),
)?,
raw: None,
}),
specifiers: vec![ast::ImportSpecifier::Named(ast::ImportNamedSpecifier {
is_type_only: false,
span: DUMMY_SP,
imported,
local: new_ident_from_id(id),
})],
},
)));
}
}
let expr = if let Some(use_lexical_scope) = use_lexical_scope {
Box::new(transform_function_expr(
*ctx.expr,
&use_lexical_scope,
ctx.scoped_idents,
))
} else {
ctx.expr
};
let expr = if let Some(use_lexical_scope) = use_lexical_scope {
Box::new(transform_function_expr(
*ctx.expr,
&use_lexical_scope,
ctx.scoped_idents,
))
} else {
ctx.expr
};
module.body.push(create_named_export(expr, ctx.name));
if ctx.need_handle_watch {
// Inject qwik internal import
add_handle_watch(&mut module.body, ctx.core_module);
}
Ok((module, comments))
module.body.push(create_named_export(expr, ctx.name));
if ctx.need_handle_watch {
// Inject qwik internal import
add_handle_watch(&mut module.body, ctx.core_module);
}
Ok((module, comments))
}
pub fn fix_path<S: AsRef<Path>, D: AsRef<Path>>(
src: S,
dest: D,
ident: &str,
src: S,
dest: D,
ident: &str,
) -> Result<JsWord, Error> {
let src = src.as_ref();
let dest = dest.as_ref();
if ident.starts_with('.') {
let diff = pathdiff::diff_paths(src, dest);
let src = src.as_ref();
let dest = dest.as_ref();
if ident.starts_with('.') {
let diff = pathdiff::diff_paths(src, dest);
if let Some(diff) = diff {
let normalize = diff.to_slash_lossy();
let relative = relative_path::RelativePath::new(&normalize);
let final_path = relative.join(ident).normalize();
let final_str = final_path.as_str();
return Ok(if final_str.starts_with('.') {
JsWord::from(final_str)
} else {
JsWord::from(format!("./{}", final_str))
});
}
}
if let Some(diff) = diff {
let normalize = diff.to_slash_lossy();
let relative = relative_path::RelativePath::new(&normalize);
let final_path = relative.join(ident).normalize();
let final_str = final_path.as_str();
return Ok(if final_str.starts_with('.') {
JsWord::from(final_str)
} else {
JsWord::from(format!("./{}", final_str))
});
}
}
Ok(JsWord::from(ident))
Ok(JsWord::from(ident))
}
fn create_named_export(expr: Box<ast::Expr>, name: &str) -> ast::ModuleItem {
ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportDecl(ast::ExportDecl {
span: DUMMY_SP,
decl: ast::Decl::Var(Box::new(ast::VarDecl {
span: DUMMY_SP,
kind: ast::VarDeclKind::Const,
declare: false,
decls: vec![ast::VarDeclarator {
span: DUMMY_SP,
definite: false,
name: ast::Pat::Ident(ast::BindingIdent::from(ast::Ident::new(
JsWord::from(name),
DUMMY_SP,
))),
init: Some(expr),
}],
})),
}))
ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportDecl(ast::ExportDecl {
span: DUMMY_SP,
decl: ast::Decl::Var(Box::new(ast::VarDecl {
span: DUMMY_SP,
kind: ast::VarDeclKind::Const,
declare: false,
decls: vec![ast::VarDeclarator {
span: DUMMY_SP,
definite: false,
name: ast::Pat::Ident(ast::BindingIdent::from(ast::Ident::new(
JsWord::from(name),
DUMMY_SP,
))),
init: Some(expr),
}],
})),
}))
}
#[test]
fn test_fix_path() {
assert_eq!(
fix_path("src", "", "./state.qwik.mjs").unwrap(),
JsWord::from("./src/state.qwik.mjs")
);
assert_eq!(
fix_path("src", "", "./state.qwik.mjs").unwrap(),
JsWord::from("./src/state.qwik.mjs")
);
assert_eq!(
fix_path("src/path", "", "./state").unwrap(),
JsWord::from("./src/path/state")
);
assert_eq!(
fix_path("src/path", "", "./state").unwrap(),
JsWord::from("./src/path/state")
);
assert_eq!(
fix_path("src", "", "../state").unwrap(),
JsWord::from("./state")
);
assert_eq!(
fix_path("a", "a", "./state").unwrap(),
JsWord::from("./state")
);
assert_eq!(
fix_path("src", "", "../state").unwrap(),
JsWord::from("./state")
);
assert_eq!(
fix_path("a", "a", "./state").unwrap(),
JsWord::from("./state")
);
}
pub fn generate_entries(
mut output: TransformOutput,
core_module: &JsWord,
explicit_extensions: bool,
root_dir: Option<&Path>,
mut output: TransformOutput,
core_module: &JsWord,
explicit_extensions: bool,
root_dir: Option<&Path>,
) -> Result<TransformOutput, anyhow::Error> {
let source_map = Lrc::new(SourceMap::default());
let mut entries_map: BTreeMap<&str, Vec<&HookAnalysis>> = BTreeMap::new();
let mut new_modules = Vec::with_capacity(output.modules.len());
{
let hooks: Vec<&HookAnalysis> = output.modules.iter().flat_map(|m| &m.hook).collect();
for hook in hooks {
if let Some(ref e) = hook.entry {
entries_map
.entry(e.as_ref())
.or_insert_with(Vec::new)
.push(hook);
}
}
let source_map = Lrc::new(SourceMap::default());
let mut entries_map: BTreeMap<&str, Vec<&HookAnalysis>> = BTreeMap::new();
let mut new_modules = Vec::with_capacity(output.modules.len());
{
let hooks: Vec<&HookAnalysis> = output.modules.iter().flat_map(|m| &m.hook).collect();
for hook in hooks {
if let Some(ref e) = hook.entry {
entries_map
.entry(e.as_ref())
.or_insert_with(Vec::new)
.push(hook);
}
}
for (entry, hooks) in &entries_map {
let module = new_entry_module(hooks, core_module, explicit_extensions);
let (code, map) =
emit_source_code(Lrc::clone(&source_map), None, &module, root_dir, false)
.context("Emitting source code")?;
new_modules.push(TransformModule {
path: [entry, ".js"].concat(),
code,
map,
is_entry: true,
hook: None,
order: 0,
});
}
}
output.modules.append(&mut new_modules);
for (entry, hooks) in &entries_map {
let module = new_entry_module(hooks, core_module, explicit_extensions);
let (code, map) =
emit_source_code(Lrc::clone(&source_map), None, &module, root_dir, false)
.context("Emitting source code")?;
new_modules.push(TransformModule {
path: [entry, ".js"].concat(),
code,
map,
is_entry: true,
hook: None,
order: 0,
});
}
}
output.modules.append(&mut new_modules);
Ok(output)
Ok(output)
}
fn new_entry_module(
hooks: &[&HookAnalysis],
core_module: &JsWord,
explicit_extensions: bool,
hooks: &[&HookAnalysis],
core_module: &JsWord,
explicit_extensions: bool,
) -> ast::Module {
let mut module = ast::Module {
span: DUMMY_SP,
body: Vec::with_capacity(hooks.len()),
shebang: None,
};
let mut need_handle_watch = false;
for hook in hooks {
let mut src = ["./", &hook.canonical_filename].concat();
if explicit_extensions {
src = src + "." + hook.extension.as_ref();
}
if might_need_handle_watch(&hook.ctx_kind, &hook.ctx_name) {
need_handle_watch = true;
}
module
.body
.push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed(
ast::NamedExport {
span: DUMMY_SP,
type_only: false,
asserts: None,
src: Some(Box::new(ast::Str {
span: DUMMY_SP,
value: JsWord::from(src),
raw: None,
})),
specifiers: vec![ast::ExportSpecifier::Named(ast::ExportNamedSpecifier {
is_type_only: false,
span: DUMMY_SP,
orig: ast::ModuleExportName::Ident(ast::Ident::new(
hook.name.clone(),
DUMMY_SP,
)),
exported: None,
})],
},
)));
}
if need_handle_watch {
add_handle_watch(&mut module.body, core_module);
}
module
let mut module = ast::Module {
span: DUMMY_SP,
body: Vec::with_capacity(hooks.len()),
shebang: None,
};
let mut need_handle_watch = false;
for hook in hooks {
let mut src = ["./", &hook.canonical_filename].concat();
if explicit_extensions {
src = src + "." + hook.extension.as_ref();
}
if might_need_handle_watch(&hook.ctx_kind, &hook.ctx_name) {
need_handle_watch = true;
}
module
.body
.push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed(
ast::NamedExport {
span: DUMMY_SP,
type_only: false,
asserts: None,
src: Some(Box::new(ast::Str {
span: DUMMY_SP,
value: JsWord::from(src),
raw: None,
})),
specifiers: vec![ast::ExportSpecifier::Named(ast::ExportNamedSpecifier {
is_type_only: false,
span: DUMMY_SP,
orig: ast::ModuleExportName::Ident(ast::Ident::new(
hook.name.clone(),
DUMMY_SP,
)),
exported: None,
})],
},
)));
}
if need_handle_watch {
add_handle_watch(&mut module.body, core_module);
}
module
}
pub fn transform_function_expr(
expr: ast::Expr,
use_lexical_scope: &Id,
scoped_idents: &[Id],
expr: ast::Expr,
use_lexical_scope: &Id,
scoped_idents: &[Id],
) -> ast::Expr {
match expr {
ast::Expr::Arrow(node) => {
ast::Expr::Arrow(transform_arrow_fn(node, use_lexical_scope, scoped_idents))
}
ast::Expr::Fn(node) => ast::Expr::Fn(transform_fn(node, use_lexical_scope, scoped_idents)),
_ => expr,
}
match expr {
ast::Expr::Arrow(node) => {
ast::Expr::Arrow(transform_arrow_fn(node, use_lexical_scope, scoped_idents))
}
ast::Expr::Fn(node) => ast::Expr::Fn(transform_fn(node, use_lexical_scope, scoped_idents)),
_ => expr,
}
}
fn transform_arrow_fn(
arrow: ast::ArrowExpr,
use_lexical_scope: &Id,
scoped_idents: &[Id],
arrow: ast::ArrowExpr,
use_lexical_scope: &Id,
scoped_idents: &[Id],
) -> ast::ArrowExpr {
match arrow.body {
box ast::BlockStmtOrExpr::BlockStmt(mut block) => {
let mut stmts = Vec::with_capacity(1 + block.stmts.len());
stmts.push(create_use_lexical_scope(use_lexical_scope, scoped_idents));
stmts.append(&mut block.stmts);
ast::ArrowExpr {
body: Box::new(ast::BlockStmtOrExpr::BlockStmt(ast::BlockStmt {
span: DUMMY_SP,
stmts,
})),
..arrow
}
}
box ast::BlockStmtOrExpr::Expr(expr) => {
let mut stmts = Vec::with_capacity(2);
if !scoped_idents.is_empty() {
stmts.push(create_use_lexical_scope(use_lexical_scope, scoped_idents));
}
stmts.push(create_return_stmt(expr));
ast::ArrowExpr {
body: Box::new(ast::BlockStmtOrExpr::BlockStmt(ast::BlockStmt {
span: DUMMY_SP,
stmts,
})),
..arrow
}
}
}
match arrow.body {
box ast::BlockStmtOrExpr::BlockStmt(mut block) => {
let mut stmts = Vec::with_capacity(1 + block.stmts.len());
stmts.push(create_use_lexical_scope(use_lexical_scope, scoped_idents));
stmts.append(&mut block.stmts);
ast::ArrowExpr {
body: Box::new(ast::BlockStmtOrExpr::BlockStmt(ast::BlockStmt {
span: DUMMY_SP,
stmts,
})),
..arrow
}
}
box ast::BlockStmtOrExpr::Expr(expr) => {
let mut stmts = Vec::with_capacity(2);
if !scoped_idents.is_empty() {
stmts.push(create_use_lexical_scope(use_lexical_scope, scoped_idents));
}
stmts.push(create_return_stmt(expr));
ast::ArrowExpr {
body: Box::new(ast::BlockStmtOrExpr::BlockStmt(ast::BlockStmt {
span: DUMMY_SP,
stmts,
})),
..arrow
}
}
}
}
fn transform_fn(node: ast::FnExpr, use_lexical_scope: &Id, scoped_idents: &[Id]) -> ast::FnExpr {
let mut stmts = Vec::with_capacity(
1 + node
.function
.body
.as_ref()
.map_or(0, |body| body.stmts.len()),
);
if !scoped_idents.is_empty() {
stmts.push(create_use_lexical_scope(use_lexical_scope, scoped_idents));
}
if let Some(mut body) = node.function.body {
stmts.append(&mut body.stmts);
}
ast::FnExpr {
function: Box::new(ast::Function {
body: Some(ast::BlockStmt {
span: DUMMY_SP,
stmts,
}),
..*node.function
}),
..node
}
let mut stmts = Vec::with_capacity(
1 + node
.function
.body
.as_ref()
.map_or(0, |body| body.stmts.len()),
);
if !scoped_idents.is_empty() {
stmts.push(create_use_lexical_scope(use_lexical_scope, scoped_idents));
}
if let Some(mut body) = node.function.body {
stmts.append(&mut body.stmts);
}
ast::FnExpr {
function: Box::new(ast::Function {
body: Some(ast::BlockStmt {
span: DUMMY_SP,
stmts,
}),
..*node.function
}),
..node
}
}
pub const fn create_return_stmt(expr: Box<ast::Expr>) -> ast::Stmt {
ast::Stmt::Return(ast::ReturnStmt {
arg: Some(expr),
span: DUMMY_SP,
})
ast::Stmt::Return(ast::ReturnStmt {
arg: Some(expr),
span: DUMMY_SP,
})
}
fn create_use_lexical_scope(use_lexical_scope: &Id, scoped_idents: &[Id]) -> ast::Stmt {
ast::Stmt::Decl(ast::Decl::Var(Box::new(ast::VarDecl {
span: DUMMY_SP,
declare: false,
kind: ast::VarDeclKind::Const,
decls: vec![ast::VarDeclarator {
definite: false,
span: DUMMY_SP,
init: Some(Box::new(ast::Expr::Call(ast::CallExpr {
callee: ast::Callee::Expr(Box::new(ast::Expr::Ident(new_ident_from_id(
use_lexical_scope,
)))),
span: DUMMY_SP,
type_args: None,
args: vec![],
}))),
name: ast::Pat::Array(ast::ArrayPat {
span: DUMMY_SP,
optional: false,
type_ann: None,
elems: scoped_idents
.iter()
.map(|id| {
Some(ast::Pat::Ident(ast::BindingIdent::from(new_ident_from_id(
id,
))))
})
.collect(),
}),
}],
})))
ast::Stmt::Decl(ast::Decl::Var(Box::new(ast::VarDecl {
span: DUMMY_SP,
declare: false,
kind: ast::VarDeclKind::Const,
decls: vec![ast::VarDeclarator {
definite: false,
span: DUMMY_SP,
init: Some(Box::new(ast::Expr::Call(ast::CallExpr {
callee: ast::Callee::Expr(Box::new(ast::Expr::Ident(new_ident_from_id(
use_lexical_scope,
)))),
span: DUMMY_SP,
type_args: None,
args: vec![],
}))),
name: ast::Pat::Array(ast::ArrayPat {
span: DUMMY_SP,
optional: false,
type_ann: None,
elems: scoped_idents
.iter()
.map(|id| {
Some(ast::Pat::Ident(ast::BindingIdent::from(new_ident_from_id(
id,
))))
})
.collect(),
}),
}],
})))
}

View File

@@ -7,429 +7,429 @@ use swc_ecmascript::utils::private_ident;
use swc_ecmascript::visit::{noop_visit_type, visit_expr, visit_stmt, Visit, VisitWith};
macro_rules! id {
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
}
pub type Id = (JsWord, SyntaxContext);
pub fn new_ident_from_id(id: &Id) -> ast::Ident {
ast::Ident::new(
id.0.clone(),
Span {
lo: BytePos(0),
hi: BytePos(0),
ctxt: id.1,
},
)
ast::Ident::new(
id.0.clone(),
Span {
lo: BytePos(0),
hi: BytePos(0),
ctxt: id.1,
},
)
}
#[derive(Eq, PartialEq, Clone, Copy)]
pub enum ImportKind {
Named,
All,
Default,
Named,
All,
Default,
}
#[derive(Clone)]
pub struct Import {
pub source: JsWord,
pub specifier: JsWord,
pub kind: ImportKind,
pub synthetic: bool,
pub asserts: Option<Box<ast::ObjectLit>>,
pub source: JsWord,
pub specifier: JsWord,
pub kind: ImportKind,
pub synthetic: bool,
pub asserts: Option<Box<ast::ObjectLit>>,
}
pub struct GlobalCollect {
pub synthetic: Vec<(Id, Import)>,
pub imports: HashMap<Id, Import>,
pub exports: HashMap<Id, Option<JsWord>>,
pub root: HashMap<Id, Span>,
pub synthetic: Vec<(Id, Import)>,
pub imports: HashMap<Id, Import>,
pub exports: HashMap<Id, Option<JsWord>>,
pub root: HashMap<Id, Span>,
rev_imports: HashMap<(JsWord, JsWord), Id>,
in_export_decl: bool,
rev_imports: HashMap<(JsWord, JsWord), Id>,
in_export_decl: bool,
}
pub fn global_collect(module: &ast::Module) -> GlobalCollect {
let mut collect = GlobalCollect {
synthetic: vec![],
imports: HashMap::with_capacity(16),
exports: HashMap::with_capacity(16),
let mut collect = GlobalCollect {
synthetic: vec![],
imports: HashMap::with_capacity(16),
exports: HashMap::with_capacity(16),
root: HashMap::with_capacity(16),
rev_imports: HashMap::with_capacity(16),
root: HashMap::with_capacity(16),
rev_imports: HashMap::with_capacity(16),
in_export_decl: false,
};
module.visit_with(&mut collect);
collect
in_export_decl: false,
};
module.visit_with(&mut collect);
collect
}
impl GlobalCollect {
pub fn get_imported_local(&self, specifier: &JsWord, source: &JsWord) -> Option<Id> {
self.imports
.iter()
.find(|(_, import)| &import.specifier == specifier && &import.source == source)
.map(|s| s.0.clone())
}
pub fn get_imported_local(&self, specifier: &JsWord, source: &JsWord) -> Option<Id> {
self.imports
.iter()
.find(|(_, import)| &import.specifier == specifier && &import.source == source)
.map(|s| s.0.clone())
}
pub fn is_global(&self, local: &Id) -> bool {
if self.imports.contains_key(local) {
return true;
}
if self.exports.contains_key(local) {
return true;
}
if self.root.contains_key(local) {
return true;
}
false
}
pub fn is_global(&self, local: &Id) -> bool {
if self.imports.contains_key(local) {
return true;
}
if self.exports.contains_key(local) {
return true;
}
if self.root.contains_key(local) {
return true;
}
false
}
pub fn import(&mut self, specifier: &JsWord, source: &JsWord) -> Id {
self.rev_imports
.get(&(specifier.clone(), source.clone()))
.cloned()
.map_or_else(
|| {
let local = id!(private_ident!(specifier));
self.add_import(
local.clone(),
Import {
source: source.clone(),
specifier: specifier.clone(),
kind: ImportKind::Named,
synthetic: true,
asserts: None,
},
);
local
},
|local| local,
)
}
pub fn import(&mut self, specifier: &JsWord, source: &JsWord) -> Id {
self.rev_imports
.get(&(specifier.clone(), source.clone()))
.cloned()
.map_or_else(
|| {
let local = id!(private_ident!(specifier));
self.add_import(
local.clone(),
Import {
source: source.clone(),
specifier: specifier.clone(),
kind: ImportKind::Named,
synthetic: true,
asserts: None,
},
);
local
},
|local| local,
)
}
pub fn add_import(&mut self, local: Id, import: Import) {
if import.synthetic {
self.synthetic.push((local.clone(), import.clone()));
}
self.rev_imports.insert(
(import.specifier.clone(), import.source.clone()),
local.clone(),
);
self.imports.insert(local, import);
}
pub fn add_import(&mut self, local: Id, import: Import) {
if import.synthetic {
self.synthetic.push((local.clone(), import.clone()));
}
self.rev_imports.insert(
(import.specifier.clone(), import.source.clone()),
local.clone(),
);
self.imports.insert(local, import);
}
pub fn add_export(&mut self, local: Id, exported: Option<JsWord>) -> bool {
if let std::collections::hash_map::Entry::Vacant(e) = self.exports.entry(local) {
e.insert(exported);
true
} else {
false
}
}
pub fn add_export(&mut self, local: Id, exported: Option<JsWord>) -> bool {
if let std::collections::hash_map::Entry::Vacant(e) = self.exports.entry(local) {
e.insert(exported);
true
} else {
false
}
}
}
impl Visit for GlobalCollect {
noop_visit_type!();
noop_visit_type!();
fn visit_module_item(&mut self, node: &ast::ModuleItem) {
if let ast::ModuleItem::Stmt(ast::Stmt::Decl(decl)) = node {
match decl {
ast::Decl::Fn(function) => {
self.root.insert(id!(function.ident), function.ident.span);
}
ast::Decl::Class(class) => {
self.root.insert(id!(class.ident), class.ident.span);
}
ast::Decl::Var(var) => {
for decl in &var.decls {
let mut identifiers: Vec<(Id, Span)> = vec![];
collect_from_pat(&decl.name, &mut identifiers);
self.root.extend(identifiers.into_iter());
}
}
ast::Decl::TsEnum(enu) => {
self.root.insert(id!(enu.id), enu.id.span);
}
_ => {}
}
} else {
node.visit_children_with(self);
}
}
fn visit_module_item(&mut self, node: &ast::ModuleItem) {
if let ast::ModuleItem::Stmt(ast::Stmt::Decl(decl)) = node {
match decl {
ast::Decl::Fn(function) => {
self.root.insert(id!(function.ident), function.ident.span);
}
ast::Decl::Class(class) => {
self.root.insert(id!(class.ident), class.ident.span);
}
ast::Decl::Var(var) => {
for decl in &var.decls {
let mut identifiers: Vec<(Id, Span)> = vec![];
collect_from_pat(&decl.name, &mut identifiers);
self.root.extend(identifiers.into_iter());
}
}
ast::Decl::TsEnum(enu) => {
self.root.insert(id!(enu.id), enu.id.span);
}
_ => {}
}
} else {
node.visit_children_with(self);
}
}
fn visit_import_decl(&mut self, node: &ast::ImportDecl) {
for specifier in &node.specifiers {
match specifier {
ast::ImportSpecifier::Named(named) => {
let imported = match &named.imported {
Some(ast::ModuleExportName::Ident(ident)) => ident.sym.clone(),
_ => named.local.sym.clone(),
};
self.add_import(
id!(named.local),
Import {
source: node.src.value.clone(),
specifier: imported,
kind: ImportKind::Named,
synthetic: false,
asserts: node.asserts.clone(),
},
);
}
ast::ImportSpecifier::Default(default) => {
self.add_import(
id!(default.local),
Import {
source: node.src.value.clone(),
specifier: js_word!("default"),
kind: ImportKind::Default,
synthetic: false,
asserts: node.asserts.clone(),
},
);
}
ast::ImportSpecifier::Namespace(namespace) => {
self.add_import(
id!(namespace.local),
Import {
source: node.src.value.clone(),
specifier: "*".into(),
kind: ImportKind::All,
synthetic: false,
asserts: node.asserts.clone(),
},
);
}
}
}
}
fn visit_import_decl(&mut self, node: &ast::ImportDecl) {
for specifier in &node.specifiers {
match specifier {
ast::ImportSpecifier::Named(named) => {
let imported = match &named.imported {
Some(ast::ModuleExportName::Ident(ident)) => ident.sym.clone(),
_ => named.local.sym.clone(),
};
self.add_import(
id!(named.local),
Import {
source: node.src.value.clone(),
specifier: imported,
kind: ImportKind::Named,
synthetic: false,
asserts: node.asserts.clone(),
},
);
}
ast::ImportSpecifier::Default(default) => {
self.add_import(
id!(default.local),
Import {
source: node.src.value.clone(),
specifier: js_word!("default"),
kind: ImportKind::Default,
synthetic: false,
asserts: node.asserts.clone(),
},
);
}
ast::ImportSpecifier::Namespace(namespace) => {
self.add_import(
id!(namespace.local),
Import {
source: node.src.value.clone(),
specifier: "*".into(),
kind: ImportKind::All,
synthetic: false,
asserts: node.asserts.clone(),
},
);
}
}
}
}
fn visit_named_export(&mut self, node: &ast::NamedExport) {
if node.src.is_some() {
return;
}
fn visit_named_export(&mut self, node: &ast::NamedExport) {
if node.src.is_some() {
return;
}
for specifier in &node.specifiers {
match specifier {
ast::ExportSpecifier::Named(named) => {
let local = match &named.orig {
ast::ModuleExportName::Ident(ident) => Some(id!(ident)),
_ => None,
};
let exported = match &named.exported {
Some(ast::ModuleExportName::Ident(exported)) => Some(exported.sym.clone()),
_ => None,
};
if let Some(local) = local {
self.add_export(local, exported);
}
}
ast::ExportSpecifier::Default(default) => {
self.exports
.entry(id!(default.exported))
.or_insert(Some(js_word!("default")));
}
ast::ExportSpecifier::Namespace(namespace) => {
if let ast::ModuleExportName::Ident(ident) = &namespace.name {
self.exports
.entry(id!(ident))
.or_insert_with(|| Some("*".into()));
}
}
}
}
}
for specifier in &node.specifiers {
match specifier {
ast::ExportSpecifier::Named(named) => {
let local = match &named.orig {
ast::ModuleExportName::Ident(ident) => Some(id!(ident)),
_ => None,
};
let exported = match &named.exported {
Some(ast::ModuleExportName::Ident(exported)) => Some(exported.sym.clone()),
_ => None,
};
if let Some(local) = local {
self.add_export(local, exported);
}
}
ast::ExportSpecifier::Default(default) => {
self.exports
.entry(id!(default.exported))
.or_insert(Some(js_word!("default")));
}
ast::ExportSpecifier::Namespace(namespace) => {
if let ast::ModuleExportName::Ident(ident) = &namespace.name {
self.exports
.entry(id!(ident))
.or_insert_with(|| Some("*".into()));
}
}
}
}
}
fn visit_export_decl(&mut self, node: &ast::ExportDecl) {
match &node.decl {
ast::Decl::TsEnum(enu) => {
self.add_export(id!(enu.id), None);
}
ast::Decl::Class(class) => {
self.add_export(id!(class.ident), None);
}
ast::Decl::Fn(func) => {
self.add_export(id!(func.ident), None);
}
ast::Decl::Var(var) => {
for decl in &var.decls {
self.in_export_decl = true;
decl.name.visit_with(self);
self.in_export_decl = false;
fn visit_export_decl(&mut self, node: &ast::ExportDecl) {
match &node.decl {
ast::Decl::TsEnum(enu) => {
self.add_export(id!(enu.id), None);
}
ast::Decl::Class(class) => {
self.add_export(id!(class.ident), None);
}
ast::Decl::Fn(func) => {
self.add_export(id!(func.ident), None);
}
ast::Decl::Var(var) => {
for decl in &var.decls {
self.in_export_decl = true;
decl.name.visit_with(self);
self.in_export_decl = false;
decl.init.visit_with(self);
}
}
_ => {}
}
}
decl.init.visit_with(self);
}
}
_ => {}
}
}
fn visit_export_default_decl(&mut self, node: &ast::ExportDefaultDecl) {
match &node.decl {
ast::DefaultDecl::Class(class) => {
if let Some(ident) = &class.ident {
self.add_export(id!(ident), Some(js_word!("default")));
}
}
ast::DefaultDecl::Fn(func) => {
if let Some(ident) = &func.ident {
self.add_export(id!(ident), Some(js_word!("default")));
}
}
_ => {
unreachable!("unsupported export default declaration");
}
};
}
fn visit_export_default_decl(&mut self, node: &ast::ExportDefaultDecl) {
match &node.decl {
ast::DefaultDecl::Class(class) => {
if let Some(ident) = &class.ident {
self.add_export(id!(ident), Some(js_word!("default")));
}
}
ast::DefaultDecl::Fn(func) => {
if let Some(ident) = &func.ident {
self.add_export(id!(ident), Some(js_word!("default")));
}
}
_ => {
unreachable!("unsupported export default declaration");
}
};
}
fn visit_binding_ident(&mut self, node: &ast::BindingIdent) {
if self.in_export_decl {
self.add_export(id!(node.id), None);
}
}
fn visit_binding_ident(&mut self, node: &ast::BindingIdent) {
if self.in_export_decl {
self.add_export(id!(node.id), None);
}
}
fn visit_assign_pat_prop(&mut self, node: &ast::AssignPatProp) {
if self.in_export_decl {
self.add_export(id!(node.key), None);
}
}
fn visit_assign_pat_prop(&mut self, node: &ast::AssignPatProp) {
if self.in_export_decl {
self.add_export(id!(node.key), None);
}
}
}
#[derive(Debug)]
enum ExprOrSkip {
Expr,
Skip,
Expr,
Skip,
}
#[derive(Debug)]
pub struct IdentCollector {
pub local_idents: HashSet<Id>,
pub use_h: bool,
pub use_fragment: bool,
pub local_idents: HashSet<Id>,
pub use_h: bool,
pub use_fragment: bool,
expr_ctxt: Vec<ExprOrSkip>,
expr_ctxt: Vec<ExprOrSkip>,
}
impl IdentCollector {
pub fn new() -> Self {
Self {
local_idents: HashSet::new(),
expr_ctxt: Vec::with_capacity(32),
use_h: false,
use_fragment: false,
}
}
pub fn new() -> Self {
Self {
local_idents: HashSet::new(),
expr_ctxt: Vec::with_capacity(32),
use_h: false,
use_fragment: false,
}
}
pub fn get_words(self) -> Vec<Id> {
let mut local_idents: Vec<Id> = self.local_idents.into_iter().collect();
local_idents.sort();
local_idents
}
pub fn get_words(self) -> Vec<Id> {
let mut local_idents: Vec<Id> = self.local_idents.into_iter().collect();
local_idents.sort();
local_idents
}
}
impl Visit for IdentCollector {
noop_visit_type!();
noop_visit_type!();
fn visit_expr(&mut self, node: &ast::Expr) {
self.expr_ctxt.push(ExprOrSkip::Expr);
visit_expr(self, node);
self.expr_ctxt.pop();
}
fn visit_expr(&mut self, node: &ast::Expr) {
self.expr_ctxt.push(ExprOrSkip::Expr);
visit_expr(self, node);
self.expr_ctxt.pop();
}
fn visit_stmt(&mut self, node: &ast::Stmt) {
self.expr_ctxt.push(ExprOrSkip::Skip);
visit_stmt(self, node);
self.expr_ctxt.pop();
}
fn visit_stmt(&mut self, node: &ast::Stmt) {
self.expr_ctxt.push(ExprOrSkip::Skip);
visit_stmt(self, node);
self.expr_ctxt.pop();
}
fn visit_jsx_element(&mut self, node: &ast::JSXElement) {
self.use_h = true;
node.visit_children_with(self);
}
fn visit_jsx_element(&mut self, node: &ast::JSXElement) {
self.use_h = true;
node.visit_children_with(self);
}
fn visit_jsx_fragment(&mut self, node: &ast::JSXFragment) {
self.use_h = true;
self.use_fragment = true;
node.visit_children_with(self);
}
fn visit_jsx_fragment(&mut self, node: &ast::JSXFragment) {
self.use_h = true;
self.use_fragment = true;
node.visit_children_with(self);
}
fn visit_jsx_element_name(&mut self, node: &ast::JSXElementName) {
if let ast::JSXElementName::Ident(ref ident) = node {
let ident_name = ident.sym.as_ref().chars().next();
if let Some('A'..='Z') = ident_name {
} else {
return;
}
}
fn visit_jsx_element_name(&mut self, node: &ast::JSXElementName) {
if let ast::JSXElementName::Ident(ref ident) = node {
let ident_name = ident.sym.as_ref().chars().next();
if let Some('A'..='Z') = ident_name {
} else {
return;
}
}
node.visit_children_with(self);
}
fn visit_jsx_attr(&mut self, node: &ast::JSXAttr) {
self.expr_ctxt.push(ExprOrSkip::Skip);
node.visit_children_with(self);
self.expr_ctxt.pop();
}
node.visit_children_with(self);
}
fn visit_jsx_attr(&mut self, node: &ast::JSXAttr) {
self.expr_ctxt.push(ExprOrSkip::Skip);
node.visit_children_with(self);
self.expr_ctxt.pop();
}
fn visit_ident(&mut self, node: &ast::Ident) {
if matches!(self.expr_ctxt.last(), Some(ExprOrSkip::Expr))
&& node.span.ctxt() != SyntaxContext::empty()
{
self.local_idents.insert(id!(node));
}
}
fn visit_ident(&mut self, node: &ast::Ident) {
if matches!(self.expr_ctxt.last(), Some(ExprOrSkip::Expr))
&& node.span.ctxt() != SyntaxContext::empty()
{
self.local_idents.insert(id!(node));
}
}
fn visit_key_value_prop(&mut self, node: &ast::KeyValueProp) {
self.expr_ctxt.push(ExprOrSkip::Skip);
node.visit_children_with(self);
self.expr_ctxt.pop();
}
fn visit_key_value_prop(&mut self, node: &ast::KeyValueProp) {
self.expr_ctxt.push(ExprOrSkip::Skip);
node.visit_children_with(self);
self.expr_ctxt.pop();
}
}
pub fn collect_from_pat(pat: &ast::Pat, identifiers: &mut Vec<(Id, Span)>) -> bool {
match pat {
ast::Pat::Ident(ident) => {
identifiers.push((id!(ident.id), ident.id.span));
true
}
ast::Pat::Array(array) => {
for el in array.elems.iter().flatten() {
collect_from_pat(el, identifiers);
}
false
}
ast::Pat::Rest(rest) => {
if let ast::Pat::Ident(ident) = rest.arg.as_ref() {
identifiers.push((id!(ident.id), ident.id.span));
}
false
}
ast::Pat::Assign(expr) => {
if let ast::Pat::Ident(ident) = expr.left.as_ref() {
identifiers.push((id!(ident.id), ident.id.span));
}
false
}
ast::Pat::Object(obj) => {
for prop in &obj.props {
match prop {
ast::ObjectPatProp::Assign(ref v) => {
identifiers.push((id!(v.key), v.key.span));
}
ast::ObjectPatProp::KeyValue(ref v) => {
collect_from_pat(&v.value, identifiers);
}
ast::ObjectPatProp::Rest(ref v) => {
if let ast::Pat::Ident(ident) = v.arg.as_ref() {
identifiers.push((id!(ident.id), ident.id.span));
}
}
}
}
false
}
_ => false,
}
match pat {
ast::Pat::Ident(ident) => {
identifiers.push((id!(ident.id), ident.id.span));
true
}
ast::Pat::Array(array) => {
for el in array.elems.iter().flatten() {
collect_from_pat(el, identifiers);
}
false
}
ast::Pat::Rest(rest) => {
if let ast::Pat::Ident(ident) = rest.arg.as_ref() {
identifiers.push((id!(ident.id), ident.id.span));
}
false
}
ast::Pat::Assign(expr) => {
if let ast::Pat::Ident(ident) = expr.left.as_ref() {
identifiers.push((id!(ident.id), ident.id.span));
}
false
}
ast::Pat::Object(obj) => {
for prop in &obj.props {
match prop {
ast::ObjectPatProp::Assign(ref v) => {
identifiers.push((id!(v.key), v.key.span));
}
ast::ObjectPatProp::KeyValue(ref v) => {
collect_from_pat(&v.value, identifiers);
}
ast::ObjectPatProp::Rest(ref v) => {
if let ast::Pat::Ident(ident) = v.arg.as_ref() {
identifiers.push((id!(ident.id), ident.id.span));
}
}
}
}
false
}
_ => false,
}
}

View File

@@ -4,80 +4,80 @@ use swc_common::DUMMY_SP;
use swc_ecmascript::ast;
use swc_ecmascript::visit::{VisitMut, VisitMutWith};
pub struct ConstReplacerVisitor {
pub is_server: bool,
pub is_dev: bool,
pub is_server_ident: Option<Id>,
pub is_browser_ident: Option<Id>,
pub is_dev_ident: Option<Id>,
pub is_server: bool,
pub is_dev: bool,
pub is_server_ident: Option<Id>,
pub is_browser_ident: Option<Id>,
pub is_dev_ident: Option<Id>,
}
impl ConstReplacerVisitor {
pub fn new(is_server: bool, is_dev: bool, global_collector: &GlobalCollect) -> Self {
Self {
is_server,
is_dev,
is_server_ident: global_collector
.get_imported_local(&IS_SERVER, &BUILDER_IO_QWIK_BUILD),
is_browser_ident: global_collector
.get_imported_local(&IS_BROWSER, &BUILDER_IO_QWIK_BUILD),
is_dev_ident: global_collector.get_imported_local(&IS_DEV, &BUILDER_IO_QWIK_BUILD),
}
}
pub fn new(is_server: bool, is_dev: bool, global_collector: &GlobalCollect) -> Self {
Self {
is_server,
is_dev,
is_server_ident: global_collector
.get_imported_local(&IS_SERVER, &BUILDER_IO_QWIK_BUILD),
is_browser_ident: global_collector
.get_imported_local(&IS_BROWSER, &BUILDER_IO_QWIK_BUILD),
is_dev_ident: global_collector.get_imported_local(&IS_DEV, &BUILDER_IO_QWIK_BUILD),
}
}
}
macro_rules! id_eq {
($ident: expr, $cid: expr) => {
if let Some(cid) = $cid {
cid.0 == $ident.sym && cid.1 == $ident.span.ctxt()
} else {
false
}
};
($ident: expr, $cid: expr) => {
if let Some(cid) = $cid {
cid.0 == $ident.sym && cid.1 == $ident.span.ctxt()
} else {
false
}
};
}
enum ConstVariable {
IsServer,
IsBrowser,
IsDev,
None,
IsServer,
IsBrowser,
IsDev,
None,
}
impl VisitMut for ConstReplacerVisitor {
fn visit_mut_expr(&mut self, node: &mut ast::Expr) {
let mode = match node {
ast::Expr::Ident(ref ident) => {
if id_eq!(ident, &self.is_server_ident) {
ConstVariable::IsServer
} else if id_eq!(ident, &self.is_browser_ident) {
ConstVariable::IsBrowser
} else if id_eq!(ident, &self.is_dev_ident) {
ConstVariable::IsDev
} else {
ConstVariable::None
}
}
_ => ConstVariable::None,
};
match mode {
ConstVariable::IsServer => {
*node = ast::Expr::Lit(ast::Lit::Bool(ast::Bool {
span: DUMMY_SP,
value: self.is_server,
}))
}
ConstVariable::IsBrowser => {
*node = ast::Expr::Lit(ast::Lit::Bool(ast::Bool {
span: DUMMY_SP,
value: !self.is_server,
}))
}
ConstVariable::IsDev => {
*node = ast::Expr::Lit(ast::Lit::Bool(ast::Bool {
span: DUMMY_SP,
value: self.is_dev,
}))
}
ConstVariable::None => {
node.visit_mut_children_with(self);
}
}
}
fn visit_mut_expr(&mut self, node: &mut ast::Expr) {
let mode = match node {
ast::Expr::Ident(ref ident) => {
if id_eq!(ident, &self.is_server_ident) {
ConstVariable::IsServer
} else if id_eq!(ident, &self.is_browser_ident) {
ConstVariable::IsBrowser
} else if id_eq!(ident, &self.is_dev_ident) {
ConstVariable::IsDev
} else {
ConstVariable::None
}
}
_ => ConstVariable::None,
};
match mode {
ConstVariable::IsServer => {
*node = ast::Expr::Lit(ast::Lit::Bool(ast::Bool {
span: DUMMY_SP,
value: self.is_server,
}))
}
ConstVariable::IsBrowser => {
*node = ast::Expr::Lit(ast::Lit::Bool(ast::Bool {
span: DUMMY_SP,
value: !self.is_server,
}))
}
ConstVariable::IsDev => {
*node = ast::Expr::Lit(ast::Lit::Bool(ast::Bool {
span: DUMMY_SP,
value: self.is_dev,
}))
}
ConstVariable::None => {
node.visit_mut_children_with(self);
}
}
}
}

View File

@@ -8,185 +8,185 @@ use swc_atoms::JsWord;
use lazy_static::lazy_static;
lazy_static! {
static ref ENTRY_HOOKS: JsWord = JsWord::from("entry_hooks");
static ref ENTRY_SERVER: JsWord = JsWord::from("entry_server");
static ref ENTRY_HOOKS: JsWord = JsWord::from("entry_hooks");
static ref ENTRY_SERVER: JsWord = JsWord::from("entry_server");
}
// EntryStrategies
#[derive(Debug, Serialize, Copy, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EntryStrategy {
Inline,
Hoist,
Single,
Hook,
Component,
Smart,
Inline,
Hoist,
Single,
Hook,
Component,
Smart,
}
pub trait EntryPolicy: Send + Sync {
fn get_entry_for_sym(
&self,
hash: &str,
location: &PathData,
context: &[String],
hook_data: &HookData,
) -> Option<JsWord>;
fn get_entry_for_sym(
&self,
hash: &str,
location: &PathData,
context: &[String],
hook_data: &HookData,
) -> Option<JsWord>;
}
#[derive(Default, Clone)]
pub struct InlineStrategy;
impl EntryPolicy for InlineStrategy {
fn get_entry_for_sym(
&self,
_hash: &str,
_path: &PathData,
_context: &[String],
_hook_data: &HookData,
) -> Option<JsWord> {
Some(ENTRY_HOOKS.clone())
}
fn get_entry_for_sym(
&self,
_hash: &str,
_path: &PathData,
_context: &[String],
_hook_data: &HookData,
) -> Option<JsWord> {
Some(ENTRY_HOOKS.clone())
}
}
#[derive(Clone)]
pub struct SingleStrategy {
map: Option<HashMap<String, JsWord>>,
map: Option<HashMap<String, JsWord>>,
}
impl SingleStrategy {
pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
Self { map }
}
pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
Self { map }
}
}
impl EntryPolicy for SingleStrategy {
fn get_entry_for_sym(
&self,
hash: &str,
_path: &PathData,
_context: &[String],
_hook_data: &HookData,
) -> Option<JsWord> {
if let Some(map) = &self.map {
let entry = map.get(hash);
if let Some(entry) = entry {
return Some(entry.clone());
}
}
Some(ENTRY_HOOKS.clone())
}
fn get_entry_for_sym(
&self,
hash: &str,
_path: &PathData,
_context: &[String],
_hook_data: &HookData,
) -> Option<JsWord> {
if let Some(map) = &self.map {
let entry = map.get(hash);
if let Some(entry) = entry {
return Some(entry.clone());
}
}
Some(ENTRY_HOOKS.clone())
}
}
#[derive(Clone)]
pub struct PerHookStrategy {
map: Option<HashMap<String, JsWord>>,
map: Option<HashMap<String, JsWord>>,
}
impl PerHookStrategy {
pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
Self { map }
}
pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
Self { map }
}
}
impl EntryPolicy for PerHookStrategy {
fn get_entry_for_sym(
&self,
hash: &str,
_path: &PathData,
_context: &[String],
_hook_data: &HookData,
) -> Option<JsWord> {
if let Some(map) = &self.map {
let entry = map.get(hash);
if let Some(entry) = entry {
return Some(entry.clone());
}
}
None
}
fn get_entry_for_sym(
&self,
hash: &str,
_path: &PathData,
_context: &[String],
_hook_data: &HookData,
) -> Option<JsWord> {
if let Some(map) = &self.map {
let entry = map.get(hash);
if let Some(entry) = entry {
return Some(entry.clone());
}
}
None
}
}
#[derive(Clone)]
pub struct PerComponentStrategy {
map: Option<HashMap<String, JsWord>>,
map: Option<HashMap<String, JsWord>>,
}
impl PerComponentStrategy {
pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
Self { map }
}
pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
Self { map }
}
}
impl EntryPolicy for PerComponentStrategy {
fn get_entry_for_sym(
&self,
hash: &str,
_path: &PathData,
context: &[String],
_hook_data: &HookData,
) -> Option<JsWord> {
if let Some(map) = &self.map {
let entry = map.get(hash);
if let Some(entry) = entry {
return Some(entry.clone());
}
}
context.first().map_or_else(
|| Some(ENTRY_HOOKS.clone()),
|root| Some(JsWord::from(["entry_", root].concat())),
)
}
fn get_entry_for_sym(
&self,
hash: &str,
_path: &PathData,
context: &[String],
_hook_data: &HookData,
) -> Option<JsWord> {
if let Some(map) = &self.map {
let entry = map.get(hash);
if let Some(entry) = entry {
return Some(entry.clone());
}
}
context.first().map_or_else(
|| Some(ENTRY_HOOKS.clone()),
|root| Some(JsWord::from(["entry_", root].concat())),
)
}
}
#[derive(Clone)]
pub struct SmartStrategy {
map: Option<HashMap<String, JsWord>>,
map: Option<HashMap<String, JsWord>>,
}
impl SmartStrategy {
pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
Self { map }
}
pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
Self { map }
}
}
impl EntryPolicy for SmartStrategy {
fn get_entry_for_sym(
&self,
hash: &str,
_path: &PathData,
context: &[String],
hook_data: &HookData,
) -> Option<JsWord> {
if hook_data.scoped_idents.is_empty()
&& (hook_data.ctx_kind != HookKind::Function || &hook_data.ctx_name == "event$")
{
return None;
}
if hook_data.ctx_name == *USE_SERVER_MOUNT {
return Some(ENTRY_SERVER.clone());
}
if let Some(map) = &self.map {
let entry = map.get(hash);
if let Some(entry) = entry {
return Some(entry.clone());
}
}
Some(context.first().map_or_else(
|| ENTRY_HOOKS.clone(),
|root| JsWord::from(["entry_", root].concat()),
))
}
fn get_entry_for_sym(
&self,
hash: &str,
_path: &PathData,
context: &[String],
hook_data: &HookData,
) -> Option<JsWord> {
if hook_data.scoped_idents.is_empty()
&& (hook_data.ctx_kind != HookKind::Function || &hook_data.ctx_name == "event$")
{
return None;
}
if hook_data.ctx_name == *USE_SERVER_MOUNT {
return Some(ENTRY_SERVER.clone());
}
if let Some(map) = &self.map {
let entry = map.get(hash);
if let Some(entry) = entry {
return Some(entry.clone());
}
}
Some(context.first().map_or_else(
|| ENTRY_HOOKS.clone(),
|root| JsWord::from(["entry_", root].concat()),
))
}
}
pub fn parse_entry_strategy(
strategy: &EntryStrategy,
manual_chunks: Option<HashMap<String, JsWord>>,
strategy: &EntryStrategy,
manual_chunks: Option<HashMap<String, JsWord>>,
) -> Box<dyn EntryPolicy> {
match strategy {
EntryStrategy::Inline | EntryStrategy::Hoist => Box::<InlineStrategy>::default(),
EntryStrategy::Hook => Box::new(PerHookStrategy::new(manual_chunks)),
EntryStrategy::Single => Box::new(SingleStrategy::new(manual_chunks)),
EntryStrategy::Component => Box::new(PerComponentStrategy::new(manual_chunks)),
EntryStrategy::Smart => Box::new(SmartStrategy::new(manual_chunks)),
}
match strategy {
EntryStrategy::Inline | EntryStrategy::Hoist => Box::<InlineStrategy>::default(),
EntryStrategy::Hook => Box::new(PerHookStrategy::new(manual_chunks)),
EntryStrategy::Single => Box::new(SingleStrategy::new(manual_chunks)),
EntryStrategy::Component => Box::new(PerComponentStrategy::new(manual_chunks)),
EntryStrategy::Smart => Box::new(SmartStrategy::new(manual_chunks)),
}
}

View File

@@ -1,13 +1,13 @@
use swc_common::errors::DiagnosticId;
pub enum Error {
FunctionReference = 2,
CanNotCapture,
DynamicImportInsideQhook,
MissingQrlImplementation,
FunctionReference = 2,
CanNotCapture,
DynamicImportInsideQhook,
MissingQrlImplementation,
}
pub fn get_diagnostic_id(err: Error) -> DiagnosticId {
let id = err as u32;
DiagnosticId::Error(format!("C{:02}", id))
let id = err as u32;
DiagnosticId::Error(format!("C{:02}", id))
}

View File

@@ -4,47 +4,47 @@ use swc_ecmascript::ast;
use swc_ecmascript::visit::VisitMut;
pub struct StripExportsVisitor<'a> {
pub filter_symbols: &'a [JsWord],
pub filter_symbols: &'a [JsWord],
}
impl<'a> StripExportsVisitor<'a> {
pub const fn new(filter_symbols: &'a [JsWord]) -> Self {
Self { filter_symbols }
}
pub const fn new(filter_symbols: &'a [JsWord]) -> Self {
Self { filter_symbols }
}
}
impl<'a> VisitMut for StripExportsVisitor<'a> {
fn visit_mut_module(&mut self, node: &mut ast::Module) {
for item in &mut node.body {
if let ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportDecl(decl)) = item {
match &decl.decl {
ast::Decl::Var(var) => {
if var.decls.len() == 1 {
if let Some(ast::VarDeclarator {
name: ast::Pat::Ident(ident),
..
}) = var.decls.first()
{
if self.filter_symbols.contains(&ident.id.sym) {
*item = empty_module_item(ident.id.clone());
}
}
}
}
ast::Decl::Fn(fn_decl) => {
if self.filter_symbols.contains(&fn_decl.ident.sym) {
*item = empty_module_item(fn_decl.ident.clone());
}
}
_ => {}
}
}
}
}
fn visit_mut_module(&mut self, node: &mut ast::Module) {
for item in &mut node.body {
if let ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportDecl(decl)) = item {
match &decl.decl {
ast::Decl::Var(var) => {
if var.decls.len() == 1 {
if let Some(ast::VarDeclarator {
name: ast::Pat::Ident(ident),
..
}) = var.decls.first()
{
if self.filter_symbols.contains(&ident.id.sym) {
*item = empty_module_item(ident.id.clone());
}
}
}
}
ast::Decl::Fn(fn_decl) => {
if self.filter_symbols.contains(&fn_decl.ident.sym) {
*item = empty_module_item(fn_decl.ident.clone());
}
}
_ => {}
}
}
}
}
}
fn empty_module_item(ident: ast::Ident) -> ast::ModuleItem {
ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportDecl(ast::ExportDecl {
ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportDecl(ast::ExportDecl {
span: DUMMY_SP,
decl: ast::Decl::Var(Box::new(ast::VarDecl {
span: DUMMY_SP,

View File

@@ -5,139 +5,139 @@ use swc_ecmascript::ast;
use swc_ecmascript::visit::{noop_visit_type, Visit, VisitWith};
macro_rules! id {
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
}
pub fn is_conditional_jsx(
expr: &ast::BlockStmtOrExpr,
jsx_functions: &HashSet<Id>,
immutable_function_cmp: &HashSet<Id>,
expr: &ast::BlockStmtOrExpr,
jsx_functions: &HashSet<Id>,
immutable_function_cmp: &HashSet<Id>,
) -> bool {
let mut collector = HasBranches::new(jsx_functions, immutable_function_cmp);
expr.visit_with(&mut collector);
collector.conditional
let mut collector = HasBranches::new(jsx_functions, immutable_function_cmp);
expr.visit_with(&mut collector);
collector.conditional
}
pub fn is_conditional_jsx_block(
expr: &ast::BlockStmt,
jsx_functions: &HashSet<Id>,
immutable_function_cmp: &HashSet<Id>,
expr: &ast::BlockStmt,
jsx_functions: &HashSet<Id>,
immutable_function_cmp: &HashSet<Id>,
) -> bool {
let mut collector = HasBranches::new(jsx_functions, immutable_function_cmp);
expr.visit_with(&mut collector);
collector.conditional
let mut collector = HasBranches::new(jsx_functions, immutable_function_cmp);
expr.visit_with(&mut collector);
collector.conditional
}
pub struct HasBranches<'a> {
under_conditional: i32,
jsx_functions: &'a HashSet<Id>,
immutable_function_cmp: &'a HashSet<Id>,
conditional: bool,
found_return: bool,
under_conditional: i32,
jsx_functions: &'a HashSet<Id>,
immutable_function_cmp: &'a HashSet<Id>,
conditional: bool,
found_return: bool,
}
impl<'a> HasBranches<'a> {
const fn new(jsx_functions: &'a HashSet<Id>, immutable_function_cmp: &'a HashSet<Id>) -> Self {
Self {
jsx_functions,
immutable_function_cmp,
under_conditional: 0,
conditional: false,
found_return: false,
}
}
const fn new(jsx_functions: &'a HashSet<Id>, immutable_function_cmp: &'a HashSet<Id>) -> Self {
Self {
jsx_functions,
immutable_function_cmp,
under_conditional: 0,
conditional: false,
found_return: false,
}
}
}
impl<'a> Visit for HasBranches<'a> {
noop_visit_type!();
noop_visit_type!();
fn visit_arrow_expr(&mut self, _: &ast::ArrowExpr) {}
fn visit_fn_expr(&mut self, _: &ast::FnExpr) {}
fn visit_fn_decl(&mut self, _: &ast::FnDecl) {}
fn visit_arrow_expr(&mut self, _: &ast::ArrowExpr) {}
fn visit_fn_expr(&mut self, _: &ast::FnExpr) {}
fn visit_fn_decl(&mut self, _: &ast::FnDecl) {}
fn visit_return_stmt(&mut self, node: &ast::ReturnStmt) {
node.visit_children_with(self);
self.found_return = true;
}
fn visit_return_stmt(&mut self, node: &ast::ReturnStmt) {
node.visit_children_with(self);
self.found_return = true;
}
fn visit_for_in_stmt(&mut self, node: &ast::ForInStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_for_in_stmt(&mut self, node: &ast::ForInStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_for_of_stmt(&mut self, node: &ast::ForOfStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_for_of_stmt(&mut self, node: &ast::ForOfStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_for_stmt(&mut self, node: &ast::ForStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_for_stmt(&mut self, node: &ast::ForStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_if_stmt(&mut self, node: &ast::IfStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_if_stmt(&mut self, node: &ast::IfStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_while_stmt(&mut self, node: &ast::WhileStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_while_stmt(&mut self, node: &ast::WhileStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_do_while_stmt(&mut self, node: &ast::DoWhileStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_do_while_stmt(&mut self, node: &ast::DoWhileStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_switch_stmt(&mut self, node: &ast::SwitchStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_switch_stmt(&mut self, node: &ast::SwitchStmt) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_cond_expr(&mut self, node: &ast::CondExpr) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_cond_expr(&mut self, node: &ast::CondExpr) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
}
fn visit_bin_expr(&mut self, node: &ast::BinExpr) {
if matches!(
node.op,
ast::BinaryOp::LogicalAnd | ast::BinaryOp::LogicalOr | ast::BinaryOp::NullishCoalescing
) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
} else {
node.visit_children_with(self);
}
}
fn visit_bin_expr(&mut self, node: &ast::BinExpr) {
if matches!(
node.op,
ast::BinaryOp::LogicalAnd | ast::BinaryOp::LogicalOr | ast::BinaryOp::NullishCoalescing
) {
self.under_conditional += 1;
node.visit_children_with(self);
self.under_conditional -= 1;
} else {
node.visit_children_with(self);
}
}
fn visit_call_expr(&mut self, node: &ast::CallExpr) {
if self.under_conditional > 0 || self.found_return {
if let ast::Callee::Expr(box ast::Expr::Ident(ident)) = &node.callee {
if self.jsx_functions.contains(&id!(ident)) {
let first_arg = node.args.first();
if let Some(name) = first_arg {
if let ast::Expr::Ident(jsx_id) = &*name.expr {
if !self.immutable_function_cmp.contains(&id!(jsx_id)) {
self.conditional = true;
}
}
}
}
}
}
node.visit_children_with(self);
}
fn visit_call_expr(&mut self, node: &ast::CallExpr) {
if self.under_conditional > 0 || self.found_return {
if let ast::Callee::Expr(box ast::Expr::Ident(ident)) = &node.callee {
if self.jsx_functions.contains(&id!(ident)) {
let first_arg = node.args.first();
if let Some(name) = first_arg {
if let ast::Expr::Ident(jsx_id) = &*name.expr {
if !self.immutable_function_cmp.contains(&id!(jsx_id)) {
self.conditional = true;
}
}
}
}
}
}
node.visit_children_with(self);
}
}

View File

@@ -9,202 +9,202 @@ use swc_ecmascript::codegen::text_writer::JsWriter;
use swc_ecmascript::transforms::fixer;
use swc_ecmascript::transforms::hygiene::hygiene_with_config;
use swc_ecmascript::{
utils::private_ident,
visit::{VisitMut, VisitMutWith},
utils::private_ident,
visit::{VisitMut, VisitMutWith},
};
macro_rules! id {
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
}
pub fn convert_inlined_fn(
mut expr: ast::Expr,
scoped_idents: Vec<Id>,
qqhook: &Id,
accept_call_expr: bool,
serialize_fn: bool,
mut expr: ast::Expr,
scoped_idents: Vec<Id>,
qqhook: &Id,
accept_call_expr: bool,
serialize_fn: bool,
) -> (Option<ast::Expr>, bool) {
let mut identifiers = HashMap::new();
let params: Vec<ast::Pat> = scoped_idents
.iter()
.enumerate()
.map(|(index, id)| {
let new_ident = private_ident!(format!("p{}", index));
identifiers.insert(id.clone(), ast::Expr::Ident(new_ident.clone()));
ast::Pat::Ident(ast::BindingIdent {
id: new_ident,
type_ann: None,
})
})
.collect();
let mut identifiers = HashMap::new();
let params: Vec<ast::Pat> = scoped_idents
.iter()
.enumerate()
.map(|(index, id)| {
let new_ident = private_ident!(format!("p{}", index));
identifiers.insert(id.clone(), ast::Expr::Ident(new_ident.clone()));
ast::Pat::Ident(ast::BindingIdent {
id: new_ident,
type_ann: None,
})
})
.collect();
if matches!(expr, ast::Expr::Arrow(_)) {
return (None, false);
}
if matches!(expr, ast::Expr::Arrow(_)) {
return (None, false);
}
// Replace identifier
let mut replace_identifiers = ReplaceIdentifiers::new(identifiers, accept_call_expr);
expr.visit_mut_with(&mut replace_identifiers);
// Replace identifier
let mut replace_identifiers = ReplaceIdentifiers::new(identifiers, accept_call_expr);
expr.visit_mut_with(&mut replace_identifiers);
if replace_identifiers.abort {
return (None, false);
}
if replace_identifiers.abort {
return (None, false);
}
let rendered_expr = render_expr(&expr);
if rendered_expr.len() > 150 {
return (None, false);
}
let rendered_expr = render_expr(&expr);
if rendered_expr.len() > 150 {
return (None, false);
}
if scoped_idents.is_empty() {
return (None, true);
}
if scoped_idents.is_empty() {
return (None, true);
}
// Generate stringified version
let rendered_str =
ast::ExprOrSpread::from(ast::Expr::Lit(ast::Lit::Str(ast::Str::from(rendered_expr))));
// Generate stringified version
let rendered_str =
ast::ExprOrSpread::from(ast::Expr::Lit(ast::Lit::Str(ast::Str::from(rendered_expr))));
// Wrap around arrow functions
let expr = ast::Expr::Arrow(ast::ArrowExpr {
body: Box::new(ast::BlockStmtOrExpr::Expr(Box::new(expr))),
is_async: false,
is_generator: false,
params,
return_type: None,
span: DUMMY_SP,
type_params: None,
});
// Wrap around arrow functions
let expr = ast::Expr::Arrow(ast::ArrowExpr {
body: Box::new(ast::BlockStmtOrExpr::Expr(Box::new(expr))),
is_async: false,
is_generator: false,
params,
return_type: None,
span: DUMMY_SP,
type_params: None,
});
let mut args = vec![
ast::ExprOrSpread::from(expr),
ast::ExprOrSpread::from(ast::Expr::Array(ast::ArrayLit {
span: DUMMY_SP,
elems: scoped_idents
.iter()
.map(|id| {
Some(ast::ExprOrSpread::from(ast::Expr::Ident(
new_ident_from_id(id),
)))
})
.collect(),
})),
];
let mut args = vec![
ast::ExprOrSpread::from(expr),
ast::ExprOrSpread::from(ast::Expr::Array(ast::ArrayLit {
span: DUMMY_SP,
elems: scoped_idents
.iter()
.map(|id| {
Some(ast::ExprOrSpread::from(ast::Expr::Ident(
new_ident_from_id(id),
)))
})
.collect(),
})),
];
if serialize_fn {
args.push(rendered_str)
}
if serialize_fn {
args.push(rendered_str)
}
(
Some(ast::Expr::Call(ast::CallExpr {
span: DUMMY_SP,
callee: ast::Callee::Expr(Box::new(ast::Expr::Ident(new_ident_from_id(qqhook)))),
type_args: None,
args,
})),
true,
)
(
Some(ast::Expr::Call(ast::CallExpr {
span: DUMMY_SP,
callee: ast::Callee::Expr(Box::new(ast::Expr::Ident(new_ident_from_id(qqhook)))),
type_args: None,
args,
})),
true,
)
}
struct ReplaceIdentifiers {
pub identifiers: HashMap<Id, ast::Expr>,
pub accept_call_expr: bool,
pub abort: bool,
pub identifiers: HashMap<Id, ast::Expr>,
pub accept_call_expr: bool,
pub abort: bool,
}
impl ReplaceIdentifiers {
const fn new(identifiers: HashMap<Id, ast::Expr>, accept_call_expr: bool) -> Self {
Self {
identifiers,
accept_call_expr,
abort: false,
}
}
const fn new(identifiers: HashMap<Id, ast::Expr>, accept_call_expr: bool) -> Self {
Self {
identifiers,
accept_call_expr,
abort: false,
}
}
}
impl VisitMut for ReplaceIdentifiers {
fn visit_mut_expr(&mut self, node: &mut ast::Expr) {
match node {
ast::Expr::Ident(ident) => {
if let Some(expr) = self.identifiers.get(&id!(ident)) {
*node = expr.clone();
}
}
_ => {
node.visit_mut_children_with(self);
}
}
}
fn visit_mut_expr(&mut self, node: &mut ast::Expr) {
match node {
ast::Expr::Ident(ident) => {
if let Some(expr) = self.identifiers.get(&id!(ident)) {
*node = expr.clone();
}
}
_ => {
node.visit_mut_children_with(self);
}
}
}
fn visit_mut_prop(&mut self, node: &mut ast::Prop) {
if let ast::Prop::Shorthand(short) = node {
if let Some(expr) = self.identifiers.get(&id!(short)) {
*node = ast::Prop::KeyValue(ast::KeyValueProp {
key: ast::PropName::Ident(short.clone()),
value: Box::new(expr.clone()),
});
}
}
node.visit_mut_children_with(self);
}
fn visit_mut_prop(&mut self, node: &mut ast::Prop) {
if let ast::Prop::Shorthand(short) = node {
if let Some(expr) = self.identifiers.get(&id!(short)) {
*node = ast::Prop::KeyValue(ast::KeyValueProp {
key: ast::PropName::Ident(short.clone()),
value: Box::new(expr.clone()),
});
}
}
node.visit_mut_children_with(self);
}
fn visit_mut_callee(&mut self, node: &mut ast::Callee) {
if !self.accept_call_expr || matches!(node, ast::Callee::Import(_)) {
self.abort = true;
} else {
node.visit_mut_children_with(self);
}
}
fn visit_mut_callee(&mut self, node: &mut ast::Callee) {
if !self.accept_call_expr || matches!(node, ast::Callee::Import(_)) {
self.abort = true;
} else {
node.visit_mut_children_with(self);
}
}
fn visit_mut_arrow_expr(&mut self, _: &mut ast::ArrowExpr) {
self.abort = true;
}
fn visit_mut_arrow_expr(&mut self, _: &mut ast::ArrowExpr) {
self.abort = true;
}
fn visit_mut_function(&mut self, _: &mut ast::Function) {
self.abort = true;
}
fn visit_mut_function(&mut self, _: &mut ast::Function) {
self.abort = true;
}
fn visit_mut_class_expr(&mut self, _: &mut ast::ClassExpr) {
self.abort = true;
}
fn visit_mut_class_expr(&mut self, _: &mut ast::ClassExpr) {
self.abort = true;
}
fn visit_mut_decorator(&mut self, _: &mut ast::Decorator) {
self.abort = true;
}
fn visit_mut_decorator(&mut self, _: &mut ast::Decorator) {
self.abort = true;
}
fn visit_mut_stmt(&mut self, _: &mut ast::Stmt) {
self.abort = true;
}
fn visit_mut_stmt(&mut self, _: &mut ast::Stmt) {
self.abort = true;
}
}
pub fn render_expr(expr: &ast::Expr) -> String {
let mut expr = expr.clone();
let mut buf = Vec::new();
let source_map = Lrc::new(SourceMap::default());
let writer = Box::new(JsWriter::new(Lrc::clone(&source_map), "\n", &mut buf, None));
let config = swc_ecmascript::codegen::Config {
minify: true,
target: ast::EsVersion::latest(),
ascii_only: false,
omit_last_semi: false,
};
let mut emitter = swc_ecmascript::codegen::Emitter {
cfg: config,
comments: None,
cm: Lrc::clone(&source_map),
wr: writer,
};
expr.visit_mut_with(&mut hygiene_with_config(Default::default()));
expr.visit_mut_with(&mut fixer(None));
emitter
.emit_module_item(&ast::ModuleItem::Stmt(ast::Stmt::Expr(ast::ExprStmt {
span: DUMMY_SP,
expr: Box::new(expr),
})))
.expect("Should emit");
let mut expr = expr.clone();
let mut buf = Vec::new();
let source_map = Lrc::new(SourceMap::default());
let writer = Box::new(JsWriter::new(Lrc::clone(&source_map), "\n", &mut buf, None));
let config = swc_ecmascript::codegen::Config {
minify: true,
target: ast::EsVersion::latest(),
ascii_only: false,
omit_last_semi: false,
};
let mut emitter = swc_ecmascript::codegen::Emitter {
cfg: config,
comments: None,
cm: Lrc::clone(&source_map),
wr: writer,
};
expr.visit_mut_with(&mut hygiene_with_config(Default::default()));
expr.visit_mut_with(&mut fixer(None));
emitter
.emit_module_item(&ast::ModuleItem::Stmt(ast::Stmt::Expr(ast::ExprStmt {
span: DUMMY_SP,
expr: Box::new(expr),
})))
.expect("Should emit");
str::from_utf8(&buf)
.expect("should be utf8")
.trim_end_matches(';')
.to_string()
str::from_utf8(&buf)
.expect("should be utf8")
.trim_end_matches(';')
.to_string()
}

View File

@@ -4,36 +4,36 @@ use swc_ecmascript::ast;
use swc_ecmascript::visit::{noop_visit_type, Visit};
macro_rules! id {
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
}
pub fn is_immutable_expr(
expr: &ast::Expr,
global: &GlobalCollect,
current_stack: Option<&Vec<IdPlusType>>,
expr: &ast::Expr,
global: &GlobalCollect,
current_stack: Option<&Vec<IdPlusType>>,
) -> bool {
let mut collector = ImmutableCollector::new(global, current_stack);
collector.visit_expr(expr);
collector.is_immutable
let mut collector = ImmutableCollector::new(global, current_stack);
collector.visit_expr(expr);
collector.is_immutable
}
pub struct ImmutableCollector<'a> {
global: &'a GlobalCollect,
immutable_idents: Option<&'a Vec<IdPlusType>>,
global: &'a GlobalCollect,
immutable_idents: Option<&'a Vec<IdPlusType>>,
pub is_immutable: bool,
pub is_immutable: bool,
}
impl<'a> ImmutableCollector<'a> {
const fn new(global: &'a GlobalCollect, immutable_idents: Option<&'a Vec<IdPlusType>>) -> Self {
Self {
global,
is_immutable: true,
immutable_idents,
}
}
const fn new(global: &'a GlobalCollect, immutable_idents: Option<&'a Vec<IdPlusType>>) -> Self {
Self {
global,
is_immutable: true,
immutable_idents,
}
}
}
// A prop is considered mutable if it:
@@ -41,34 +41,34 @@ impl<'a> ImmutableCollector<'a> {
// - accesses a member
// - is a variable that is not an import, an export, or in the immutable stack
impl<'a> Visit for ImmutableCollector<'a> {
noop_visit_type!();
noop_visit_type!();
fn visit_call_expr(&mut self, _: &ast::CallExpr) {
self.is_immutable = false;
}
fn visit_call_expr(&mut self, _: &ast::CallExpr) {
self.is_immutable = false;
}
fn visit_member_expr(&mut self, _: &ast::MemberExpr) {
self.is_immutable = false;
}
fn visit_member_expr(&mut self, _: &ast::MemberExpr) {
self.is_immutable = false;
}
fn visit_arrow_expr(&mut self, _: &ast::ArrowExpr) {}
fn visit_arrow_expr(&mut self, _: &ast::ArrowExpr) {}
fn visit_ident(&mut self, ident: &ast::Ident) {
let id = id!(ident);
if self.global.imports.contains_key(&id) {
return;
}
if self.global.exports.contains_key(&id) {
return;
}
if let Some(current_stack) = self.immutable_idents {
if current_stack
.iter()
.any(|item| item.1 == IdentType::Var(true) && item.0 == id)
{
return;
}
}
self.is_immutable = false;
}
fn visit_ident(&mut self, ident: &ast::Ident) {
let id = id!(ident);
if self.global.imports.contains_key(&id) {
return;
}
if self.global.exports.contains_key(&id) {
return;
}
if let Some(current_stack) = self.immutable_idents {
if current_stack
.iter()
.any(|item| item.1 == IdentType::Var(true) && item.0 == id)
{
return;
}
}
self.is_immutable = false;
}
}

View File

@@ -55,195 +55,195 @@ pub use crate::parse::{ErrorBuffer, HookAnalysis, MinifyMode, TransformModule, T
#[derive(Serialize, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransformFsOptions {
pub src_dir: String,
pub root_dir: Option<String>,
pub vendor_roots: Vec<String>,
pub glob: Option<String>,
pub minify: MinifyMode,
pub entry_strategy: EntryStrategy,
pub manual_chunks: Option<HashMap<String, JsWord>>,
pub source_maps: bool,
pub transpile_ts: bool,
pub transpile_jsx: bool,
pub preserve_filenames: bool,
pub explicit_extensions: bool,
pub mode: EmitMode,
pub scope: Option<String>,
pub src_dir: String,
pub root_dir: Option<String>,
pub vendor_roots: Vec<String>,
pub glob: Option<String>,
pub minify: MinifyMode,
pub entry_strategy: EntryStrategy,
pub manual_chunks: Option<HashMap<String, JsWord>>,
pub source_maps: bool,
pub transpile_ts: bool,
pub transpile_jsx: bool,
pub preserve_filenames: bool,
pub explicit_extensions: bool,
pub mode: EmitMode,
pub scope: Option<String>,
pub core_module: Option<String>,
pub strip_exports: Option<Vec<JsWord>>,
pub strip_ctx_name: Option<Vec<JsWord>>,
pub strip_event_handlers: bool,
pub reg_ctx_name: Option<Vec<JsWord>>,
pub is_server: Option<bool>,
pub core_module: Option<String>,
pub strip_exports: Option<Vec<JsWord>>,
pub strip_ctx_name: Option<Vec<JsWord>>,
pub strip_event_handlers: bool,
pub reg_ctx_name: Option<Vec<JsWord>>,
pub is_server: Option<bool>,
}
#[derive(Serialize, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransformModuleInput {
pub path: String,
pub code: String,
pub path: String,
pub code: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransformModulesOptions {
pub src_dir: String,
pub root_dir: Option<String>,
pub input: Vec<TransformModuleInput>,
pub source_maps: bool,
pub minify: MinifyMode,
pub transpile_ts: bool,
pub transpile_jsx: bool,
pub preserve_filenames: bool,
pub entry_strategy: EntryStrategy,
pub manual_chunks: Option<HashMap<String, JsWord>>,
pub explicit_extensions: bool,
pub mode: EmitMode,
pub scope: Option<String>,
pub src_dir: String,
pub root_dir: Option<String>,
pub input: Vec<TransformModuleInput>,
pub source_maps: bool,
pub minify: MinifyMode,
pub transpile_ts: bool,
pub transpile_jsx: bool,
pub preserve_filenames: bool,
pub entry_strategy: EntryStrategy,
pub manual_chunks: Option<HashMap<String, JsWord>>,
pub explicit_extensions: bool,
pub mode: EmitMode,
pub scope: Option<String>,
pub core_module: Option<String>,
pub strip_exports: Option<Vec<JsWord>>,
pub strip_ctx_name: Option<Vec<JsWord>>,
pub strip_event_handlers: bool,
pub reg_ctx_name: Option<Vec<JsWord>>,
pub is_server: Option<bool>,
pub core_module: Option<String>,
pub strip_exports: Option<Vec<JsWord>>,
pub strip_ctx_name: Option<Vec<JsWord>>,
pub strip_event_handlers: bool,
pub reg_ctx_name: Option<Vec<JsWord>>,
pub is_server: Option<bool>,
}
#[cfg(feature = "fs")]
pub fn transform_fs(config: TransformFsOptions) -> Result<TransformOutput, Error> {
let core_module = config
.core_module
.map_or(BUILDER_IO_QWIK.clone(), |s| s.into());
let src_dir = Path::new(&config.src_dir);
let root_dir = config.root_dir.as_ref().map(Path::new);
let core_module = config
.core_module
.map_or(BUILDER_IO_QWIK.clone(), |s| s.into());
let src_dir = Path::new(&config.src_dir);
let root_dir = config.root_dir.as_ref().map(Path::new);
let mut paths = vec![];
let entry_policy = &*parse_entry_strategy(&config.entry_strategy, config.manual_chunks);
crate::package_json::find_modules(src_dir, config.vendor_roots, &mut paths)?;
let mut paths = vec![];
let entry_policy = &*parse_entry_strategy(&config.entry_strategy, config.manual_chunks);
crate::package_json::find_modules(src_dir, config.vendor_roots, &mut paths)?;
#[cfg(feature = "parallel")]
let iterator = paths.par_iter();
#[cfg(feature = "parallel")]
let iterator = paths.par_iter();
#[cfg(not(feature = "parallel"))]
let iterator = paths.iter();
let mut final_output = iterator
.map(|path| -> Result<TransformOutput, Error> {
let code = fs::read_to_string(path)
.with_context(|| format!("Opening {}", &path.to_string_lossy()))?;
#[cfg(not(feature = "parallel"))]
let iterator = paths.iter();
let mut final_output = iterator
.map(|path| -> Result<TransformOutput, Error> {
let code = fs::read_to_string(path)
.with_context(|| format!("Opening {}", &path.to_string_lossy()))?;
let relative_path = pathdiff::diff_paths(path, &config.src_dir).unwrap();
transform_code(TransformCodeOptions {
src_dir,
root_dir,
relative_path: relative_path.to_str().unwrap(),
minify: config.minify,
code: &code,
explicit_extensions: config.explicit_extensions,
source_maps: config.source_maps,
transpile_jsx: config.transpile_jsx,
transpile_ts: config.transpile_ts,
preserve_filenames: config.preserve_filenames,
scope: config.scope.as_ref(),
entry_policy,
mode: config.mode,
core_module: core_module.clone(),
entry_strategy: config.entry_strategy,
reg_ctx_name: config.reg_ctx_name.as_deref(),
strip_exports: config.strip_exports.as_deref(),
strip_ctx_name: config.strip_ctx_name.as_deref(),
strip_event_handlers: config.strip_event_handlers,
is_server: config.is_server,
})
})
.reduce(|| Ok(TransformOutput::new()), |x, y| Ok(x?.append(&mut y?)))?;
let relative_path = pathdiff::diff_paths(path, &config.src_dir).unwrap();
transform_code(TransformCodeOptions {
src_dir,
root_dir,
relative_path: relative_path.to_str().unwrap(),
minify: config.minify,
code: &code,
explicit_extensions: config.explicit_extensions,
source_maps: config.source_maps,
transpile_jsx: config.transpile_jsx,
transpile_ts: config.transpile_ts,
preserve_filenames: config.preserve_filenames,
scope: config.scope.as_ref(),
entry_policy,
mode: config.mode,
core_module: core_module.clone(),
entry_strategy: config.entry_strategy,
reg_ctx_name: config.reg_ctx_name.as_deref(),
strip_exports: config.strip_exports.as_deref(),
strip_ctx_name: config.strip_ctx_name.as_deref(),
strip_event_handlers: config.strip_event_handlers,
is_server: config.is_server,
})
})
.reduce(|| Ok(TransformOutput::new()), |x, y| Ok(x?.append(&mut y?)))?;
final_output.modules.sort_unstable_by_key(|key| key.order);
if !matches!(
config.entry_strategy,
EntryStrategy::Hook | EntryStrategy::Inline | EntryStrategy::Hoist
) {
final_output = generate_entries(
final_output,
&core_module,
config.explicit_extensions,
root_dir,
)?;
}
// final_output = generate_entries(
// final_output,
// &core_module,
// config.explicit_extensions,
// root_dir,
// )?;
Ok(final_output)
final_output.modules.sort_unstable_by_key(|key| key.order);
if !matches!(
config.entry_strategy,
EntryStrategy::Hook | EntryStrategy::Inline | EntryStrategy::Hoist
) {
final_output = generate_entries(
final_output,
&core_module,
config.explicit_extensions,
root_dir,
)?;
}
// final_output = generate_entries(
// final_output,
// &core_module,
// config.explicit_extensions,
// root_dir,
// )?;
Ok(final_output)
}
pub fn transform_modules(config: TransformModulesOptions) -> Result<TransformOutput, Error> {
let core_module = config
.core_module
.map_or(BUILDER_IO_QWIK.clone(), |s| s.into());
let src_dir = std::path::Path::new(&config.src_dir);
let root_dir = config.root_dir.as_ref().map(Path::new);
let core_module = config
.core_module
.map_or(BUILDER_IO_QWIK.clone(), |s| s.into());
let src_dir = std::path::Path::new(&config.src_dir);
let root_dir = config.root_dir.as_ref().map(Path::new);
let entry_policy = &*parse_entry_strategy(&config.entry_strategy, config.manual_chunks);
#[cfg(feature = "parallel")]
let iterator = config.input.par_iter();
let entry_policy = &*parse_entry_strategy(&config.entry_strategy, config.manual_chunks);
#[cfg(feature = "parallel")]
let iterator = config.input.par_iter();
#[cfg(not(feature = "parallel"))]
let iterator = config.input.iter();
let iterator = iterator.map(|path| -> Result<TransformOutput, Error> {
transform_code(TransformCodeOptions {
src_dir,
root_dir,
relative_path: &path.path,
code: &path.code,
minify: config.minify,
source_maps: config.source_maps,
transpile_ts: config.transpile_ts,
transpile_jsx: config.transpile_jsx,
preserve_filenames: config.preserve_filenames,
explicit_extensions: config.explicit_extensions,
entry_policy,
mode: config.mode,
scope: config.scope.as_ref(),
core_module: core_module.clone(),
entry_strategy: config.entry_strategy,
reg_ctx_name: config.reg_ctx_name.as_deref(),
strip_exports: config.strip_exports.as_deref(),
strip_ctx_name: config.strip_ctx_name.as_deref(),
strip_event_handlers: config.strip_event_handlers,
is_server: config.is_server,
})
});
#[cfg(not(feature = "parallel"))]
let iterator = config.input.iter();
let iterator = iterator.map(|path| -> Result<TransformOutput, Error> {
transform_code(TransformCodeOptions {
src_dir,
root_dir,
relative_path: &path.path,
code: &path.code,
minify: config.minify,
source_maps: config.source_maps,
transpile_ts: config.transpile_ts,
transpile_jsx: config.transpile_jsx,
preserve_filenames: config.preserve_filenames,
explicit_extensions: config.explicit_extensions,
entry_policy,
mode: config.mode,
scope: config.scope.as_ref(),
core_module: core_module.clone(),
entry_strategy: config.entry_strategy,
reg_ctx_name: config.reg_ctx_name.as_deref(),
strip_exports: config.strip_exports.as_deref(),
strip_ctx_name: config.strip_ctx_name.as_deref(),
strip_event_handlers: config.strip_event_handlers,
is_server: config.is_server,
})
});
#[cfg(feature = "parallel")]
let final_output: Result<TransformOutput, Error> =
iterator.reduce(|| Ok(TransformOutput::new()), |x, y| Ok(x?.append(&mut y?)));
#[cfg(feature = "parallel")]
let final_output: Result<TransformOutput, Error> =
iterator.reduce(|| Ok(TransformOutput::new()), |x, y| Ok(x?.append(&mut y?)));
#[cfg(not(feature = "parallel"))]
let final_output: Result<TransformOutput, Error> =
iterator.fold(Ok(TransformOutput::new()), |x, y| Ok(x?.append(&mut y?)));
#[cfg(not(feature = "parallel"))]
let final_output: Result<TransformOutput, Error> =
iterator.fold(Ok(TransformOutput::new()), |x, y| Ok(x?.append(&mut y?)));
let mut final_output = final_output?;
final_output.modules.sort_unstable_by_key(|key| key.order);
if !matches!(
config.entry_strategy,
EntryStrategy::Hook | EntryStrategy::Inline | EntryStrategy::Hoist
) {
final_output = generate_entries(
final_output,
&core_module,
config.explicit_extensions,
root_dir,
)?;
}
// final_output = generate_entries(
// final_output,
// &core_module,
// config.explicit_extensions,
// root_dir,
// )?;
let mut final_output = final_output?;
final_output.modules.sort_unstable_by_key(|key| key.order);
if !matches!(
config.entry_strategy,
EntryStrategy::Hook | EntryStrategy::Inline | EntryStrategy::Hoist
) {
final_output = generate_entries(
final_output,
&core_module,
config.explicit_extensions,
root_dir,
)?;
}
// final_output = generate_entries(
// final_output,
// &core_module,
// config.explicit_extensions,
// root_dir,
// )?;
Ok(final_output)
Ok(final_output)
}

View File

@@ -3,39 +3,39 @@ pub use crate::parse::{ErrorBuffer, HookAnalysis, MinifyMode, TransformModule, T
#[cfg(feature = "fs")]
pub fn find_modules(
src_dir: &std::path::Path,
vendor_dirs: Vec<String>,
files: &mut Vec<std::path::PathBuf>,
src_dir: &std::path::Path,
vendor_dirs: Vec<String>,
files: &mut Vec<std::path::PathBuf>,
) -> std::io::Result<()> {
for root in &vendor_dirs {
find_files(std::path::Path::new(root), files)?;
}
find_files(src_dir, files)
for root in &vendor_dirs {
find_files(std::path::Path::new(root), files)?;
}
find_files(src_dir, files)
}
#[cfg(feature = "fs")]
fn find_files(dir: &std::path::Path, files: &mut Vec<std::path::PathBuf>) -> std::io::Result<()> {
if dir.is_dir() {
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
find_files(&path, files)?;
} else if should_capture_file(&path) {
files.push(path);
}
}
} else if should_capture_file(dir) {
files.push(dir.to_path_buf());
}
Ok(())
if dir.is_dir() {
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
find_files(&path, files)?;
} else if should_capture_file(&path) {
files.push(path);
}
}
} else if should_capture_file(dir) {
files.push(dir.to_path_buf());
}
Ok(())
}
#[cfg(feature = "fs")]
fn should_capture_file(path: &std::path::Path) -> bool {
let ext = path.extension().and_then(|p| p.to_str());
matches!(
ext,
Some("ts" | "tsx" | "js" | "jsx" | "mjs" | "mts" | "mtsx" | "mjsx")
)
let ext = path.extension().and_then(|p| p.to_str());
matches!(
ext,
Some("ts" | "tsx" | "js" | "jsx" | "mjs" | "mts" | "mtsx" | "mjsx")
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,437 +11,437 @@ use swc_ecmascript::utils::private_ident;
use swc_ecmascript::visit::{VisitMut, VisitMutWith};
struct PropsDestructuring<'a> {
component_ident: Option<Id>,
pub identifiers: HashMap<Id, ast::Expr>,
pub global_collect: &'a mut GlobalCollect,
pub core_module: &'a JsWord,
component_ident: Option<Id>,
pub identifiers: HashMap<Id, ast::Expr>,
pub global_collect: &'a mut GlobalCollect,
pub core_module: &'a JsWord,
}
pub fn transform_props_destructuring(
main_module: &mut ast::Module,
global_collect: &mut GlobalCollect,
core_module: &JsWord,
main_module: &mut ast::Module,
global_collect: &mut GlobalCollect,
core_module: &JsWord,
) {
main_module.visit_mut_with(&mut PropsDestructuring {
component_ident: global_collect.get_imported_local(&COMPONENT, core_module),
identifiers: HashMap::new(),
global_collect,
core_module,
});
main_module.visit_mut_with(&mut PropsDestructuring {
component_ident: global_collect.get_imported_local(&COMPONENT, core_module),
identifiers: HashMap::new(),
global_collect,
core_module,
});
}
macro_rules! id {
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
($ident: expr) => {
($ident.sym.clone(), $ident.span.ctxt())
};
}
macro_rules! id_eq {
($ident: expr, $cid: expr) => {
if let Some(cid) = $cid {
cid.0 == $ident.sym && cid.1 == $ident.span.ctxt()
} else {
false
}
};
($ident: expr, $cid: expr) => {
if let Some(cid) = $cid {
cid.0 == $ident.sym && cid.1 == $ident.span.ctxt()
} else {
false
}
};
}
enum TransformInit {
Keep,
Remove,
Replace(ast::Expr),
Keep,
Remove,
Replace(ast::Expr),
}
impl<'a> PropsDestructuring<'a> {
fn transform_component_props(&mut self, arrow: &mut ast::ArrowExpr) {
if let Some(ast::Pat::Object(obj)) = arrow.params.first() {
let new_ident = private_ident!("props");
if let Some((rest_id, local)) =
transform_pat(ast::Expr::Ident(new_ident.clone()), obj, self)
{
if let Some(rest_id) = rest_id {
let omit_fn = self.global_collect.import(&_REST_PROPS, self.core_module);
let omit = local.iter().map(|(_, id, _)| id.clone()).collect();
transform_rest(
arrow,
&omit_fn,
&rest_id,
ast::Expr::Ident(new_ident.clone()),
omit,
);
}
for (id, _, expr) in local {
self.identifiers.insert(id, expr);
}
arrow.params[0] = ast::Pat::Ident(ast::BindingIdent::from(new_ident));
}
}
if let ast::BlockStmtOrExpr::BlockStmt(body) = &mut *arrow.body {
self.transform_component_body(body);
}
}
fn transform_component_body(&mut self, body: &mut ast::BlockStmt) {
let mut inserts = vec![];
for (index, stmt) in body.stmts.iter_mut().enumerate() {
if let ast::Stmt::Decl(ast::Decl::Var(var_decl)) = stmt {
if var_decl.kind == ast::VarDeclKind::Const {
for decl in var_decl.decls.iter_mut() {
let convert = match &decl.init {
Some(box ast::Expr::Lit(lit)) => {
let new_ident = private_ident!("_unused");
Some((
new_ident,
ast::Expr::Lit(lit.clone()),
TransformInit::Remove,
))
}
Some(box ast::Expr::Member(member_expr)) => match &member_expr.obj {
box ast::Expr::Ident(ident) => {
let new_ident = private_ident!("_unused");
let expr = self
.identifiers
.get(&id!(ident.clone()))
.cloned()
.unwrap_or_else(|| ast::Expr::Ident(ident.clone()));
fn transform_component_props(&mut self, arrow: &mut ast::ArrowExpr) {
if let Some(ast::Pat::Object(obj)) = arrow.params.first() {
let new_ident = private_ident!("props");
if let Some((rest_id, local)) =
transform_pat(ast::Expr::Ident(new_ident.clone()), obj, self)
{
if let Some(rest_id) = rest_id {
let omit_fn = self.global_collect.import(&_REST_PROPS, self.core_module);
let omit = local.iter().map(|(_, id, _)| id.clone()).collect();
transform_rest(
arrow,
&omit_fn,
&rest_id,
ast::Expr::Ident(new_ident.clone()),
omit,
);
}
for (id, _, expr) in local {
self.identifiers.insert(id, expr);
}
arrow.params[0] = ast::Pat::Ident(ast::BindingIdent::from(new_ident));
}
}
if let ast::BlockStmtOrExpr::BlockStmt(body) = &mut *arrow.body {
self.transform_component_body(body);
}
}
fn transform_component_body(&mut self, body: &mut ast::BlockStmt) {
let mut inserts = vec![];
for (index, stmt) in body.stmts.iter_mut().enumerate() {
if let ast::Stmt::Decl(ast::Decl::Var(var_decl)) = stmt {
if var_decl.kind == ast::VarDeclKind::Const {
for decl in var_decl.decls.iter_mut() {
let convert = match &decl.init {
Some(box ast::Expr::Lit(lit)) => {
let new_ident = private_ident!("_unused");
Some((
new_ident,
ast::Expr::Lit(lit.clone()),
TransformInit::Remove,
))
}
Some(box ast::Expr::Member(member_expr)) => match &member_expr.obj {
box ast::Expr::Ident(ident) => {
let new_ident = private_ident!("_unused");
let expr = self
.identifiers
.get(&id!(ident.clone()))
.cloned()
.unwrap_or_else(|| ast::Expr::Ident(ident.clone()));
let mut cloned_prop = member_expr.prop.clone();
cloned_prop.visit_mut_with(self);
let new_replace = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(expr),
prop: cloned_prop,
span: member_expr.span,
});
Some((new_ident, new_replace, TransformInit::Remove))
}
box ast::Expr::Call(call_expr) => {
if let ast::Callee::Expr(box ast::Expr::Ident(ref ident)) =
&call_expr.callee
{
if ident.sym.starts_with("use") {
let new_ident =
private_ident!(ident.sym[3..].to_lowercase());
let mut cloned_prop = member_expr.prop.clone();
cloned_prop.visit_mut_with(self);
let new_replace = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(expr),
prop: cloned_prop,
span: member_expr.span,
});
Some((new_ident, new_replace, TransformInit::Remove))
}
box ast::Expr::Call(call_expr) => {
if let ast::Callee::Expr(box ast::Expr::Ident(ref ident)) =
&call_expr.callee
{
if ident.sym.starts_with("use") {
let new_ident =
private_ident!(ident.sym[3..].to_lowercase());
let mut cloned_prop = member_expr.prop.clone();
cloned_prop.visit_mut_with(self);
let mut cloned_prop = member_expr.prop.clone();
cloned_prop.visit_mut_with(self);
let new_replace = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(ast::Expr::Ident(new_ident.clone())),
prop: cloned_prop,
span: DUMMY_SP,
});
Some((
new_ident,
new_replace,
TransformInit::Replace(ast::Expr::Call(
call_expr.clone(),
)),
))
} else {
None
}
} else {
None
}
}
_ => None,
},
Some(box ast::Expr::Ident(ref ident)) => {
let new_ident = private_ident!("_unused");
let new_replace = self
.identifiers
.get(&id!(ident.clone()))
.cloned()
.unwrap_or_else(|| ast::Expr::Ident(ident.clone()));
Some((new_ident, new_replace, TransformInit::Remove))
}
Some(box ast::Expr::Call(call_expr)) => {
if let ast::Callee::Expr(box ast::Expr::Ident(ref ident)) =
&call_expr.callee
{
if ident.sym.starts_with("use") {
let new_ident =
private_ident!(ident.sym[3..].to_lowercase());
let new_replace = ast::Expr::Ident(new_ident.clone());
Some((new_ident, new_replace, TransformInit::Keep))
} else {
None
}
} else {
None
}
}
_ => None,
};
if let Some((replace_pat, new_ref, init)) = convert {
let keep_ident = matches!(init, TransformInit::Keep);
let mut transform_init = init;
match &decl.name {
ast::Pat::Ident(ident) => {
if !keep_ident {
self.identifiers.insert(id!(ident.id.clone()), new_ref);
decl.name =
ast::Pat::Ident(ast::BindingIdent::from(replace_pat));
} else {
transform_init = TransformInit::Keep;
}
}
ast::Pat::Object(obj_pat) => {
if let Some((rest_id, local)) =
transform_pat(new_ref.clone(), obj_pat, self)
{
if let Some(rest_id) = rest_id {
let omit_fn = self
.global_collect
.import(&_REST_PROPS, self.core_module);
let omit =
local.iter().map(|(_, id, _)| id.clone()).collect();
let new_replace = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(ast::Expr::Ident(new_ident.clone())),
prop: cloned_prop,
span: DUMMY_SP,
});
Some((
new_ident,
new_replace,
TransformInit::Replace(ast::Expr::Call(
call_expr.clone(),
)),
))
} else {
None
}
} else {
None
}
}
_ => None,
},
Some(box ast::Expr::Ident(ref ident)) => {
let new_ident = private_ident!("_unused");
let new_replace = self
.identifiers
.get(&id!(ident.clone()))
.cloned()
.unwrap_or_else(|| ast::Expr::Ident(ident.clone()));
Some((new_ident, new_replace, TransformInit::Remove))
}
Some(box ast::Expr::Call(call_expr)) => {
if let ast::Callee::Expr(box ast::Expr::Ident(ref ident)) =
&call_expr.callee
{
if ident.sym.starts_with("use") {
let new_ident =
private_ident!(ident.sym[3..].to_lowercase());
let new_replace = ast::Expr::Ident(new_ident.clone());
Some((new_ident, new_replace, TransformInit::Keep))
} else {
None
}
} else {
None
}
}
_ => None,
};
if let Some((replace_pat, new_ref, init)) = convert {
let keep_ident = matches!(init, TransformInit::Keep);
let mut transform_init = init;
match &decl.name {
ast::Pat::Ident(ident) => {
if !keep_ident {
self.identifiers.insert(id!(ident.id.clone()), new_ref);
decl.name =
ast::Pat::Ident(ast::BindingIdent::from(replace_pat));
} else {
transform_init = TransformInit::Keep;
}
}
ast::Pat::Object(obj_pat) => {
if let Some((rest_id, local)) =
transform_pat(new_ref.clone(), obj_pat, self)
{
if let Some(rest_id) = rest_id {
let omit_fn = self
.global_collect
.import(&_REST_PROPS, self.core_module);
let omit =
local.iter().map(|(_, id, _)| id.clone()).collect();
let element = create_omit_props(
&omit_fn, &rest_id, new_ref, omit,
);
inserts.push((index + 1 + inserts.len(), element));
}
for (id, _, expr) in local {
self.identifiers.insert(id, expr);
}
decl.name =
ast::Pat::Ident(ast::BindingIdent::from(replace_pat));
} else {
transform_init = TransformInit::Keep;
}
}
_ => {
transform_init = TransformInit::Keep;
}
}
match transform_init {
TransformInit::Remove => {
decl.init = None;
}
TransformInit::Replace(expr) => {
decl.init = Some(Box::new(expr));
}
TransformInit::Keep => {}
}
}
}
} else {
break;
}
}
}
let element = create_omit_props(
&omit_fn, &rest_id, new_ref, omit,
);
inserts.push((index + 1 + inserts.len(), element));
}
for (id, _, expr) in local {
self.identifiers.insert(id, expr);
}
decl.name =
ast::Pat::Ident(ast::BindingIdent::from(replace_pat));
} else {
transform_init = TransformInit::Keep;
}
}
_ => {
transform_init = TransformInit::Keep;
}
}
match transform_init {
TransformInit::Remove => {
decl.init = None;
}
TransformInit::Replace(expr) => {
decl.init = Some(Box::new(expr));
}
TransformInit::Keep => {}
}
}
}
} else {
break;
}
}
}
for (index, stmt) in inserts {
body.stmts.insert(index, stmt);
}
}
for (index, stmt) in inserts {
body.stmts.insert(index, stmt);
}
}
}
impl<'a> VisitMut for PropsDestructuring<'a> {
fn visit_mut_call_expr(&mut self, node: &mut ast::CallExpr) {
if let ast::Callee::Expr(box ast::Expr::Ident(ref ident)) = &node.callee {
if id_eq!(ident, &self.component_ident) {
if let Some(first_arg) = node.args.first_mut() {
if let ast::Expr::Arrow(arrow) = &mut *first_arg.expr {
self.transform_component_props(arrow);
}
}
}
}
fn visit_mut_call_expr(&mut self, node: &mut ast::CallExpr) {
if let ast::Callee::Expr(box ast::Expr::Ident(ref ident)) = &node.callee {
if id_eq!(ident, &self.component_ident) {
if let Some(first_arg) = node.args.first_mut() {
if let ast::Expr::Arrow(arrow) = &mut *first_arg.expr {
self.transform_component_props(arrow);
}
}
}
}
node.visit_mut_children_with(self);
}
node.visit_mut_children_with(self);
}
fn visit_mut_expr(&mut self, node: &mut ast::Expr) {
match node {
ast::Expr::Ident(ident) => {
if let Some(expr) = self.identifiers.get(&id!(ident)) {
*node = expr.clone();
}
}
_ => {
node.visit_mut_children_with(self);
}
}
}
fn visit_mut_expr(&mut self, node: &mut ast::Expr) {
match node {
ast::Expr::Ident(ident) => {
if let Some(expr) = self.identifiers.get(&id!(ident)) {
*node = expr.clone();
}
}
_ => {
node.visit_mut_children_with(self);
}
}
}
fn visit_mut_prop(&mut self, node: &mut ast::Prop) {
if let ast::Prop::Shorthand(short) = node {
if let Some(expr) = self.identifiers.get(&id!(short)) {
*node = ast::Prop::KeyValue(ast::KeyValueProp {
key: ast::PropName::Ident(short.clone()),
value: Box::new(expr.clone()),
});
}
}
node.visit_mut_children_with(self);
}
fn visit_mut_prop(&mut self, node: &mut ast::Prop) {
if let ast::Prop::Shorthand(short) = node {
if let Some(expr) = self.identifiers.get(&id!(short)) {
*node = ast::Prop::KeyValue(ast::KeyValueProp {
key: ast::PropName::Ident(short.clone()),
value: Box::new(expr.clone()),
});
}
}
node.visit_mut_children_with(self);
}
}
type TransformPatReturn = (Option<Id>, Vec<(Id, JsWord, ast::Expr)>);
fn transform_pat(
new_ident: ast::Expr,
obj: &ast::ObjectPat,
props_transform: &mut PropsDestructuring,
new_ident: ast::Expr,
obj: &ast::ObjectPat,
props_transform: &mut PropsDestructuring,
) -> Option<TransformPatReturn> {
let mut local = vec![];
let mut skip = false;
let mut rest_id = None;
for prop in &obj.props {
match prop {
ast::ObjectPatProp::Assign(ref v) => {
let access = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(new_ident.clone()),
prop: ast::MemberProp::Ident(v.key.clone()),
span: DUMMY_SP,
});
if let Some(value) = &v.value {
if is_immutable_expr(value.as_ref(), props_transform.global_collect, None) {
local.push((
id!(v.key),
v.key.sym.clone(),
ast::Expr::Bin(ast::BinExpr {
span: DUMMY_SP,
op: ast::BinaryOp::NullishCoalescing,
left: Box::new(access),
right: value.clone(),
}),
));
} else {
skip = true;
}
} else {
local.push((id!(v.key), v.key.sym.clone(), access));
}
}
ast::ObjectPatProp::KeyValue(ref v) => {
if let ast::PropName::Ident(ref key) = v.key {
match &v.value {
box ast::Pat::Ident(ref ident) => {
let access = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(new_ident.clone()),
prop: ast::MemberProp::Ident(key.clone()),
span: DUMMY_SP,
});
local.push((id!(ident), key.sym.clone(), access));
}
box ast::Pat::Assign(ast::AssignPat {
left: box ast::Pat::Ident(ident),
right: value,
..
}) => {
if is_immutable_expr(
value.as_ref(),
props_transform.global_collect,
None,
) {
let access = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(new_ident.clone()),
prop: ast::MemberProp::Ident(key.clone()),
span: DUMMY_SP,
});
local.push((
id!(ident.id),
key.sym.clone(),
ast::Expr::Bin(ast::BinExpr {
span: DUMMY_SP,
op: ast::BinaryOp::NullishCoalescing,
left: Box::new(access),
right: value.clone(),
}),
));
} else {
skip = true;
}
}
_ => {
skip = true;
}
}
} else {
skip = true;
}
}
ast::ObjectPatProp::Rest(ast::RestPat { box arg, .. }) => {
if let ast::Pat::Ident(ref ident) = arg {
rest_id = Some(id!(&ident.id));
} else {
skip = true;
}
}
}
}
if skip || local.is_empty() {
return None;
}
Some((rest_id, local))
let mut local = vec![];
let mut skip = false;
let mut rest_id = None;
for prop in &obj.props {
match prop {
ast::ObjectPatProp::Assign(ref v) => {
let access = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(new_ident.clone()),
prop: ast::MemberProp::Ident(v.key.clone()),
span: DUMMY_SP,
});
if let Some(value) = &v.value {
if is_immutable_expr(value.as_ref(), props_transform.global_collect, None) {
local.push((
id!(v.key),
v.key.sym.clone(),
ast::Expr::Bin(ast::BinExpr {
span: DUMMY_SP,
op: ast::BinaryOp::NullishCoalescing,
left: Box::new(access),
right: value.clone(),
}),
));
} else {
skip = true;
}
} else {
local.push((id!(v.key), v.key.sym.clone(), access));
}
}
ast::ObjectPatProp::KeyValue(ref v) => {
if let ast::PropName::Ident(ref key) = v.key {
match &v.value {
box ast::Pat::Ident(ref ident) => {
let access = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(new_ident.clone()),
prop: ast::MemberProp::Ident(key.clone()),
span: DUMMY_SP,
});
local.push((id!(ident), key.sym.clone(), access));
}
box ast::Pat::Assign(ast::AssignPat {
left: box ast::Pat::Ident(ident),
right: value,
..
}) => {
if is_immutable_expr(
value.as_ref(),
props_transform.global_collect,
None,
) {
let access = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(new_ident.clone()),
prop: ast::MemberProp::Ident(key.clone()),
span: DUMMY_SP,
});
local.push((
id!(ident.id),
key.sym.clone(),
ast::Expr::Bin(ast::BinExpr {
span: DUMMY_SP,
op: ast::BinaryOp::NullishCoalescing,
left: Box::new(access),
right: value.clone(),
}),
));
} else {
skip = true;
}
}
_ => {
skip = true;
}
}
} else {
skip = true;
}
}
ast::ObjectPatProp::Rest(ast::RestPat { box arg, .. }) => {
if let ast::Pat::Ident(ref ident) = arg {
rest_id = Some(id!(&ident.id));
} else {
skip = true;
}
}
}
}
if skip || local.is_empty() {
return None;
}
Some((rest_id, local))
}
fn transform_rest(
arrow: &mut ast::ArrowExpr,
omit_fn: &Id,
rest_id: &Id,
props_expr: ast::Expr,
omit: Vec<JsWord>,
arrow: &mut ast::ArrowExpr,
omit_fn: &Id,
rest_id: &Id,
props_expr: ast::Expr,
omit: Vec<JsWord>,
) {
let new_stmt = create_omit_props(omit_fn, rest_id, props_expr, omit);
match &mut arrow.body {
box ast::BlockStmtOrExpr::BlockStmt(block) => {
block.stmts.insert(0, new_stmt);
}
box ast::BlockStmtOrExpr::Expr(ref expr) => {
arrow.body = Box::new(ast::BlockStmtOrExpr::BlockStmt(ast::BlockStmt {
span: DUMMY_SP,
stmts: vec![new_stmt, create_return_stmt(expr.clone())],
}));
}
}
let new_stmt = create_omit_props(omit_fn, rest_id, props_expr, omit);
match &mut arrow.body {
box ast::BlockStmtOrExpr::BlockStmt(block) => {
block.stmts.insert(0, new_stmt);
}
box ast::BlockStmtOrExpr::Expr(ref expr) => {
arrow.body = Box::new(ast::BlockStmtOrExpr::BlockStmt(ast::BlockStmt {
span: DUMMY_SP,
stmts: vec![new_stmt, create_return_stmt(expr.clone())],
}));
}
}
}
fn create_omit_props(
omit_fn: &Id,
rest_id: &Id,
props_expr: ast::Expr,
omit: Vec<JsWord>,
omit_fn: &Id,
rest_id: &Id,
props_expr: ast::Expr,
omit: Vec<JsWord>,
) -> ast::Stmt {
ast::Stmt::Decl(ast::Decl::Var(Box::new(ast::VarDecl {
span: DUMMY_SP,
declare: false,
kind: ast::VarDeclKind::Const,
decls: vec![ast::VarDeclarator {
definite: false,
span: DUMMY_SP,
init: Some(Box::new(ast::Expr::Call(ast::CallExpr {
callee: ast::Callee::Expr(Box::new(ast::Expr::Ident(new_ident_from_id(omit_fn)))),
span: DUMMY_SP,
type_args: None,
args: vec![
ast::ExprOrSpread {
spread: None,
expr: Box::new(props_expr),
},
ast::ExprOrSpread {
spread: None,
expr: Box::new(ast::Expr::Array(ast::ArrayLit {
span: DUMMY_SP,
elems: omit
.into_iter()
.map(|v| {
Some(ast::ExprOrSpread {
spread: None,
expr: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str {
span: DUMMY_SP,
value: v,
raw: None,
}))),
})
})
.collect(),
})),
},
],
}))),
name: ast::Pat::Ident(ast::BindingIdent::from(new_ident_from_id(rest_id))),
}],
})))
ast::Stmt::Decl(ast::Decl::Var(Box::new(ast::VarDecl {
span: DUMMY_SP,
declare: false,
kind: ast::VarDeclKind::Const,
decls: vec![ast::VarDeclarator {
definite: false,
span: DUMMY_SP,
init: Some(Box::new(ast::Expr::Call(ast::CallExpr {
callee: ast::Callee::Expr(Box::new(ast::Expr::Ident(new_ident_from_id(omit_fn)))),
span: DUMMY_SP,
type_args: None,
args: vec![
ast::ExprOrSpread {
spread: None,
expr: Box::new(props_expr),
},
ast::ExprOrSpread {
spread: None,
expr: Box::new(ast::Expr::Array(ast::ArrayLit {
span: DUMMY_SP,
elems: omit
.into_iter()
.map(|v| {
Some(ast::ExprOrSpread {
spread: None,
expr: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str {
span: DUMMY_SP,
value: v,
raw: None,
}))),
})
})
.collect(),
})),
},
],
}))),
name: ast::Pat::Ident(ast::BindingIdent::from(new_ident_from_id(rest_id))),
}],
})))
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,74 +5,74 @@ use swc_atoms::JsWord;
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SourceLocation {
lo: usize,
hi: usize,
start_line: usize,
start_col: usize,
end_line: usize,
end_col: usize,
lo: usize,
hi: usize,
start_line: usize,
start_col: usize,
end_line: usize,
end_col: usize,
}
impl SourceLocation {
pub fn from(source_map: &swc_common::SourceMap, span: swc_common::Span) -> Self {
let start = source_map.lookup_char_pos(span.lo);
let end = source_map.lookup_char_pos(span.hi);
// - SWC's columns are exclusive, ours are inclusive (column - 1)
// - SWC has 0-based columns, ours are 1-based (column + 1)
// = +-0
pub fn from(source_map: &swc_common::SourceMap, span: swc_common::Span) -> Self {
let start = source_map.lookup_char_pos(span.lo);
let end = source_map.lookup_char_pos(span.hi);
// - SWC's columns are exclusive, ours are inclusive (column - 1)
// - SWC has 0-based columns, ours are 1-based (column + 1)
// = +-0
Self {
lo: span.lo.0 as usize,
hi: span.hi.0 as usize,
start_line: start.line,
start_col: start.col_display + 1,
end_line: end.line,
end_col: end.col_display,
}
}
Self {
lo: span.lo.0 as usize,
hi: span.hi.0 as usize,
start_line: start.line,
start_col: start.col_display + 1,
end_line: end.line,
end_col: end.col_display,
}
}
}
impl PartialOrd for SourceLocation {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.start_line.cmp(&other.start_line) {
Ordering::Equal => self.start_col.partial_cmp(&other.start_col),
o => Some(o),
}
}
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.start_line.cmp(&other.start_line) {
Ordering::Equal => self.start_col.partial_cmp(&other.start_col),
o => Some(o),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Diagnostic {
pub category: DiagnosticCategory,
pub code: Option<String>,
pub file: JsWord,
pub message: String,
pub highlights: Option<Vec<SourceLocation>>,
pub suggestions: Option<Vec<String>>,
pub scope: DiagnosticScope,
pub category: DiagnosticCategory,
pub code: Option<String>,
pub file: JsWord,
pub message: String,
pub highlights: Option<Vec<SourceLocation>>,
pub suggestions: Option<Vec<String>>,
pub scope: DiagnosticScope,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum DiagnosticCategory {
/// Fails the build with an error.
Error,
/// Logs a warning, but the build does not fail.
Warning,
/// An error if this is source code in the project, or a warning if in node_modules.
SourceError,
/// Fails the build with an error.
Error,
/// Logs a warning, but the build does not fail.
Warning,
/// An error if this is source code in the project, or a warning if in node_modules.
SourceError,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum DiagnosticScope {
Optimizer,
Optimizer,
}
#[derive(Serialize, Debug, Deserialize, Eq, PartialEq, Clone, Copy)]
#[serde(rename_all = "camelCase")]
pub enum SourceType {
Script,
Module,
Script,
Module,
}

View File

@@ -5,44 +5,44 @@ pub const SIGNAL: char = '$';
pub const LONG_SUFFIX: &str = "Qrl";
lazy_static! {
pub static ref REF: JsWord = JsWord::from("ref");
pub static ref QSLOT: JsWord = JsWord::from("q:slot");
pub static ref CHILDREN: JsWord = JsWord::from("children");
pub static ref HANDLE_WATCH: JsWord = JsWord::from("_hW");
pub static ref _QRL: JsWord = JsWord::from("qrl");
pub static ref _QRL_DEV: JsWord = JsWord::from("qrlDEV");
pub static ref _INLINED_QRL: JsWord = JsWord::from("inlinedQrl");
pub static ref _INLINED_QRL_DEV: JsWord = JsWord::from("inlinedQrlDEV");
pub static ref _NOOP_QRL: JsWord = JsWord::from("_noopQrl");
pub static ref _REST_PROPS: JsWord = JsWord::from("_restProps");
pub static ref QHOOK: JsWord = JsWord::from("$");
pub static ref Q_SYNC: JsWord = JsWord::from("sync$");
pub static ref QWIK_INTERNAL: JsWord = JsWord::from("qwik");
pub static ref BUILDER_IO_QWIK: JsWord = JsWord::from("@builder.io/qwik");
pub static ref BUILDER_IO_QWIK_BUILD: JsWord = JsWord::from("@builder.io/qwik/build");
pub static ref BUILDER_IO_QWIK_JSX: JsWord = JsWord::from("@builder.io/qwik/jsx-runtime");
pub static ref BUILDER_IO_QWIK_JSX_DEV: JsWord =
JsWord::from("@builder.io/qwik/jsx-dev-runtime");
pub static ref QCOMPONENT: JsWord = JsWord::from("component$");
pub static ref USE_LEXICAL_SCOPE: JsWord = JsWord::from("useLexicalScope");
pub static ref USE_SERVER_MOUNT: JsWord = JsWord::from("useServerMount$");
pub static ref H: JsWord = JsWord::from("h");
pub static ref FRAGMENT: JsWord = JsWord::from("Fragment");
pub static ref _IMMUTABLE: JsWord = JsWord::from("_IMMUTABLE");
pub static ref _INLINED_FN: JsWord = JsWord::from("_fnSignal");
pub static ref IS_SERVER: JsWord = JsWord::from("isServer");
pub static ref IS_BROWSER: JsWord = JsWord::from("isBrowser");
pub static ref IS_DEV: JsWord = JsWord::from("isDev");
pub static ref COMPONENT: JsWord = JsWord::from("component$");
pub static ref _REG_SYMBOL: JsWord = JsWord::from("_regSymbol");
pub static ref _JSX_BRANCH: JsWord = JsWord::from("_jsxBranch");
pub static ref _QRL_SYNC: JsWord = JsWord::from("_qrlSync");
pub static ref _WRAP_PROP: JsWord = JsWord::from("_wrapProp");
pub static ref _WRAP_SIGNAL: JsWord = JsWord::from("_wrapSignal");
pub static ref _JSX_Q: JsWord = JsWord::from("_jsxQ");
pub static ref _JSX_S: JsWord = JsWord::from("_jsxS");
pub static ref _JSX_C: JsWord = JsWord::from("_jsxC");
pub static ref JSX: JsWord = JsWord::from("jsx");
pub static ref JSXS: JsWord = JsWord::from("jsxs");
pub static ref JSX_DEV: JsWord = JsWord::from("jsxDEV");
pub static ref REF: JsWord = JsWord::from("ref");
pub static ref QSLOT: JsWord = JsWord::from("q:slot");
pub static ref CHILDREN: JsWord = JsWord::from("children");
pub static ref HANDLE_WATCH: JsWord = JsWord::from("_hW");
pub static ref _QRL: JsWord = JsWord::from("qrl");
pub static ref _QRL_DEV: JsWord = JsWord::from("qrlDEV");
pub static ref _INLINED_QRL: JsWord = JsWord::from("inlinedQrl");
pub static ref _INLINED_QRL_DEV: JsWord = JsWord::from("inlinedQrlDEV");
pub static ref _NOOP_QRL: JsWord = JsWord::from("_noopQrl");
pub static ref _REST_PROPS: JsWord = JsWord::from("_restProps");
pub static ref QHOOK: JsWord = JsWord::from("$");
pub static ref Q_SYNC: JsWord = JsWord::from("sync$");
pub static ref QWIK_INTERNAL: JsWord = JsWord::from("qwik");
pub static ref BUILDER_IO_QWIK: JsWord = JsWord::from("@builder.io/qwik");
pub static ref BUILDER_IO_QWIK_BUILD: JsWord = JsWord::from("@builder.io/qwik/build");
pub static ref BUILDER_IO_QWIK_JSX: JsWord = JsWord::from("@builder.io/qwik/jsx-runtime");
pub static ref BUILDER_IO_QWIK_JSX_DEV: JsWord =
JsWord::from("@builder.io/qwik/jsx-dev-runtime");
pub static ref QCOMPONENT: JsWord = JsWord::from("component$");
pub static ref USE_LEXICAL_SCOPE: JsWord = JsWord::from("useLexicalScope");
pub static ref USE_SERVER_MOUNT: JsWord = JsWord::from("useServerMount$");
pub static ref H: JsWord = JsWord::from("h");
pub static ref FRAGMENT: JsWord = JsWord::from("Fragment");
pub static ref _IMMUTABLE: JsWord = JsWord::from("_IMMUTABLE");
pub static ref _INLINED_FN: JsWord = JsWord::from("_fnSignal");
pub static ref IS_SERVER: JsWord = JsWord::from("isServer");
pub static ref IS_BROWSER: JsWord = JsWord::from("isBrowser");
pub static ref IS_DEV: JsWord = JsWord::from("isDev");
pub static ref COMPONENT: JsWord = JsWord::from("component$");
pub static ref _REG_SYMBOL: JsWord = JsWord::from("_regSymbol");
pub static ref _JSX_BRANCH: JsWord = JsWord::from("_jsxBranch");
pub static ref _QRL_SYNC: JsWord = JsWord::from("_qrlSync");
pub static ref _WRAP_PROP: JsWord = JsWord::from("_wrapProp");
pub static ref _WRAP_SIGNAL: JsWord = JsWord::from("_wrapSignal");
pub static ref _JSX_Q: JsWord = JsWord::from("_jsxQ");
pub static ref _JSX_S: JsWord = JsWord::from("_jsxS");
pub static ref _JSX_C: JsWord = JsWord::from("_jsxC");
pub static ref JSX: JsWord = JsWord::from("jsx");
pub static ref JSXS: JsWord = JsWord::from("jsxs");
pub static ref JSX_DEV: JsWord = JsWord::from("jsxDEV");
}

1
rustfmt.toml Normal file
View File

@@ -0,0 +1 @@
hard_tabs = true