Skip to content

feat(eslint-plugin): follow the import aliases and style positions the build compiles - #735

Open
owjs3901 wants to merge 4 commits into
mainfrom
fix/eslint-alias-rules
Open

owjs3901 wants to merge 4 commits into
mainfrom
fix/eslint-alias-rules

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Refs #684 (TOOL-03, TOOL-04, TOOL-05 / KNOWN-15, W4에서 보고한 한계)

#733(fix/eslint-autofix-scope) 위에 쌓인 PR입니다. base는 #733 브랜치이고, #733이 먼저 병합되어야 합니다(병합되면 base를 main으로 바꿉니다).

요약

  • 배열/typography/media/styleOrder 규칙과 css-utils-literal-only가 빌드가 기본으로 컴파일하는 import alias(@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css)와 그 스타일 위치를 그대로 따릅니다. 위치: css/keyframes/createGlobalStyle 호출, styled.div({...}), styled('div')({...}), styled('div', {...}), styled(Base)({...}, {...}), Emotion <Global styles>, VE style/globalStyle(일반 모듈에서). .attrs()/.withConfig() 인자와 CSS 텍스트는 스타일이 아닙니다.
  • 모듈 최상위 const s = { w: [1, 1] }; css(s)처럼 빌드가 인라인하는 상수 객체/배열을, export되지 않고 바뀌지 않고 스타일로만 읽힐 때 선언 위치에서 검사/수정합니다.
  • 새 규칙 @devup-ui/no-runtime-read(recommended 포함): css/keyframes/styled/Box를 값으로 읽는 코드를 빌드 에러와 같은 의미의 메시지로 미리 보고합니다(KNOWN-15, 맨 styled.div).
  • css-utils-literal-only: Emotion/styled-components 별칭 호출과 Emotion <Global styles>의 동적 값을 보고하고, .css.ts/.css.js(빌드가 통째로 실행)에서는 실행 환경에 없는 전역(window, document, process, setTimeout, fetch, URL 등, 빌드에서 직접 확인한 목록)의 읽기를 보고합니다(typeof 가드는 허용).

동작

  • 빌드가 읽는 곳만 검사합니다. 빌드가 .css.ts를 VE로 실행하는 파일에서는 배열이 반응형이 아니므로(대체 값) 모든 스타일 규칙이 꺼집니다. 다른 패키지(@emotion/css 등, 기본 alias에 없음)는 건드리지 않습니다.
  • no-runtime-read는 빌드의 CompiledReads와 같은 기준입니다: 호출/태그/요소/as={Text}/styled(Box)(...)/타입(typeof css)/최상위 별칭(const a = css)만 허용하고, 함수 안의 별칭 초기화, css.foo, [Box], createElement(Box), <Box.Foo />, styled['div'], 끝나지 않은 styled(Box) 체인 등은 보고합니다.
  • 저장소 자체 lint가 걸린 곳: 문서의 vanilla-extract 페이지 스니펫은 .css.ts 예제라서 해당 페이지에서만 css-utils-literal-only를 끄도록 eslint.config.mjs에 override를 넣었습니다.

새로 생기는 오류

  • 빌드 에러는 추가되지 않았습니다.
  • 새 lint 진단: @devup-ui/no-runtime-read(error, recommended), .css.ts에서 없는 전역 읽기(css-utils-literal-only의 missingGlobal), <Global styles>/별칭 호출의 동적 값.

남는 한계

  • 사용자가 설정한 추가 import alias(importAliases 옵션)와 @emotion/css처럼 기본에 없는 패키지는 lint가 알 수 없어 다루지 않습니다(빌드 설정을 읽지 않음).
  • 이 base에는 Emotion css prop이 아직 없어 다루지 않았습니다.
  • styleOrder는 상수 객체를 따라가지 않고 호출/요소에 직접 쓴 것만 봅니다. 네임스페이스 import(Devup.css) 값 읽기는 빌드도 보고하지 않으므로 no-runtime-read가 보지 않습니다.
  • (범위 밖, 발견) 함수 안의 const inner = { w: [2, 2] }; css(inner)는 빌드가 에러 없이 inner(객체 자체)를 결과로 남깁니다. 로컬 상수 객체를 읽지 못하는 조용한 오동작이라 별도 수정이 필요합니다.

검증

  • bun test 5816 pass / 0 fail, 커버리지 100%. bun lint 오류 0(기존 경고 2개). Rust 변경 없음.
  • RuleTester: 모든 alias × 위치(valid/invalid, 수정 결과 포함), 상수 따라가기(export/변경/함수 안 const/let/다른 읽기 → 건드리지 않음), .css.ts 무시, no-runtime-read 허용/보고 80여 건, .css.ts 전역 읽기, <Global styles> 동적 값.
  • 빌드(WASM) 대조 실행 결과와 lint 일치 확인(probe.ts vs ESLint):
    • export const runtime = css → 빌드 2:24 css is read at runtime, lint 같은 위치
    • export const a = styled.div.attrs({}) → 빌드/lint styled 보고, styled(Box) → styled와 Box 둘 다 보고
    • const z = css; z({p:1}) 허용, 함수 안 const q = css → css 보고(빌드와 동일)
    • <Global styles={{ body: { color: window.name } }} /> → 빌드/lint 보고
    • .css.ts의 style({ color: window.name }) → 빌드 ReferenceError: window is not defined, lint window 보고
    • styled('div', { w: [1, 1, null] }) 등 → 빌드가 읽는 스타일이므로 [1, null]/[1]로 수정, .attrs({ w: [1, 1] })와 <Global data-x={{ w: [1, 1] }} />는 변경 없음

owjs3901 and others added 3 commits October 3, 2026 15:50
Refs #684

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #684

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…e build compiles

Refs #684

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@owjs3901
owjs3901 changed the base branch from fix/eslint-autofix-scope to main October 3, 2026 07:57
Refs #684

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Changepacks

@devup-ui/wasm@1.0.82 → 1.0.83 - bindings/devup-ui-wasm/package.json

Patch

  • 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.
  • globalCss: theme tokens ($text) resolve to var(--text) in global rules instead of being written as invalid $text; imports given as { url, query } objects with unquoted keys are emitted instead of dropped; an '@layer name' key puts the rules it holds in that cascade layer (layers nest as base.reset) instead of flattening them into invalid declarations, and a layer name that is not one identifier is a build error
  • Theme CSS: a color only one variant defines is defined in that variant; a variant renders dark only when it is named dark or listed as dark in the new theme.colorScheme, others light, and light-dark() only encodes a light default with one dark variant; theme names that are not CSS identifiers are quoted in [data-theme]; typography emits fontStyle and textTransform; color variables come out in a fixed order. $tokens with dashes (text-primary) resolve instead of breaking into var(--text)-primary, and dotted length and shadow names are declared with dashes like their references. registerTheme reports, with the theme path, a token name the $token syntax cannot reach and two tokens of a variant that become the same CSS variable (a-b and a.b)

@devup-ui/bun-plugin@1.0.21 → 1.0.22 - packages/bun-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/plugin-utils' via a local workspace dependency

@devup-ui/components@0.1.59 → 0.1.60 - packages/components/package.json

Patch

  • Auto-update: depends on '@devup-ui/vite-plugin' via a local workspace dependency

@devup-ui/eslint-plugin@1.0.21 → 1.0.22 - packages/eslint-plugin/package.json

Patch

  • ESLint rules now check exactly what the build reads. The array, typography, media and styleOrder rules and css-utils-literal-only follow the import aliases the build compiles by default (@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css) and the rules of styled (styled.div({...}), styled("div", {...}), styled(Base)({...}, {...}); not .attrs()), createGlobalStyle({...}) and the styles of Emotion Global, and check and fix a module-level const object that only styles read. Vanilla-extract stylesheets (.css.ts/.css.js) are no longer read as Devup UI. New rule no-runtime-read reports css/keyframes/styled/Box read as a value ("X is read at runtime, where it does not exist"), and css-utils-literal-only reports globals that do not exist where a .css.ts is evaluated (window, document, process, ...). No new build errors.
  • 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.
  • no-duplicate-value, no-useless-responsive, no-useless-tailing-nulls, no-typography-token-prefix and prefer-media-shorthand only report and fix values the build reads as styles: style props of Box, Flex and the other style components and the arguments of css, globalCss and keyframes, through style objects, responsive arrays, conditions and spreads. Arrays and keys in props the component passes through (data-, aria-, event handlers, HTML attributes, props, styleVars), in arguments of other functions and under imports/fontFaces/params are left alone, where autofix used to rewrite them; styles of a component nested in another's prop are checked too. css-utils-literal-only reads a css() or keyframes() result held in a const of any scope as static, as the build does

@devup-ui/next-plugin@1.0.89 → 1.0.90 - packages/next-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/plugin-utils' via a local workspace dependency

@devup-ui/plugin-utils@1.0.16 → 1.0.17 - packages/plugin-utils/package.json

Patch

  • Theme CSS: a color only one variant defines is defined in that variant; a variant renders dark only when it is named dark or listed as dark in the new theme.colorScheme, others light, and light-dark() only encodes a light default with one dark variant; theme names that are not CSS identifiers are quoted in [data-theme]; typography emits fontStyle and textTransform; color variables come out in a fixed order. $tokens with dashes (text-primary) resolve instead of breaking into var(--text)-primary, and dotted length and shadow names are declared with dashes like their references. registerTheme reports, with the theme path, a token name the $token syntax cannot reach and two tokens of a variant that become the same CSS variable (a-b and a.b)

@devup-ui/rsbuild-plugin@1.0.66 → 1.0.67 - packages/rsbuild-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/plugin-utils' via a local workspace dependency

@devup-ui/vite-plugin@1.0.72 → 1.0.73 - packages/vite-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/plugin-utils' via a local workspace dependency

@devup-ui/webpack-plugin@1.0.70 → 1.0.71 - packages/webpack-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/plugin-utils' via a local workspace dependency

@codecov

codecov Bot commented Oct 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
bindings/devup-ui-wasm/src/lib.rs 100.00% <ø> (ø)
libs/css/src/theme_tokens.rs 100.00% <ø> (ø)
libs/extractor/src/lib.rs 100.00% <ø> (ø)
libs/extractor/src/tailwind.rs 100.00% <100.00%> (ø)
packages/eslint-plugin/src/configs/recommended.ts 100.00% <100.00%> (ø)
...t-plugin/src/rules/css-utils-literal-only/index.ts 100.00% <100.00%> (ø)
packages/eslint-plugin/src/rules/index.ts 100.00% <100.00%> (ø)
...slint-plugin/src/rules/no-duplicate-value/index.ts 100.00% <100.00%> (ø)
...s/eslint-plugin/src/rules/no-runtime-read/index.ts 100.00% <100.00%> (ø)
...ugin/src/rules/no-typography-token-prefix/index.ts 100.00% <100.00%> (ø)
... and 6 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant