Skip to content

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

Merged
owjs3901 merged 9 commits into
mainfrom
fix/eslint-alias-rules
Oct 14, 2026
Merged

owjs3901 merged 9 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] }} />는 변경 없음

2026-10-08 업데이트: main 11790bef를 merge commit으로 병합했습니다; 충돌 파일: 없음; 충돌 없이 자동 병합했고, 이미 포함된 squash 원본은 조상 확인 후 ours 전략으로 중복 적용하지 않았습니다.

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.83 → 1.0.84 - 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.
  • Tailwind classes in a className: each class compiles on its own and every class Tailwind does not define (custom, plugin and analytics classes) stays as written, where any recognized class used to rebuild the whole className without them and could rewrite part of another class; a class touching a template interpolation (icon-${name}) is left as written, and template classNames no longer get spaces inserted around interpolations. A class whose variant is not understood stays as written instead of applying unconditionally; variants follow Tailwind v4: stacked variants compose (hover:focus: matches the element itself), hover is guarded by @media (hover: hover), group-/peer- use the group/peer marker classes (and group-hover/name), and data-, aria-, has-, nth-, , **, supports-[…] and [&…]/[@media …] variants compile. Utilities emit all their declarations (size-, rounded-t-, truncate, sr-only, not-sr-only, text-sm with its line height, text-sm/6, leading-), translate/rotate/scale/skew compose through Tailwind's individual properties and registered variables (@Property) with negative values kept negative, and arbitrary values follow Tailwind's grammar: spaces for underscores except in url() and var() names, _ for an underscore, spaces around math operators, type hints, [property:value] and (--variable) shorthands. Words that only share a utility's prefix (select-wrapper, order-summary, cursor-foo) are no longer compiled

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

Patch

  • Bun plugin works with Bun.build: DevupUI() (from @devup-ui/bun-plugin/register) serves the generated stylesheet to the bundler's CSS loader once every other module is loaded, so the build emits a CSS output with the styles of the whole bundle instead of an empty module; class names are short there and readable under the runtime (debug option). Files importing the packages Devup UI takes the place of (@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css), @stylexjs/stylex, or a subpath or re-export of these or @devup-ui/react, are compiled instead of skipped

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

Patch

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

@devup-ui/eslint-plugin@1.0.22 → 1.0.23 - 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.

@devup-ui/next-plugin@1.0.90 → 1.0.91 - 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.17 → 1.0.18 - packages/plugin-utils/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles

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

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles

@devup-ui/vite-plugin@1.0.73 → 1.0.74 - 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.71 → 1.0.72 - packages/webpack-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles

@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 Δ
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%> (ø)
...nt-plugin/src/rules/no-useless-responsive/index.ts 100.00% <100.00%> (ø)
...plugin/src/rules/no-useless-tailing-nulls/index.ts 100.00% <100.00%> (ø)
...t-plugin/src/rules/prefer-media-shorthand/index.ts 100.00% <100.00%> (ø)
...eslint-plugin/src/rules/style-order-range/index.ts 100.00% <100.00%> (ø)
... and 2 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.

Merge main history while preserving this PR implementation. No manual conflict resolutions.
No conflicted files. The exact original #733 head fa67537 is already an ancestor; retain its implementation with the ours strategy.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
@owjs3901
owjs3901 merged commit 05aa5c8 into main Oct 14, 2026
5 checks passed
@owjs3901
owjs3901 deleted the fix/eslint-alias-rules branch October 14, 2026 07:10
owjs3901 added a commit that referenced this pull request Oct 14, 2026
Refs #770, #734, #735, #753, #739

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
(cherry picked from commit 7960b48)
owjs3901 added a commit that referenced this pull request Oct 14, 2026
Refs #770, #734, #735, #753, #739

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
(cherry picked from commit 7960b48)
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