Skip to content

Commit

Permalink
fix(emotion): Port label tests and fix label generation (#245)
Browse files Browse the repository at this point in the history
Apologies for the large PR. This PR addresses a number of issues with
label generation when attempting to find a label for `[Local]`.

All behaviour and code generation has been tested between the original
babel implementation and this plugin for consistency.

## Added
- All of the relevant fixtures from `@emotion` have been ported to this
repository, which exposed a number of label issues.
- Source:
https://github.com/emotion-js/emotion/tree/main/packages/babel-plugin/__tests__/css-macro/__fixtures__
  - Lives under `fixture/labels/emotion-js`
- All fixtures brought over contain links to their original
implementation, but have been extended in some cases (mainly the class
based tests).

## Changed
- Moved `label` related tests under a `label` folder in fixtures.
  - New structure: `fixture/labels/*`

## Fixed
- Spaces in names now collapsed to singular hyphens.
- Labels that are unable to find a context emit an empty string instead
of `[Local]`
- The following fixes have been made to align with the current
`@emotion/babel-plugin` behaviour:
  - Functions on classes now emit their class name as the label.
- Calls outside of an assignment (i.e. global scoped called to `css()`
do not emit a label.
- Keys on objects are now used as labels instead of the parent variable
assignment. (Fixes #180)
- Functions that return calls to `css` now use the function name as the
label. (Fixes #180)
- Named function expressions on variable assignments now use the correct
label.
  - String keys on objects now generate the expected label.
- Class methods use the class name for the label. (Upstream behaviour,
weirdly).
  - Class members use the member name for the label.
  - Computed properties should not generate a label.
  • Loading branch information
Codex- authored Jan 18, 2024
1 parent 8759fdf commit ab782e6
Show file tree
Hide file tree
Showing 113 changed files with 757 additions and 93 deletions.
44 changes: 22 additions & 22 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion packages/constify/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
license = "Apache-2.0"
name = "swc_plugin_constify"
publish = false
version = "0.3.0"
version = "0.4.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
2 changes: 1 addition & 1 deletion packages/constify/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@swc/plugin-constify",
"version": "0.1.39",
"version": "0.1.40",
"description": "SWC plugin for optimization",
"main": "swc_plugin_constify.wasm",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/constify/transform/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
license = "Apache-2.0"
name = "swc_constify"
repository = "https://github.com/swc-project/plugins.git"
version = "0.32.0"
version = "0.33.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
2 changes: 1 addition & 1 deletion packages/emotion/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
license = "Apache-2.0"
name = "swc_plugin_emotion"
publish = false
version = "0.14.0"
version = "0.15.0"

[lib]
crate-type = ["cdylib", "rlib"]
Expand Down
2 changes: 1 addition & 1 deletion packages/emotion/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@swc/plugin-emotion",
"version": "2.5.109",
"version": "2.5.110",
"description": "SWC plugin for emotion css-in-js library",
"main": "swc_plugin_emotion.wasm",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/emotion/transform/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
license = "Apache-2.0"
name = "swc_emotion"
repository = "https://github.com/swc-project/plugins.git"
version = "0.68.0"
version = "0.69.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
90 changes: 84 additions & 6 deletions packages/emotion/transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ use sourcemap::{RawToken, SourceMap as RawSourcemap};
use swc_atoms::JsWord;
use swc_common::{comments::Comments, util::take::Take, BytePos, SourceMapperDyn, DUMMY_SP};
use swc_ecma_ast::{
ArrayLit, CallExpr, Callee, Expr, ExprOrSpread, Id, Ident, ImportDecl, ImportSpecifier,
JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXElement, JSXElementName, JSXExpr,
JSXExprContainer, JSXObject, KeyValueProp, MemberProp, ModuleExportName, ObjectLit, Pat, Prop,
PropName, PropOrSpread, SourceMapperExt, SpreadElement, Tpl, VarDeclarator,
ArrayLit, CallExpr, Callee, ClassDecl, ClassMethod, ClassProp, Expr, ExprOrSpread, FnDecl, Id,
Ident, ImportDecl, ImportSpecifier, JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue,
JSXElement, JSXElementName, JSXExpr, JSXExprContainer, JSXObject, KeyValueProp, MemberProp,
MethodProp, ModuleExportName, ObjectLit, Pat, Prop, PropName, PropOrSpread, SourceMapperExt,
SpreadElement, Tpl, VarDeclarator,
};
use swc_ecma_utils::ExprFactory;
use swc_ecma_visit::{Fold, FoldWith};
Expand Down Expand Up @@ -76,6 +77,9 @@ static EMOTION_OFFICIAL_LIBRARIES: Lazy<Vec<EmotionModuleConfig>> = Lazy::new(||
]
});

static INVALID_LABEL_SPACES: Lazy<Regex> =
Lazy::new(|| RegexBuilder::new(r#"\s+"#).build().unwrap());

static INVALID_CSS_CLASS_NAME_CHARACTERS: Lazy<Regex> = Lazy::new(|| {
RegexBuilder::new(r##"[!"#$%&'()*+,./:;<=>?@\[\]^`|}~{]"##)
.build()
Expand Down Expand Up @@ -178,6 +182,7 @@ pub struct EmotionTransformer<C: Comments> {
import_packages: FxHashMap<Id, PackageMeta>,
emotion_target_class_name_count: usize,
current_context: Option<String>,
current_class: Option<String>,
// skip `css` transformation if it in JSX Element/Attribute
in_jsx_element: bool,

Expand Down Expand Up @@ -217,13 +222,19 @@ impl<C: Comments> EmotionTransformer<C> {
import_packages: FxHashMap::default(),
emotion_target_class_name_count: 0,
current_context: None,
current_class: None,
in_jsx_element: false,
registered_imports,
}
}

fn sanitize_label_part<'t>(&self, label_part: &'t str) -> Cow<'t, str> {
INVALID_CSS_CLASS_NAME_CHARACTERS.replace_all(label_part, "-")
fn sanitize_label_part<'t>(&self, label_part: &'t str) -> String {
// Existing @emotion/babel-plugin behaviour is to replace all spaces
// with a single hyphen
let without_spaces = INVALID_LABEL_SPACES.replace_all(label_part, "-");
INVALID_CSS_CLASS_NAME_CHARACTERS
.replace_all(&without_spaces, "-")
.to_string()
}

fn create_label(&self, with_prefix: bool) -> String {
Expand All @@ -244,6 +255,10 @@ impl<C: Comments> EmotionTransformer<C> {
if let Some(dirname) = self.dirname.as_ref() {
label = label.replace("[dirname]", &self.sanitize_label_part(dirname));
};
} else {
// Existing @emotion/babel-plugin behaviour is to
// not provide a label if there is no available identifier
return "".to_string();
}
label
}
Expand Down Expand Up @@ -430,9 +445,72 @@ impl<C: Comments> Fold for EmotionTransformer<C> {
if let Pat::Ident(i) = &dec.name {
self.current_context = Some(i.id.as_ref().to_owned());
}
// If we encounter a named function expression
if let Expr::Fn(f) = *dec.init.clone().unwrap() {
if let Some(i) = &f.ident {
self.current_context = Some(i.sym.as_ref().to_owned());
}
}
dec.fold_children_with(self)
}

fn fold_key_value_prop(&mut self, kv: KeyValueProp) -> KeyValueProp {
match &kv.key {
PropName::Ident(k) => {
self.current_context = Some(k.sym.as_ref().to_owned());
}
PropName::Str(k) => {
self.current_context = Some(k.value.as_ref().to_owned());
}
_ => (),
}
kv.fold_children_with(self)
}

fn fold_method_prop(&mut self, mp: MethodProp) -> MethodProp {
if let PropName::Ident(p) = &mp.key {
self.current_context = Some(p.sym.as_ref().to_owned());
}
mp.fold_children_with(self)
}

fn fold_fn_decl(&mut self, fn_dec: FnDecl) -> FnDecl {
self.current_context = Some(fn_dec.ident.sym.as_ref().to_owned());
fn_dec.fold_children_with(self)
}

fn fold_class_decl(&mut self, cd: ClassDecl) -> ClassDecl {
self.current_class = Some(cd.ident.sym.as_ref().to_owned());
self.current_context = self.current_class.clone();
cd.fold_children_with(self)
}

fn fold_class_method(&mut self, cm: ClassMethod) -> ClassMethod {
// class methods use the class name for the context
if self.current_class.is_some() {
self.current_context = self.current_class.clone();
}
cm.fold_children_with(self)
}

fn fold_class_prop(&mut self, cp: ClassProp) -> ClassProp {
if let PropName::Ident(p) = &cp.key {
self.current_context = Some(p.sym.as_ref().to_owned());
}
cp.fold_children_with(self)
}

fn fold_computed_prop_name(
&mut self,
n: swc_ecma_ast::ComputedPropName,
) -> swc_ecma_ast::ComputedPropName {
// Existing @emotion/babel-plugin behaviour is that computed
// properties do not have a label. We reset the label here as
// an unset label is reduced to an empty string in `create_label`.
self.current_context = None;
n.fold_children_with(self)
}

fn fold_call_expr(&mut self, mut expr: CallExpr) -> CallExpr {
// If no package that we care about is imported, skip the following
// transformation logic.
Expand Down
Loading

0 comments on commit ab782e6

Please sign in to comment.