Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changepacks/changepack_log_eslint_autofix_scope.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"changes": {
"packages/eslint-plugin/package.json": "Patch",
"bindings/devup-ui-wasm/package.json": "Patch"
},
"note": "style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.",
"date": "2026-10-01T00:00:00.000Z"
}
8 changes: 4 additions & 4 deletions bindings/devup-ui-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1551,7 +1551,7 @@ mod tests {
);

// Test getters
assert!(!output.code().is_empty());
assert_ne!(output.code(), "");
assert_eq!(output.css_file(), Some("devup-ui-0.css".to_string()));
assert_eq!(output.map(), Some("//# sourceMappingURL=test".to_string()));
assert!(output.css().is_some());
Expand Down Expand Up @@ -1951,7 +1951,7 @@ mod tests {

assert!(result.is_ok());
let output = result.unwrap();
assert!(!output.code().is_empty());
assert_ne!(output.code(), "");
assert!(output.map().is_some());
}

Expand All @@ -1975,7 +1975,7 @@ mod tests {

assert!(result.is_ok());
let output = result.unwrap();
assert!(!output.code().is_empty());
assert_ne!(output.code(), "");
assert!(output.map().is_none());
}

Expand All @@ -2000,7 +2000,7 @@ mod tests {

assert!(result.is_err());
if let Err(error) = result {
assert!(!error.is_empty());
assert_ne!(error, "");
}
}

Expand Down
2 changes: 1 addition & 1 deletion libs/css/src/theme_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ mod tests {
set_typography_keys(vec!["body".to_string(), "title".to_string()]);
assert_eq!(get_typography_keys(), vec!["body", "title"]);
set_typography_keys(vec![]);
assert!(get_typography_keys().is_empty());
assert_eq!(get_typography_keys(), Vec::<String>::new());
}

#[test]
Expand Down
8 changes: 4 additions & 4 deletions libs/extractor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -836,8 +836,8 @@ mod tests {
alternate: None,
};

assert!(empty.extract().is_empty());
assert!(empty.into_extract().is_empty());
assert_eq!(empty.extract(), vec![]);
assert_eq!(empty.into_extract(), vec![]);
}

#[test]
Expand Down Expand Up @@ -13598,7 +13598,7 @@ globalCss({
);
assert!(result.is_ok());
let output = result.unwrap();
assert!(!output.code.is_empty());
assert_ne!(output.code, "");
}

#[test]
Expand Down Expand Up @@ -18778,7 +18778,7 @@ export const k = styled('div')({ color: SIZE });",
&memory_resolver(CONSTANT_MODULES),
)
.unwrap();
assert!(without_imports.dependencies.is_empty());
assert_eq!(without_imports.dependencies.len(), 0);
let without_constants = extract_with_modules(
"/src/Handler.tsx",
"import { Box } from '@devup-ui/react';\nimport { handler } from './handler';\nexport const a = <Box onClick={handler} color='red' />;",
Expand Down
4 changes: 2 additions & 2 deletions libs/extractor/src/tailwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ pub struct TailwindClass {
/// non-overlapping) but mutates the existing buffer instead of allocating a new
/// `String`. `needle` must be non-empty.
fn remove_all_substr(haystack: &mut String, needle: &str) {
debug_assert!(!needle.is_empty());
debug_assert_ne!(needle, "");
let mut search_from = 0;
while let Some(rel) = haystack[search_from..].find(needle) {
let at = search_from + rel;
Expand Down Expand Up @@ -3934,7 +3934,7 @@ mod tests {
#[test]
fn test_empty_string() {
let styles = parse_tailwind_to_styles("");
assert!(styles.is_empty());
assert_eq!(styles, vec![]);
}

#[test]
Expand Down
4 changes: 4 additions & 0 deletions packages/eslint-plugin/src/rules/style-order-range/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ This rule enforces that the `styleOrder` prop must be a number greater than 0 an
<div styleOrder="254" />
```

## Where it applies

The rule checks `styleOrder` where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to `css`, `globalCss` or `keyframes`. A `styleOrder` in nested data (`data-*` props, `props`, an element passed as a prop, nested style objects) is not checked.

## When Not To Use It

If you don't use `styleOrder` props or want to allow any value range, you can disable this rule.
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,33 @@ describe('style-order-range rule', () => {
code: '<Box styleOrder={300} />',
filename: 'src/app/page.tsx',
},
{
code: 'import { Box, css } from "@devup-ui/react";\n<Box data-x={{ styleOrder: 0 }} icon={<div styleOrder={0} />} props={{ styleOrder: 300 }} />;\ncss({ _hover: { styleOrder: 0 }, w: [{ styleOrder: 0 }] })',
filename: 'src/app/page.tsx',
},
{
code: 'import { css } from "@devup-ui/react";\nconst other = { styleOrder: 0 };\nfoo({ styleOrder: 0 });\ncss({ [`styleOrder`]: 0, "styleOrder": 1 });\ncss({ w: css({ styleOrder: 1 }), styleOrder: 2 })',
filename: 'src/app/page.tsx',
},
{
code: 'import { css } from "@devup-ui/react";\nconst { styleOrder = 0 } = css({});\ntoString({ styleOrder: 0 });\nnew Foo({ styleOrder: 0 });',
filename: 'src/app/page.tsx',
},
],
invalid: [
{
code: 'import { css } from "@devup-ui/react";\ncss(base, { styleOrder: 300 })',
filename: 'src/app/page.tsx',
errors: [{ messageId: 'styleOrderRange' }],
},
{
code: 'import { Box, css } from "@devup-ui/react";\ncss({ w: css({ styleOrder: 0 }), styleOrder: 300 })',
filename: 'src/app/page.tsx',
errors: [
{ messageId: 'styleOrderRange' },
{ messageId: 'styleOrderRange' },
],
},
{
code: 'import { Box } from "@devup-ui/react";\n<Box styleOrder={0} />',
filename: 'src/app/page.tsx',
Expand Down
42 changes: 11 additions & 31 deletions packages/eslint-plugin/src/rules/style-order-range/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,55 +92,35 @@ export const styleOrderRange = createRule({
},
create(context) {
const importStorage = new ImportStorage()
let devupContext:
TSESTree.CallExpression | TSESTree.JSXOpeningElement | null = null

return {
ImportDeclaration(node) {
importStorage.addImportByDeclaration(node)
},
CallExpression(node) {
if (
importStorage.checkContextType(node) === 'UTIL' &&
node.arguments.length === 1 &&
node.arguments[0].type === AST_NODE_TYPES.ObjectExpression
) {
devupContext = node
}
},
'CallExpression:exit'(node) {
if (devupContext === node) {
devupContext = null
}
},
Property(node) {
// The build reads `styleOrder` only as a key of a style object handed to the utility itself
const object = node.parent
const call = object.parent
if (
devupContext &&
node.key.type === AST_NODE_TYPES.Identifier &&
!node.computed &&
node.key.name === 'styleOrder' &&
node.value.type !== AST_NODE_TYPES.AssignmentPattern &&
node.value.type !== AST_NODE_TYPES.TSEmptyBodyFunctionExpression
node.value.type !== AST_NODE_TYPES.TSEmptyBodyFunctionExpression &&
object.type === AST_NODE_TYPES.ObjectExpression &&
call?.type === AST_NODE_TYPES.CallExpression &&
call.arguments.includes(object) &&
importStorage.checkContextType(call) === 'UTIL'
) {
checkStyleOrderRange(node.value, context)
}
},
JSXOpeningElement(node) {
if (importStorage.checkContextType(node) === 'COMPONENT') {
devupContext = node
}
},
'JSXOpeningElement:exit'(node) {
if (devupContext === node) {
devupContext = null
}
},
JSXAttribute(node) {
if (!devupContext) return
// styleOrder prop만 체크
if (
node.name.type !== AST_NODE_TYPES.JSXIdentifier ||
node.name.name !== 'styleOrder' ||
!node.value
!node.value ||
importStorage.checkContextType(node.parent) !== 'COMPONENT'
) {
return
}
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-plugin/src/utils/import-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class ImportStorage {
private checkDevupUIUtil(node: TSESTree.CallExpression): boolean {
return (
(node.callee.type === AST_NODE_TYPES.Identifier &&
node.callee.name in this.imports) ||
this.importedName(node.callee.name) !== undefined) ||
(node.callee.type === AST_NODE_TYPES.MemberExpression &&
node.callee.object.type === AST_NODE_TYPES.Identifier &&
this.importObject.has(node.callee.object.name) &&
Expand All @@ -94,7 +94,7 @@ export class ImportStorage {
private checkDevupUIComponent(node: TSESTree.JSXTagNameExpression): boolean {
return (
(node.type === AST_NODE_TYPES.JSXIdentifier &&
node.name in this.imports) ||
this.importedName(node.name) !== undefined) ||
(node.type === AST_NODE_TYPES.JSXMemberExpression &&
node.object.type === AST_NODE_TYPES.JSXIdentifier &&
this.importObject.has(node.object.name) &&
Expand Down
Loading