From 45fbd4db1454aecab35ac73ac42379f47e22a1e4 Mon Sep 17 00:00:00 2001 From: OJ Kwon <1210596+kwonoj@users.noreply.github.com> Date: Tue, 12 Sep 2023 10:48:58 -0700 Subject: [PATCH] refactor(next-font): use next-transform-font (#55252) Closes WEB-1539 ### What? It looks like there are duplicated codes between `next-font` and `next-transform-font`, and the prior is not being referenced anywhere. --- packages/next-swc/crates/next-font/Cargo.toml | 15 - .../find_functions_outside_module_scope.rs | 34 --- .../next-font/src/font_functions_collector.rs | 71 ----- .../next-font/src/font_imports_generator.rs | 272 ------------------ packages/next-swc/crates/next-font/src/lib.rs | 88 ------ 5 files changed, 480 deletions(-) delete mode 100644 packages/next-swc/crates/next-font/Cargo.toml delete mode 100644 packages/next-swc/crates/next-font/src/find_functions_outside_module_scope.rs delete mode 100644 packages/next-swc/crates/next-font/src/font_functions_collector.rs delete mode 100644 packages/next-swc/crates/next-font/src/font_imports_generator.rs delete mode 100644 packages/next-swc/crates/next-font/src/lib.rs diff --git a/packages/next-swc/crates/next-font/Cargo.toml b/packages/next-swc/crates/next-font/Cargo.toml deleted file mode 100644 index 6f2a95a29faea..0000000000000 --- a/packages/next-swc/crates/next-font/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "next-font" -version = "0.1.0" -description = "TBD" -license = "MPL-2.0" -edition = "2021" - -[lib] -bench = false - -[dependencies] -rustc-hash = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -swc_core = { workspace = true } diff --git a/packages/next-swc/crates/next-font/src/find_functions_outside_module_scope.rs b/packages/next-swc/crates/next-font/src/find_functions_outside_module_scope.rs deleted file mode 100644 index 4648c805c708b..0000000000000 --- a/packages/next-swc/crates/next-font/src/find_functions_outside_module_scope.rs +++ /dev/null @@ -1,34 +0,0 @@ -use swc_core::{ - common::errors::HANDLER, - ecma::{ - ast::*, - visit::{noop_visit_type, Visit}, - }, -}; - -pub struct FindFunctionsOutsideModuleScope<'a> { - pub state: &'a super::State, -} - -impl<'a> Visit for FindFunctionsOutsideModuleScope<'a> { - noop_visit_type!(); - - fn visit_ident(&mut self, ident: &Ident) { - if self.state.font_functions.get(&ident.to_id()).is_some() - && self - .state - .font_functions_in_allowed_scope - .get(&ident.span.lo) - .is_none() - { - HANDLER.with(|handler| { - handler - .struct_span_err( - ident.span, - "Font loaders must be called and assigned to a const in the module scope", - ) - .emit() - }); - } - } -} diff --git a/packages/next-swc/crates/next-font/src/font_functions_collector.rs b/packages/next-swc/crates/next-font/src/font_functions_collector.rs deleted file mode 100644 index 1cd2e2c7f62b4..0000000000000 --- a/packages/next-swc/crates/next-font/src/font_functions_collector.rs +++ /dev/null @@ -1,71 +0,0 @@ -use swc_core::{ - common::errors::HANDLER, - ecma::{ - ast::*, - atoms::JsWord, - visit::{noop_visit_type, Visit}, - }, -}; - -pub struct FontFunctionsCollector<'a> { - pub font_loaders: &'a [JsWord], - pub state: &'a mut super::State, -} - -impl<'a> Visit for FontFunctionsCollector<'a> { - noop_visit_type!(); - - fn visit_import_decl(&mut self, import_decl: &ImportDecl) { - if self.font_loaders.contains(&import_decl.src.value) { - self.state - .removeable_module_items - .insert(import_decl.span.lo); - for specifier in &import_decl.specifiers { - match specifier { - ImportSpecifier::Named(ImportNamedSpecifier { - local, imported, .. - }) => { - self.state - .font_functions_in_allowed_scope - .insert(local.span.lo); - - let function_name = if let Some(ModuleExportName::Ident(ident)) = imported { - ident.sym.clone() - } else { - local.sym.clone() - }; - self.state.font_functions.insert( - local.to_id(), - super::FontFunction { - loader: import_decl.src.value.clone(), - function_name: Some(function_name), - }, - ); - } - ImportSpecifier::Default(ImportDefaultSpecifier { local, .. }) => { - self.state - .font_functions_in_allowed_scope - .insert(local.span.lo); - self.state.font_functions.insert( - local.to_id(), - super::FontFunction { - loader: import_decl.src.value.clone(), - function_name: None, - }, - ); - } - ImportSpecifier::Namespace(_) => { - HANDLER.with(|handler| { - handler - .struct_span_err( - import_decl.span, - "Font loaders can't have namespace imports", - ) - .emit() - }); - } - } - } - } - } -} diff --git a/packages/next-swc/crates/next-font/src/font_imports_generator.rs b/packages/next-swc/crates/next-font/src/font_imports_generator.rs deleted file mode 100644 index cc7bfeb6c324b..0000000000000 --- a/packages/next-swc/crates/next-font/src/font_imports_generator.rs +++ /dev/null @@ -1,272 +0,0 @@ -use serde_json::Value; -use swc_core::{ - common::{errors::HANDLER, Spanned, DUMMY_SP}, - ecma::{ - ast::*, - atoms::JsWord, - visit::{noop_visit_type, Visit}, - }, -}; - -pub struct FontImportsGenerator<'a> { - pub state: &'a mut super::State, - pub relative_path: &'a str, -} - -impl<'a> FontImportsGenerator<'a> { - fn check_call_expr( - &mut self, - call_expr: &CallExpr, - variable_name: &Result, - ) -> Option { - if let Callee::Expr(callee_expr) = &call_expr.callee { - if let Expr::Ident(ident) = &**callee_expr { - if let Some(font_function) = self.state.font_functions.get(&ident.to_id()) { - self.state - .font_functions_in_allowed_scope - .insert(ident.span.lo); - - let json: Result, ()> = call_expr - .args - .iter() - .map(|expr_or_spread| { - if let Some(span) = expr_or_spread.spread { - HANDLER.with(|handler| { - handler - .struct_span_err(span, "Font loaders don't accept spreads") - .emit() - }); - } - - expr_to_json(&expr_or_spread.expr) - }) - .collect(); - - if let Ok(json) = json { - let function_name = match &font_function.function_name { - Some(function) => String::from(&**function), - None => String::new(), - }; - let mut query_json_values = serde_json::Map::new(); - query_json_values.insert( - String::from("path"), - Value::String(self.relative_path.to_string()), - ); - query_json_values - .insert(String::from("import"), Value::String(function_name)); - query_json_values.insert(String::from("arguments"), Value::Array(json)); - if let Ok(ident) = variable_name { - query_json_values.insert( - String::from("variableName"), - Value::String(ident.sym.to_string()), - ); - } - - let query_json = Value::Object(query_json_values); - - return Some(ImportDecl { - src: Box::new(Str { - value: JsWord::from(format!( - "{}/target.css?{}", - font_function.loader, query_json - )), - raw: None, - span: DUMMY_SP, - }), - specifiers: vec![], - type_only: false, - asserts: None, - span: DUMMY_SP, - }); - } - } - } - } - - None - } - - fn check_var_decl(&mut self, var_decl: &VarDecl) -> Option { - if let Some(decl) = var_decl.decls.get(0) { - let ident = match &decl.name { - Pat::Ident(ident) => Ok(ident.id.clone()), - pattern => Err(pattern), - }; - if let Some(expr) = &decl.init { - if let Expr::Call(call_expr) = &**expr { - let import_decl = self.check_call_expr(call_expr, &ident); - - if let Some(mut import_decl) = import_decl { - match var_decl.kind { - VarDeclKind::Const => {} - _ => { - HANDLER.with(|handler| { - handler - .struct_span_err( - var_decl.span, - "Font loader calls must be assigned to a const", - ) - .emit() - }); - } - } - - match ident { - Ok(ident) => { - import_decl.specifiers = - vec![ImportSpecifier::Default(ImportDefaultSpecifier { - span: DUMMY_SP, - local: ident.clone(), - })]; - - self.state - .font_imports - .push(ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl))); - - return Some(ident); - } - Err(pattern) => { - HANDLER.with(|handler| { - handler - .struct_span_err( - pattern.span(), - "Font loader calls must be assigned to an identifier", - ) - .emit() - }); - } - } - } - } - } - } - None - } -} - -impl<'a> Visit for FontImportsGenerator<'a> { - noop_visit_type!(); - - fn visit_module_item(&mut self, item: &ModuleItem) { - match item { - ModuleItem::Stmt(Stmt::Decl(Decl::Var(var_decl))) => { - if self.check_var_decl(var_decl).is_some() { - self.state.removeable_module_items.insert(var_decl.span.lo); - } - } - ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => { - if let Decl::Var(var_decl) = &export_decl.decl { - if let Some(ident) = self.check_var_decl(var_decl) { - self.state - .removeable_module_items - .insert(export_decl.span.lo); - - self.state.font_exports.push(ModuleItem::ModuleDecl( - ModuleDecl::ExportNamed(NamedExport { - span: DUMMY_SP, - specifiers: vec![ExportSpecifier::Named(ExportNamedSpecifier { - orig: ModuleExportName::Ident(ident), - span: DUMMY_SP, - exported: None, - is_type_only: false, - })], - src: None, - type_only: false, - asserts: None, - }), - )); - } - } - } - _ => {} - } - } -} - -fn object_lit_to_json(object_lit: &ObjectLit) -> Value { - let mut values = serde_json::Map::new(); - for prop in &object_lit.props { - match prop { - PropOrSpread::Prop(prop) => match &**prop { - Prop::KeyValue(key_val) => { - let key = match &key_val.key { - PropName::Ident(ident) => Ok(String::from(&*ident.sym)), - key => { - HANDLER.with(|handler| { - handler - .struct_span_err(key.span(), "Unexpected object key type") - .emit() - }); - Err(()) - } - }; - let val = expr_to_json(&key_val.value); - if let (Ok(key), Ok(val)) = (key, val) { - values.insert(key, val); - } - } - key => HANDLER.with(|handler| { - handler.struct_span_err(key.span(), "Unexpected key").emit(); - }), - }, - PropOrSpread::Spread(spread_span) => HANDLER.with(|handler| { - handler - .struct_span_err(spread_span.dot3_token, "Unexpected spread") - .emit(); - }), - } - } - - Value::Object(values) -} - -fn expr_to_json(expr: &Expr) -> Result { - match expr { - Expr::Lit(Lit::Str(str)) => Ok(Value::String(String::from(&*str.value))), - Expr::Lit(Lit::Bool(Bool { value, .. })) => Ok(Value::Bool(*value)), - Expr::Lit(Lit::Num(Number { value, .. })) => { - Ok(Value::Number(serde_json::Number::from_f64(*value).unwrap())) - } - Expr::Object(object_lit) => Ok(object_lit_to_json(object_lit)), - Expr::Array(ArrayLit { - elems, - span: array_span, - .. - }) => { - let elements: Result, ()> = elems - .iter() - .map(|e| { - if let Some(expr) = e { - match expr.spread { - Some(spread_span) => HANDLER.with(|handler| { - handler - .struct_span_err(spread_span, "Unexpected spread") - .emit(); - Err(()) - }), - None => expr_to_json(&expr.expr), - } - } else { - HANDLER.with(|handler| { - handler - .struct_span_err(*array_span, "Unexpected empty value in array") - .emit(); - Err(()) - }) - } - }) - .collect(); - - elements.map(Value::Array) - } - lit => HANDLER.with(|handler| { - handler - .struct_span_err( - lit.span(), - "Font loader values must be explicitly written literals.", - ) - .emit(); - Err(()) - }), - } -} diff --git a/packages/next-swc/crates/next-font/src/lib.rs b/packages/next-swc/crates/next-font/src/lib.rs deleted file mode 100644 index aa29d03a1d151..0000000000000 --- a/packages/next-swc/crates/next-font/src/lib.rs +++ /dev/null @@ -1,88 +0,0 @@ -use rustc_hash::FxHashSet; -use serde::Deserialize; -use swc_core::{ - common::{collections::AHashMap, BytePos, Spanned}, - ecma::{ - ast::{Id, ModuleItem}, - atoms::JsWord, - visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitWith}, - }, -}; - -mod find_functions_outside_module_scope; -mod font_functions_collector; -mod font_imports_generator; - -#[derive(Clone, Debug, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "camelCase")] -pub struct Config { - pub font_loaders: Vec, - pub relative_file_path_from_root: JsWord, -} - -pub fn next_font_loaders(config: Config) -> impl Fold + VisitMut { - as_folder(NextFontLoaders { - config, - state: State { - ..Default::default() - }, - }) -} - -#[derive(Debug)] -pub struct FontFunction { - loader: JsWord, - function_name: Option, -} -#[derive(Debug, Default)] -pub struct State { - font_functions: AHashMap, - removeable_module_items: FxHashSet, - font_imports: Vec, - font_exports: Vec, - font_functions_in_allowed_scope: FxHashSet, -} - -struct NextFontLoaders { - config: Config, - state: State, -} - -impl VisitMut for NextFontLoaders { - noop_visit_mut_type!(); - - fn visit_mut_module_items(&mut self, items: &mut Vec) { - // Find imported functions from font loaders - let mut functions_collector = font_functions_collector::FontFunctionsCollector { - font_loaders: &self.config.font_loaders, - state: &mut self.state, - }; - items.visit_with(&mut functions_collector); - - if !self.state.removeable_module_items.is_empty() { - // Generate imports from font function calls - let mut import_generator = font_imports_generator::FontImportsGenerator { - state: &mut self.state, - relative_path: &self.config.relative_file_path_from_root, - }; - items.visit_with(&mut import_generator); - - // Find font function refs in wrong scope - let mut wrong_scope = - find_functions_outside_module_scope::FindFunctionsOutsideModuleScope { - state: &self.state, - }; - items.visit_with(&mut wrong_scope); - - // Remove marked module items - items.retain(|item| !self.state.removeable_module_items.contains(&item.span_lo())); - - // Add font imports and exports - let mut new_items = Vec::new(); - new_items.append(&mut self.state.font_imports); - new_items.append(items); - new_items.append(&mut self.state.font_exports); - *items = new_items; - } - } -}