@axis-ds/cli 1.7.6 → 1.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -95,25 +95,28 @@ axis theme add ontology
95
95
 
96
96
  ## 프로젝트 검증 (`axis check`)
97
97
 
98
- - Tailwind 구성 항목: Tailwind 4 이상(설치본, 없으면 `package.json` 선언으로 판정)은 설정 파일 대신 전역 CSS(`globals.css` · `app/globals.css` · `src/app/globals.css` · `src/globals.css` · `styles/globals.css` 순으로 처음 찾은 파일 하나)를 봅니다. Tailwind 3 이하는 `tailwind.config.ts` · `.js` · `.mjs` · `.cjs` 를 찾습니다.
98
+ - 전역 CSS 선택(1.7.7 부터): 전역 CSS 를 읽는 세 항목(Tailwind 4 의 Tailwind 구성 항목 · globals.css 항목 · Tailwind 4 의 animation 항목)은 같은 파일 하나를 봅니다. `axis.config.json` 의 `globalCss` 가 가리키는 파일이 있고 `.css` 이면 그 파일입니다. 모노레포의 `../shared/globals.css` 처럼 프로젝트 밖 경로도 읽기만 합니다(`axis theme add` 는 종전대로 밖 파일에 쓰지 않습니다). `globalCss` 키가 없거나 그 파일을 쓸 수 없으면 `globals.css` · `app/globals.css` · `src/app/globals.css` · `src/globals.css` · `styles/globals.css` 순으로 처음 찾은 파일이고, 쓸 수 없었던 이유를 항목 문구 끝에 붙입니다. 없는 파일 · 디렉터리면 `(globalCss ./nope.css 없음)`, `.css` 가 아닌 파일이면 `(globalCss ./package.json: .css 파일이 아님)` 입니다. brand 항목도 같은 `globalCss` 규칙과 표기를 쓰지만 후보 목록이 다릅니다(`app/globals.css` → `src/app/globals.css` → `styles/globals.css` → `src/styles/globals.css` → 루트 `globals.css`). 세 항목도 후보 이름과 같은 디렉터리(예: 루트 `globals.css/`)는 건너뜁니다(1.7.6 까지는 check 가 `EISDIR` 로 멈췄습니다). 1.7.6 까지는 세 항목이 `globalCss` 를 보지 않아, CSS 를 후보 밖(`src/styles/globals.css` 등)에 두고 `globalCss` 로 가리킨 프로젝트도 globals.css FAIL(종료 코드 1)이었고, 루트에 다른 `globals.css` 가 있으면 brand 와 다른 파일을 판정했습니다.
99
+ - 상대 `@import` 펼침(1.7.7 부터): 고른 파일이 `@import "./axis.css";` · `@import '../styles/axis.css';` 처럼 따옴표 안 상대 경로(`./` · `../`, 공백 없는 `@import"./axis.css"` · 확장자 없는 `"./axis"` 포함)로 들여온 프로젝트 안 CSS 도 근거 줄(`@import "tailwindcss"` · 토큰 진입점 · tokens CSS import · `--axis-*` 정의 · 컴포넌트 `tailwind.css` · `@source` · `tw-animate-css` · `@plugin "tailwindcss-animate"` · 브랜드 import)을 찾을 때 함께 봅니다. 근거를 하위 파일에서 찾으면 문구 끝(`globalCss` 표기 앞)에 `(app/axis.css 의 import)` 처럼 출처를 붙이고, 하위 파일이 없으면 종전 문구 그대로입니다. 확장자 없는 이름은 적힌 이름의 파일이 먼저이고 없으면 끝에 `.css` 를 붙인 파일을 봅니다(Tailwind 4 와 같음). 하위 파일이 다시 import 한 파일도 진입 파일에서 32단계까지 따라가고, 같은 파일은 한 번만 읽습니다. 프로젝트 밖으로 나가는 경로(`../` · 밖을 가리키는 심볼릭 링크), 없는 파일 · 디렉터리, `url(...)` 형태, `layer()` · `supports()` · media 조건이 붙은 import(조건 안의 토큰 진입점은 Tailwind 4 빌드가 실패함. Tailwind 3 은 조건이 붙은 import 도 펼쳐 빌드하지만 check 는 따라가지 않아 그 파일의 줄을 근거로 보지 않습니다), `./` 없는 이름(`"axis.css"`, 패키지 경로로 풀림)은 따라가지 않습니다. 프로젝트 밖 전역 CSS 가 다시 밖 파일을 import 해도 읽지 않습니다. 상대 import 가 순환하면(a → b → a · 자기 import) 빌드가 실패하므로 globals.css 항목은 진입 파일만으로 판정한 문구 뒤에 ` / ` 로 순환 문구를 붙여 WARN 을 냅니다(예: `app/globals.css에 AXIS CSS 변수가 없습니다 / app/globals.css · app/b.css 이 서로 @import 해 빌드가 순환으로 실패합니다 → 순환하는 @import 를 없애세요`, 자기 자신을 import 하면 `app/globals.css 이 자기 자신을 @import 해 빌드가 순환으로 실패합니다 → 순환하는 @import 를 없애세요`). 진입 파일이 PASS 여도 WARN 이고, 이때는 모든 항목이 진입 파일만 보며 순환 문구는 globals.css 항목에만 붙습니다. 순환은 조건이 붙은 import 까지 넣어 Tailwind 4 의 재귀 한도와 같은 100단계까지 찾습니다(근거는 위와 같이 조건 없는 import · 32단계까지만 봄). 하위 파일의 패키지 경로 줄은 진입 파일에 같은 줄이 있을 때와 같은 해석 판정(아래 Tailwind 구성 항목 2 · 5번 · globals.css 항목)을 그 하위 파일의 디렉터리 기준(그 디렉터리의 중첩 `node_modules` 포함)으로 거치고, 브랜드 import 는 종전대로 그 판정 없이 위치만 봅니다. create-next-app 기본 정의 · `font-family: Arial` 같은 결함은 진입 파일에서만 봅니다. 1.7.6 까지는 하위 파일을 보지 않아, 근거를 하위 파일에 둔 프로젝트도 「없습니다」 WARN 이었습니다(종료 코드는 그대로).
100
+ - Tailwind 구성 항목: Tailwind 4 이상(설치본, 없으면 `package.json` 선언으로 판정)은 설정 파일 대신 위 전역 CSS 선택이 고른 파일 하나(상대 `@import` 로 들여온 하위 파일 포함)를 봅니다. Tailwind 3 이하는 `tailwind.config.ts` · `.js` · `.mjs` · `.cjs` 를 찾습니다.
99
101
  - Tailwind 4: `@import "tailwindcss"` 가 있는 CSS 에서 아래 문제가 하나도 없으면 PASS 이고, 하나라도 있으면 WARN 입니다(블록 주석 안은 없는 것으로 봅니다). `@import "tailwindcss"` 가 없으면 그 WARN 하나만 냅니다.
100
102
  1. 토큰 진입점 `@import "@axis-ds/tokens/css/tailwind4";` 이 없음 → 문구에 넣을 import 줄. 진입점이 없으면 `bg-primary` · `rounded-md` 같은 AXIS 매핑이 만들어지지 않습니다. `@axis-ds/tokens/css` · `css/shadcn` · `tailwind`(Tailwind 3 preset)는 진입점으로 치지 않습니다.
101
- 2. `package.json` 에 선언된 `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 의 `tailwind.css` import 가 없음 → 빠진 import 줄. 선언이 없으면(복사 설치) 요구하지 않습니다. `tailwind.css` 대신 그 패키지를 가리키는 `@source` 를 같은 CSS 에 직접 썼으면(예: `@source "../node_modules/@axis-ds/ui-react/dist";`, 작은따옴표 · 다른 상대 경로 포함) 스캔 경로가 있는 것으로 봅니다. 경로 문자열에 `@axis-ds/<패키지>` 가 들어 있고 그 바로 뒤가 경로 끝 · `/` · `\` 이면(`@axis-ds/ui-react-extra` 같은 다른 이름은 아님) 그 패키지를 가리킨다고 판정합니다(경로가 실제로 있는지는 보지 않음). 주석 안 · `@source not` · `@source inline()` 은 인정하지 않고, brace 확장(`@source "{…/@axis-ds/ui-react,…}/dist"`) · `@import "…" source("…")` 형태도 인정하지 않습니다(WARN 쪽으로 판정).
103
+ 2. `package.json` 에 선언된 `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 의 `tailwind.css` import 가 없음 → 빠진 import 줄. 선언이 없으면(복사 설치) 요구하지 않습니다. 1.7.7 부터는 그 줄이 이 앱에서 해석될 때만 줄을 바로 안내합니다. 앱 `package.json` 선언과, 설치본에 `tailwind.css` 가 있는 워크스페이스 루트 선언은 종전대로 줄을 안내합니다. 해석되지 않는 루트 선언은 먼저 할 일을 안내합니다: Plug'n'Play 에서 루트에만 선언(→ 앱 `package.json` 에 선언한 뒤 줄), 설치본 없음(→ 설치 뒤 줄), 설치본이 1.4.0 미만이거나 버전을 읽지 못함(→ 1.4.0 이상으로 올린 뒤 줄), 1.4.0 이상인데 `tailwind.css` 가 없음(→ 다시 설치 뒤 줄). 앱 디렉터리에 lockfile 이 있으면 루트 선언을 선언으로 보지 않습니다(`axis theme add` 와 같은 판정). 1.7.6 까지는 선언만 보고 줄을 안내해, 그대로 넣으면 빌드가 `Can't resolve` 로 깨질 수 있었습니다. `tailwind.css` 대신 그 패키지를 가리키는 `@source` 를 같은 CSS 에 직접 썼으면(예: `@source "../node_modules/@axis-ds/ui-react/dist";`, 작은따옴표 · 다른 상대 경로 포함) 스캔 경로가 있는 것으로 봅니다. 경로 문자열에 `@axis-ds/<패키지>` 가 들어 있고 그 바로 뒤가 경로 끝 · `/` · `\` 이면(`@axis-ds/ui-react-extra` 같은 다른 이름은 아님) 그 패키지를 가리킨다고 판정합니다(경로가 실제로 있는지는 보지 않음). 주석 안 · `@source not` · `@source inline()` 은 인정하지 않고, brace 확장(`@source "{…/@axis-ds/ui-react,…}/dist"`) · `@import "…" source("…")` 형태도 인정하지 않습니다(WARN 쪽으로 판정).
102
104
  3. 진입점이 있는데 create-next-app 기본 정의가 남음 → 실제로 남은 정의 이름(예: `--font-sans` 만 남았으면 `@theme --font-sans: var(--font-geist-sans)` 만 표시). `--background` · `--foreground` 를 색값(`#ffffff` · `rgb()` · `oklch()` 등)으로 정의, `--color-background: var(--background)` 류, `--font-sans` · `--font-mono: var(--font-geist-*)`, `background` · `color: var(--background)` 류 직접 사용을 봅니다(create-next-app 15.5.28 기본 `globals.css` 실물 기준). AXIS 의 HSL 삼중값(`0 0% 100%`) 재정의나 `hsl(var(--background))` 는 걸리지 않습니다. 표시 앞머리(`:root 색값` · `@theme` · `body`)는 create-next-app 기본 블록 이름이라, 같은 선언이 다른 선택자에 있어도 그 이름으로 표시됩니다.
103
105
  4. 진입점이 있는데 `@axis-ds/tokens` 가 아래 버전 조건에 걸림.
104
- 5. `@axis-ds/ui-react/tailwind.css` · `@axis-ds/agentic-ui/tailwind.css` 를 import 하는데 그 패키지가 아래 버전 조건에 걸림(`tailwind.css` 는 1.4.0 부터 있음). 직접 `@source` 로 스캔하는 패키지는 `tailwind.css` 가 필요 없으므로 이 문제를 만들지 않습니다.
106
+ 5. `@axis-ds/ui-react/tailwind.css` · `@axis-ds/agentic-ui/tailwind.css` 를 import 하는데 그 패키지가 아래 버전 조건에 걸림(`tailwind.css` 는 1.4.0 부터 있음). 직접 `@source` 로 스캔하는 패키지는 `tailwind.css` 가 필요 없으므로 이 문제를 만들지 않습니다. 1.7.7 부터는 줄이 이미 있어도 2번의 해석되지 않는 루트 선언(Plug'n'Play 루트 전용 · 설치본 없음 · 설치본에 `tailwind.css` 없음 · 버전을 읽지 못함)이면 버전 조건 대신 「`<패키지>/tailwind.css` import 가 있지만 … 해석되지 않습니다」 WARN 입니다(1.4.0 미만 설치본은 종전 버전 조건 문구, 1.7.6 까지는 버전 조건만 봐서 PASS 일 수 있었음).
107
+ - 프로젝트 밖 전역 CSS(1.7.7 부터): 고른 파일이 앱 밖(`globalCss` 가 `../shared/globals.css` 등)이면 그 파일의 `@import "tailwindcss"` 와 컴포넌트 `tailwind.css` 줄을 그 파일 위치 기준으로도 봅니다. 빌드 도구가 패키지 경로를 그 CSS 파일 위치부터 상위 `node_modules` 로 풀기 때문입니다(설치본이 앱 아래에만 있으면 빌드가 `Can't resolve` 로 실패, 공통 상위로 호이스팅하면 성공). 앱에서는 설치본을 찾는데 그 파일 위치에서는 찾지 못하면 「import 가 있지만 이 파일 위치에서 <패키지> 를 찾지 못해 해석되지 않습니다」 WARN 입니다(`tailwindcss` 줄은 문제 목록 맨 앞, 컴포넌트 줄은 5번 자리). 토큰 진입점 줄은 globals.css 항목이 같은 기준으로 알립니다. Plug'n'Play 이거나 앱 위치에서 설치본을 찾지 못하면 이 판정을 하지 않습니다.
105
108
  - 문제가 여럿이면 위 순서대로 ` / ` 로 이어 한 항목에 냅니다. 끝에는 `package.json` 에 선언된 세 AXIS 패키지 가운데 1.4.0 미만인 것을 `(1.4.0 이상 필요: @axis-ds/tokens ^1.3.0)`, 판정할 수 없는 것을 `(설치된 버전이 1.4.0 이상인지 확인하세요: @axis-ds/agentic-ui catalog:)` 로 한 번 덧붙입니다. 버전은 `axis init` 과 같은 함수로 판정합니다: 설치본(`node_modules/<패키지>/package.json` 의 정확한 `version`)이 있으면 그것으로 보고(`(1.4.0 이상 필요: @axis-ds/ui-react 설치본 1.3.0)`, 1.4.0 이상이면 선언과 관계없이 넣지 않음), 없으면 선언 범위의 하한, 하한도 읽을 수 없으면(`catalog:` · `latest` · `||` 등) 판정 불가입니다. `workspace:` 는 넣지 않습니다.
106
109
  - 문구 예: `Tailwind 4: globals.css에 AXIS 토큰 진입점이 없습니다 → @import "@axis-ds/tokens/css/tailwind4"; / 컴포넌트 스캔 경로(tailwind.css)가 없습니다 → @import "@axis-ds/ui-react/tailwind.css"; (1.4.0 이상 필요: @axis-ds/tokens ^1.3.0 · @axis-ds/ui-react ^1.3.0)`
107
110
  - Tailwind 3: 설정 파일(`.ts` · `.js` · `.mjs` · `.cjs`)이 `@axis-ds/tokens/tailwind` preset 을 `require` · `import` 하면(주석 밖) `tailwind.config.ts에 AXIS 토큰이 설정되어 있습니다 (@axis-ds/tokens/tailwind preset)` 처럼 PASS 이고, 없으면 WARN 입니다(1.6.0 부터. 이전에는 `--background` · `hsl(var(` 문자열만 있어도 PASS 여서 AXIS 참조가 없는 자체 매핑도 통과했습니다).
108
111
  - Tailwind 3 CommonJS(1.6.1 부터): 설정이 `.cjs` 이거나 `module.exports` 를 쓰고 `.default` 없는 `require("@axis-ds/tokens/tailwind")` 가 있으면, 설치본(없으면 선언 하한) `@axis-ds/tokens` 가 1.7.0 미만일 때 WARN 입니다. 1.6.2 이하의 이 형태는 preset 이 아니라 `{ default }` 를 받아 색 · keyframes 가 빠집니다(1.1.0 부터). `.ts` · `.mjs` 의 `export default` 안 `require` 와 `import` 형태는 Tailwind 가 `.default` 를 꺼내 줘서 PASS 입니다.
109
- - globals.css 항목: 같은 후보에서 처음 찾은 CSS 하나를 봅니다. 1.6.1 부터 AXIS 근거가 있을 때만 PASS 입니다: `@axis-ds/tokens/css` · `@axis-ds/tokens/css/shadcn` · `@axis-ds/tokens/css/tailwind4` import(블록 주석 밖, 작은따옴표 · `url()` · `layer()` 표기 포함, 문구 끝에 `(@axis-ds/tokens/css/tailwind4 import)` 처럼 경로), `--axis-*` 변수 정의, 또는 루트 레이아웃(`app/layout.tsx` 등)의 `import "@axis-ds/tokens/css"`. 자체 `--background` · `--primary` 만 있으면 「자체 CSS 변수만 있고 AXIS 토큰이 없습니다」 WARN 입니다(1.6.0 까지는 PASS). Tailwind 3 에서 create-next-app 기본 `body` 의 `font-family: Arial` 이 남아 있으면 PASS 조건을 채워도 WARN 입니다(Tailwind 4 는 tailwind.config 항목의 create-next-app 기본 정의로 냅니다).
112
+ - globals.css 항목: 위 전역 CSS 선택이 고른 CSS 하나(상대 `@import` 로 들여온 하위 파일 포함, Arial 은 진입 파일만)를 봅니다(Tailwind 3 이하도 같음). 고를 파일이 하나도 없으면 FAIL 입니다. 1.6.1 부터 AXIS 근거가 있을 때만 PASS 입니다: `@axis-ds/tokens/css` · `@axis-ds/tokens/css/shadcn` · `@axis-ds/tokens/css/tailwind4` import(블록 주석 밖, 작은따옴표 · `url()` · `layer()` 표기 포함, 문구 끝에 `(@axis-ds/tokens/css/tailwind4 import)` 처럼 경로), `--axis-*` 변수 정의, 또는 루트 레이아웃(`app/layout.tsx` 등)의 `import "@axis-ds/tokens/css"`. 1.7.7 부터 tokens import(CSS · 루트 레이아웃 JS)는 이 앱에서 해석될 때만 근거로 칩니다. 앱 `package.json` 에 `@axis-ds/tokens` 를 선언했으면 설치 여부와 상관없이 종전대로 PASS 이고(미설치는 dependencies 항목이 알림), 선언이 없거나 워크스페이스 루트에만 선언했으면 설치본 디렉터리(`node_modules/@axis-ds/tokens`, 상위 디렉터리 포함)가 있을 때 PASS 입니다. 설치본이 없으면 「<경로> import 가 있지만 @axis-ds/tokens 가 설치되지 않아 해석되지 않습니다 → 설치(…)」, Plug'n'Play 에서 루트에만 선언했으면 「… 워크스페이스 루트에만 선언돼 Plug'n'Play 에서 앱이 해석하지 못합니다 → 앱 package.json 에 선언하세요」 WARN 입니다(1.7.6 까지는 PASS, 종료 코드는 그대로). 고른 파일이 프로젝트 밖이면 Tailwind 구성 항목과 같이 그 파일 위치에서 설치본이 보이는지도 봅니다. `--axis-*` 변수 정의는 이 판정을 거치지 않습니다. 단, 해석되지 않는 tokens import 가 함께 있으면 import 판정을 따라 WARN 입니다. 자체 `--background` · `--primary` 만 있으면 「자체 CSS 변수만 있고 AXIS 토큰이 없습니다」 WARN 입니다(1.6.0 까지는 PASS). Tailwind 3 에서 create-next-app 기본 `body` 의 `font-family: Arial` 이 남아 있으면 PASS 조건을 채워도 WARN 입니다(Tailwind 4 는 tailwind.config 항목의 create-next-app 기본 정의로 냅니다).
110
113
  - dependencies 항목(1.6.1 부터): `tailwindcss` 선언이 없으면 FAIL, `clsx` · `tailwind-merge` 선언이 없으면 WARN(종전과 같음). 여기에 선언된 `tailwindcss` · `clsx` · `tailwind-merge` · `@axis-ds/*` 가 `node_modules` 에 없으면 「선언됐지만 설치되지 않은 의존성」 WARN 과 lockfile 에 맞는 설치 명령을 냅니다. 상위 디렉터리의 `node_modules`(모노레포 호이스팅)도 찾고, Plug'n'Play(`.pnp.cjs`)는 설치본을 읽지 않고 선언 기준입니다. 1.6.0 까지는 선언만 보고 「모두 설치되어 있습니다」였습니다.
111
114
  - 1.7.6 부터 워크스페이스 루트 `package.json` 에 선언한 `@axis-ds/*`(앱에 없는 것)도 설치 확인 대상이고, 설치되지 않았으면 「워크스페이스 루트에 선언돼 있지만 설치되지 않았습니다」 WARN 입니다(종료 코드는 그대로, 1.7.5 까지는 앱 선언만 봐서 PASS).
112
115
  - 1.7.6 부터 Plug'n'Play 는 워크스페이스 루트 선언을 인정하지 않습니다. 앱이 자기 `package.json` 에 없는 패키지를 해석하지 못하기 때문입니다. 루트에만 있는 `tailwindcss` 는 「워크스페이스 루트에만 선언돼 Plug'n'Play 에서 앱이 해석하지 못합니다」 FAIL, `clsx` · `tailwind-merge` · `@axis-ds/*` 는 같은 문구의 WARN 입니다(1.7.5 까지는 PASS). `.pnp.cjs` 는 `.git` 이 있는 층에서 찾기를 멈춥니다. `pnpFallbackMode: all` 설정은 읽지 않습니다.
113
116
  - 해당할 때만 더하는 항목(1.6.0 부터, brand 항목 뒤 이 순서):
114
117
  - `brand.activation`: `brand` 가 있을 때 루트 레이아웃(`app/layout.tsx` · `src/app/layout.tsx` · `pages/_app` 등, 그 레이아웃이 import 하는 같은 디렉터리의 `./providers` 한 단계 포함)에 `data-brand` · `setBrand(` · `defaultBrand=` 가 없으면 WARN 입니다(휴리스틱, `ThemeProvider` 단독은 켜는 코드가 아님). 1.6.1 부터 마크업 `data-brand` 와 `ThemeProvider` 만 있고 `setBrand(` · `defaultBrand=` 가 없으면 설치본(없으면 선언) `@axis-ds/theme` 가 1.7.0 미만이거나 판정할 수 없을 때 WARN 입니다(1.6.2 이하는 마운트 때 속성을 지움). 1.7.6 부터 Plug'n'Play 에서 `@axis-ds/theme` 가 워크스페이스 루트에만 선언돼 있으면 「앱이 해석하지 못합니다」 WARN 입니다(Tailwind 3 CommonJS preset 의 `@axis-ds/tokens` 판정도 같음).
115
- - `tailwind.content`: Tailwind 3 설정 파일이 있고 `componentsDir` 나 선언된 `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 가 있을 때, `content` 의 문자열 glob 이 그 경로를 덮지 않으면 FAIL 입니다(그 경로의 클래스가 생성되지 않음). `content` 를 정적으로 다 읽지 못하면(변수 · 펼침) WARN 입니다. 패키지 경로는 프로젝트 디렉터리부터 상위로 찾은 설치본 위치 기준이라, npm workspaces · pnpm hoisted 처럼 루트에만 설치된 앱은 `../../node_modules/@axis-ds/ui-react/dist/**/*.{js,mjs}` 를 안내합니다(1.7.1 부터, 설치본이 없거나 Plug'n'Play 면 `./node_modules/...`). `**` 는 `../` 를 넘지 않는 것으로, 절대경로 glob 은 실제 위치와 비교해 판정합니다. 1.7.4 부터 pnpm 기본 레이아웃처럼 앱 설치본이 링크이면 실경로가 같은 상위 설치본(`../../node_modules/...`)과 `.pnpm` 스토어 실경로를 상대 경로로 쓴 glob 도 인정하고, 실경로가 다른 상위 설치본을 가리키면 FAIL 문구에 그 이유를 붙입니다. 상위 경로는 Tailwind 3 가 빌드를 실행한 디렉터리 기준으로 풀리므로 모노레포 루트에서 빌드하면 `content: { relative: true, files: [...] }` 를 쓰세요(판정은 프로젝트 디렉터리에서 빌드한다고 보고 함).
116
- - `animation`: `@axis-ds/ui-react` 선언이나 `componentsDir` 의 `animate-in` 류 클래스가 있을 때 애니메이션 플러그인(Tailwind 3 `tailwindcss-animate` 선언 + 설치본 + `plugins` 배열 참조, Tailwind 4 `@import "tw-animate-css"`)이 없으면 WARN 입니다. 1.6.1 부터 Tailwind 3 은 `plugins` 배열 안의 `require("tailwindcss-animate")` 나 그 패키지를 받은 식별자(`import animate from` · `const animate = require(...)`)만 인정합니다(import 만 있거나 다른 곳의 같은 문자열은 아님). 배열을 정적으로 다 읽지 못하면(펼침 · 단축 속성) 「확인하지 못함」 WARN 입니다. 1.7.6 부터 Tailwind 4 는 CSS 의 줄마다 그 줄의 패키지(`tw-animate-css` · `tailwindcss-animate`)가 선언 · 설치돼 있어야 PASS 이고, 줄은 있는데 선언이나 설치본이 없으면 「애니메이션 플러그인을 쓸 수 없습니다」 WARN 입니다(1.7.5 까지는 줄만 보고 PASS, 실제 빌드는 실패). Plug'n'Play 에서는 플러그인 선언을 앱 `package.json` 에서만 봅니다.
118
+ - `tailwind.content`: Tailwind 3 설정 파일이 있고 `componentsDir` 나 선언된 `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 가 있을 때, `content` 의 문자열 glob 이 그 경로를 덮지 않으면 FAIL 입니다(그 경로의 클래스가 생성되지 않음). `content` 를 정적으로 다 읽지 못하면(변수 · 펼침) WARN 입니다. 패키지 경로는 프로젝트 디렉터리부터 상위로 찾은 설치본 위치 기준이라, npm workspaces · pnpm hoisted 처럼 루트에만 설치된 앱은 `../../node_modules/@axis-ds/ui-react/dist/**/*.{js,mjs}` 를 안내합니다(1.7.1 부터, 설치본이 없으면 `./node_modules/...`). `**` 는 `../` 를 넘지 않는 것으로, 절대경로 glob 은 실제 위치와 비교해 판정합니다. 1.7.4 부터 pnpm 기본 레이아웃처럼 앱 설치본이 링크이면 실경로가 같은 상위 설치본(`../../node_modules/...`)과 `.pnpm` 스토어 실경로를 상대 경로로 쓴 glob 도 인정하고, 실경로가 다른 상위 설치본을 가리키면 FAIL 문구에 그 이유를 붙입니다. 상위 경로는 Tailwind 3 가 빌드를 실행한 디렉터리 기준으로 풀리므로 모노레포 루트에서 빌드하면 `content: { relative: true, files: [...] }` 를 쓰세요(판정은 프로젝트 디렉터리에서 빌드한다고 보고 함). 1.7.7 부터 Plug'n'Play(`.pnp.cjs`)에서는 node_modules 가 없으므로 `./node_modules/...` glob 과 넓은 `./**` 를 덮음으로 보지 않고 WARN 으로 알리고(종전 PASS, 실제 빌드는 클래스가 생기지 않음. Tailwind 의 `**` · `*` 는 `.yarn` 처럼 점으로 시작하는 디렉터리를 넘지 않으므로 앱이 Plug'n'Play 루트이고 unplugged 가 있어도 같음), 덮지 않으면 FAIL 은 그대로이되 `./node_modules/...` 대신 `require.resolve` 로 실제 위치를 넣는 동적 형태(정적으로 확인할 수 없어 WARN)를 먼저, `dependenciesMeta` 의 `unplugged: true` 를 대안으로 안내합니다(안내에는 덮지 않은 패키지 이름이 들어감). unplugged 를 설정한 프로젝트는 앱 선언 범위에 맞는 `.yarn/unplugged/*/node_modules/<패키지>` 를 가리키는 glob(상대 · 절대, `.yarn` 을 글자 그대로 쓴 것)을 덮음으로 인정합니다(종료 코드가 1 에서 0 으로 바뀔 수 있음). 컴포넌트 패키지를 워크스페이스 루트에만 선언한 Plug'n'Play 앱은 경로를 안내 · 인정하지 않고 「앱 package.json 에 선언하세요」를 냅니다.
119
+ - `animation`: `@axis-ds/ui-react` 선언이나 `componentsDir` 의 `animate-in` 류 클래스가 있을 때 애니메이션 플러그인(Tailwind 3 `tailwindcss-animate` 선언 + 설치본 + `plugins` 배열 참조, Tailwind 4 는 전역 CSS 선택이 고른 파일(상대 `@import` 로 들여온 하위 파일 포함)의 `@import "tw-animate-css"`)이 없으면 WARN 입니다. 1.6.1 부터 Tailwind 3 은 `plugins` 배열 안의 `require("tailwindcss-animate")` 나 그 패키지를 받은 식별자(`import animate from` · `const animate = require(...)`)만 인정합니다(import 만 있거나 다른 곳의 같은 문자열은 아님). 배열을 정적으로 다 읽지 못하면(펼침 · 단축 속성) 「확인하지 못함」 WARN 입니다. 1.7.6 부터 Tailwind 4 는 CSS 의 줄마다 그 줄의 패키지(`tw-animate-css` · `tailwindcss-animate`)가 선언 · 설치돼 있어야 PASS 이고, 줄은 있는데 선언이나 설치본이 없으면 「애니메이션 플러그인을 쓸 수 없습니다」 WARN 입니다(1.7.5 까지는 줄만 보고 PASS, 실제 빌드는 실패). Plug'n'Play 에서는 플러그인 선언을 앱 `package.json` 에서만 봅니다.
117
120
  - FAIL 이 하나라도 있으면 종료 코드 1 로 끝납니다. WARN 만 있으면 0 입니다(1.3.0 부터, 이전에는 결과와 관계없이 0).
118
121
  - MCP `axis.check_project` 도 같은 항목 · 문구를 냅니다.
119
122
 
@@ -10,7 +10,7 @@ import fs from "fs-extra";
10
10
  import path2 from "path";
11
11
 
12
12
  // ../axis-registry-core/src/project.ts
13
- import { existsSync, readFileSync, realpathSync, statSync } from "fs";
13
+ import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "fs";
14
14
  import path from "path";
15
15
  function resolveBaseDir(baseDirInput) {
16
16
  const resolved = path.resolve(baseDirInput);
@@ -227,6 +227,32 @@ function isDirectory(dir) {
227
227
  function findFirstFile(baseDir, candidates) {
228
228
  return candidates.find((c) => isFile(path.join(baseDir, c)));
229
229
  }
230
+ function selectGlobalCss(baseDir, configured, candidates, options = {}) {
231
+ const result = {};
232
+ if (typeof configured === "string" && configured.trim() !== "") {
233
+ const file = path.resolve(baseDir, configured);
234
+ if (!isFile(file)) result.configuredMissing = configured;
235
+ else if (options.requireStylesheet && path.extname(file).toLowerCase() !== ".css") result.configuredRejected = configured;
236
+ else return { path: file, rel: path.relative(baseDir, file).split(path.sep).join("/") };
237
+ }
238
+ for (const candidate of candidates) {
239
+ const file = path.join(baseDir, candidate);
240
+ if (isFile(file)) {
241
+ result.path = file;
242
+ result.rel = candidate;
243
+ break;
244
+ }
245
+ }
246
+ return result;
247
+ }
248
+ function globalCssSelectionNote(selection) {
249
+ if (selection.configuredMissing !== void 0) return ` (globalCss ${selection.configuredMissing} \uC5C6\uC74C)`;
250
+ if (selection.configuredRejected !== void 0) return ` (globalCss ${selection.configuredRejected}: .css \uD30C\uC77C\uC774 \uC544\uB2D8)`;
251
+ return "";
252
+ }
253
+ function cssImportViaNote(files) {
254
+ return files !== void 0 && files.length > 0 ? ` (${files.join(" \xB7 ")} \uC758 import)` : "";
255
+ }
230
256
  function detectProjectPaths(baseDirInput) {
231
257
  const baseDir = path.resolve(baseDirInput);
232
258
  const hasSrc = isDirectory(path.join(baseDir, "src"));
@@ -286,15 +312,179 @@ function findInstalledPackage(baseDirInput, name) {
286
312
  }
287
313
  }
288
314
  function isPnpProject(baseDirInput) {
315
+ return pnpProjectRoot(baseDirInput) !== void 0;
316
+ }
317
+ function pnpProjectRoot(baseDirInput) {
289
318
  let dir = resolveBaseDir(baseDirInput);
290
319
  for (; ; ) {
291
- if (isFile(path.join(dir, ".pnp.cjs")) || isFile(path.join(dir, ".pnp.js"))) return true;
292
- if (existsSync(path.join(dir, ".git"))) return false;
320
+ if (isFile(path.join(dir, ".pnp.cjs")) || isFile(path.join(dir, ".pnp.js"))) return dir;
321
+ if (existsSync(path.join(dir, ".git"))) return void 0;
293
322
  const parent = path.dirname(dir);
294
- if (parent === dir) return false;
323
+ if (parent === dir) return void 0;
295
324
  dir = parent;
296
325
  }
297
326
  }
327
+ var TAILWIND4_MIN_AXIS_VERSION = "1.4.0";
328
+ var COMPONENT_TAILWIND_CSS_MIN_VERSION = TAILWIND4_MIN_AXIS_VERSION;
329
+ function componentCssResolution(baseDir, pkg, name) {
330
+ if (pkg === void 0) return { kind: "undeclared" };
331
+ const own = hasDependency(pkg, name);
332
+ if (own !== void 0) return { kind: "app", range: own };
333
+ const pnpRoot = pnpRootOnlyDependency(baseDir, pkg, name);
334
+ if (pnpRoot !== void 0) return { kind: "pnp-root-only", range: pnpRoot.range };
335
+ const declared = declaredDependency(baseDir, pkg, name);
336
+ if (declared?.source !== "root") return { kind: "undeclared" };
337
+ if (!rootDeclarationsAllowed(baseDir, pkg)) return { kind: "app-lockfile", range: declared.range };
338
+ const installed = findInstalledPackage(baseDir, name);
339
+ if (installed === void 0) return { kind: "root-not-installed", range: declared.range };
340
+ if (isFile(path.join(installed.dir, "tailwind.css"))) {
341
+ return installed.version !== void 0 ? { kind: "root-ok", range: declared.range, installedVersion: installed.version } : { kind: "root-ok", range: declared.range };
342
+ }
343
+ const installedVersion = installed.version ?? "?";
344
+ const current = installedVersion !== "?" && (compareExactVersions(installedVersion, COMPONENT_TAILWIND_CSS_MIN_VERSION) ?? -1) >= 0;
345
+ return current ? { kind: "root-missing-file", range: declared.range, installedVersion } : { kind: "root-old", range: declared.range, installedVersion };
346
+ }
347
+ function componentCssResolvable(resolution) {
348
+ return resolution.kind === "app" || resolution.kind === "root-ok";
349
+ }
350
+ function tokensCssResolution(baseDir, pkg, name = "@axis-ds/tokens") {
351
+ const own = pkg !== void 0 ? hasDependency(pkg, name) : void 0;
352
+ if (own !== void 0) return { kind: "app", range: own };
353
+ const pnpRoot = pnpRootOnlyDependency(baseDir, pkg, name);
354
+ if (pnpRoot !== void 0) return { kind: "pnp-root-only", range: pnpRoot.range };
355
+ if (!isPnpProject(baseDir) && findInstalledPackage(baseDir, name) !== void 0) return { kind: "installed" };
356
+ const declared = rootDeclarationsAllowed(baseDir, pkg) ? declaredDependency(baseDir, pkg, name) : void 0;
357
+ return declared?.source === "root" ? { kind: "not-installed", source: "root", range: declared.range } : { kind: "not-installed", source: "none" };
358
+ }
359
+ function outsideCssPackageResolution(baseDir, cssDir, name, file) {
360
+ if (isPnpProject(baseDir)) return "unknown";
361
+ const usable = (p) => p !== void 0 && (file === void 0 || isFile(path.join(p.dir, file)));
362
+ if (!usable(findInstalledPackage(baseDir, name))) return "unknown";
363
+ return usable(findInstalledPackage(cssDir, name)) ? "resolved" : "unresolved";
364
+ }
365
+ function deferredComponentImportReason(d, installCommand) {
366
+ const line = `@import "${d.name}/tailwind.css";`;
367
+ const current = d.installedVersion !== void 0 && d.installedVersion !== "?" && (compareExactVersions(d.installedVersion, COMPONENT_TAILWIND_CSS_MIN_VERSION) ?? -1) >= 0;
368
+ return d.installedVersion === void 0 ? `${d.name} \uAC00 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC544 ${line} \uB97C \uB123\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58(${installCommand}) \uB4A4 theme add \uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uC9C1\uC811 \uB123\uC73C\uC138\uC694.` : current ? `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${d.name} \uC758 \uC124\uCE58\uBCF8(${d.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4 ${line} \uB97C \uB123\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uAC00 \uC190\uC0C1\uB410\uC744 \uC218 \uC788\uC73C\uB2C8 \uB2E4\uC2DC \uC124\uCE58(${installCommand}) \uB4A4 theme add \uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uC9C1\uC811 \uB123\uC73C\uC138\uC694.` : `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${d.name} \uC758 \uC124\uCE58\uBCF8(${d.installedVersion === "?" ? "\uBC84\uC804 \uBAA8\uB984" : d.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4(${COMPONENT_TAILWIND_CSS_MIN_VERSION} \uBD80\uD130 \uC81C\uACF5) ${line} \uB97C \uB123\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. ${COMPONENT_TAILWIND_CSS_MIN_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9B0 \uB4A4 theme add \uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uC9C1\uC811 \uB123\uC73C\uC138\uC694.`;
369
+ }
370
+ function findPnpUnpluggedPackages(baseDirInput, name, range) {
371
+ const root = pnpProjectRoot(baseDirInput);
372
+ if (root === void 0) return [];
373
+ const unplugged = path.join(root, ".yarn", "unplugged");
374
+ let entries;
375
+ try {
376
+ entries = readdirSync(unplugged).sort();
377
+ } catch {
378
+ return [];
379
+ }
380
+ const found = [];
381
+ for (const entry of entries) {
382
+ const pkgDir = path.join(unplugged, entry, "node_modules", ...name.split("/"));
383
+ let version;
384
+ try {
385
+ const parsed = JSON.parse(readFileSync(path.join(pkgDir, "package.json"), "utf8"));
386
+ version = isPlainObject(parsed) && parsed.name === name ? exactVersionOf(parsed.version) : void 0;
387
+ } catch {
388
+ version = void 0;
389
+ }
390
+ if (version !== void 0 && versionSatisfiesRange(version, range) === true) found.push(pkgDir);
391
+ }
392
+ return found;
393
+ }
394
+ function parsePartialVersion(token) {
395
+ const m = /^v?(\d+|[xX*])(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(token.trim());
396
+ if (!m) return void 0;
397
+ const num = (s) => s === void 0 || /^[xX*]$/.test(s) ? void 0 : Number(s);
398
+ const major = num(m[1]);
399
+ const minor = major === void 0 ? void 0 : num(m[2]);
400
+ const patch = minor === void 0 ? void 0 : num(m[3]);
401
+ return { major, minor, patch, pre: patch === void 0 ? void 0 : m[4] };
402
+ }
403
+ function coreOf(v) {
404
+ return v.replace(/^v/, "").replace(/[-+].*$/, "");
405
+ }
406
+ function intervalsOfToken(token) {
407
+ const t = token.trim();
408
+ if (t === "" || t === "*" || /^[xX]$/.test(t)) return [{ preCores: [] }];
409
+ const op = /^(\^|~>?|>=|<=|>|<|=)?\s*(.+)$/.exec(t);
410
+ if (!op) return void 0;
411
+ const p = parsePartialVersion(op[2]);
412
+ if (!p) return void 0;
413
+ const full = (maj, min, pat, pre) => `${maj}.${min}.${pat}${pre ? `-${pre}` : ""}`;
414
+ const preCores = p.pre !== void 0 ? [full(p.major ?? 0, p.minor ?? 0, p.patch ?? 0)] : [];
415
+ const lower = full(p.major ?? 0, p.minor ?? 0, p.patch ?? 0, p.pre);
416
+ const kind = op[1] ?? "";
417
+ if (p.major === void 0) return kind === "" || kind === "=" || kind === ">=" ? [{ preCores }] : void 0;
418
+ const next = p.minor === void 0 ? full(p.major + 1, 0, 0) : p.patch === void 0 ? full(p.major, p.minor + 1, 0) : void 0;
419
+ switch (kind) {
420
+ case "":
421
+ case "=":
422
+ return [next === void 0 ? { min: { v: lower, inclusive: true }, max: { v: lower, inclusive: true }, preCores } : { min: { v: lower, inclusive: true }, max: { v: next, inclusive: false, coreOnly: true }, preCores }];
423
+ case ">=":
424
+ return [{ min: { v: lower, inclusive: true }, preCores }];
425
+ case ">":
426
+ return [next === void 0 ? { min: { v: lower, inclusive: false }, preCores } : { min: { v: next, inclusive: true }, preCores }];
427
+ case "<":
428
+ return [{ max: { v: lower, inclusive: false, coreOnly: p.pre === void 0 }, preCores }];
429
+ case "<=":
430
+ return [next === void 0 ? { max: { v: lower, inclusive: true }, preCores } : { max: { v: next, inclusive: false, coreOnly: true }, preCores }];
431
+ case "~":
432
+ case "~>": {
433
+ const upper = p.minor === void 0 ? full(p.major + 1, 0, 0) : full(p.major, p.minor + 1, 0);
434
+ return [{ min: { v: lower, inclusive: true }, max: { v: upper, inclusive: false, coreOnly: true }, preCores }];
435
+ }
436
+ case "^": {
437
+ const upper = p.major > 0 || p.minor === void 0 ? full(p.major + 1, 0, 0) : p.minor > 0 || p.patch === void 0 ? full(0, p.minor + 1, 0) : full(0, 0, p.patch + 1);
438
+ return [{ min: { v: lower, inclusive: true }, max: { v: upper, inclusive: false, coreOnly: true }, preCores }];
439
+ }
440
+ }
441
+ return void 0;
442
+ }
443
+ function satisfiesInterval(version, iv) {
444
+ if (iv.min !== void 0) {
445
+ const c = compareExactVersions(version, iv.min.v);
446
+ if (c === void 0) return void 0;
447
+ if (c < 0 || c === 0 && !iv.min.inclusive) return false;
448
+ }
449
+ if (iv.max !== void 0) {
450
+ const c = iv.max.coreOnly ? compareExactVersions(coreOf(version), coreOf(iv.max.v)) : compareExactVersions(version, iv.max.v);
451
+ if (c === void 0) return void 0;
452
+ if (c > 0 || c === 0 && !iv.max.inclusive) return false;
453
+ }
454
+ return true;
455
+ }
456
+ function versionSatisfiesRange(version, range) {
457
+ if (exactVersionOf(version) === void 0) return void 0;
458
+ const alternatives = range.trim().split("||");
459
+ let unknown = false;
460
+ for (const alt of alternatives) {
461
+ const a = alt.trim();
462
+ let intervals;
463
+ const hyphen = /^(\S+)\s+-\s+(\S+)$/.exec(a);
464
+ if (hyphen) {
465
+ const lo = intervalsOfToken(`>=${hyphen[1]}`);
466
+ const hi = intervalsOfToken(`<=${hyphen[2]}`);
467
+ intervals = lo && hi ? [...lo, ...hi] : void 0;
468
+ } else {
469
+ const tokens = a === "" ? [""] : a.replace(/(>=|<=|>|<|=|\^|~>?)\s+/g, "$1").split(/\s+/);
470
+ const parts = tokens.map(intervalsOfToken);
471
+ intervals = parts.every((x) => x !== void 0) ? parts.flat() : void 0;
472
+ }
473
+ if (intervals === void 0) {
474
+ unknown = true;
475
+ continue;
476
+ }
477
+ const pre = /-/.test(version.replace(/\+.*$/, ""));
478
+ if (pre && !intervals.some((iv) => iv.preCores.includes(coreOf(version)))) continue;
479
+ const results = intervals.map((iv) => satisfiesInterval(version, iv));
480
+ if (results.some((x) => x === void 0)) {
481
+ unknown = true;
482
+ continue;
483
+ }
484
+ if (results.every((x) => x === true)) return true;
485
+ }
486
+ return unknown ? void 0 : false;
487
+ }
298
488
  function compareExactVersions(a, b) {
299
489
  const parse = (v) => {
300
490
  const m = /^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?/.exec(v.trim());
@@ -506,11 +696,22 @@ export {
506
696
  runScriptCommand,
507
697
  workspaceRunCommand,
508
698
  GLOBAL_CSS_CANDIDATES,
699
+ selectGlobalCss,
700
+ globalCssSelectionNote,
701
+ cssImportViaNote,
509
702
  detectProjectPaths,
510
703
  projectFileExists,
511
704
  ROOT_LAYOUT_FILES,
512
705
  findInstalledPackage,
513
706
  isPnpProject,
707
+ pnpProjectRoot,
708
+ TAILWIND4_MIN_AXIS_VERSION,
709
+ componentCssResolution,
710
+ componentCssResolvable,
711
+ tokensCssResolution,
712
+ outsideCssPackageResolution,
713
+ deferredComponentImportReason,
714
+ findPnpUnpluggedPackages,
514
715
  compareExactVersions,
515
716
  MONET_CATEGORIES,
516
717
  MonetClient,
package/dist/index.js CHANGED
@@ -4,25 +4,36 @@ import {
4
4
  MonetClient,
5
5
  PNP_ROOT_ONLY_NOTE,
6
6
  ROOT_LAYOUT_FILES,
7
+ TAILWIND4_MIN_AXIS_VERSION,
7
8
  addCommandFor,
8
9
  addCommandPrefix,
9
10
  compareExactVersions,
11
+ componentCssResolution,
12
+ componentCssResolvable,
13
+ cssImportViaNote,
10
14
  declaredDependency,
15
+ deferredComponentImportReason,
11
16
  dependencyInstallGuide,
12
17
  detectInstallCommand,
13
18
  detectProjectPaths,
14
19
  findInstalledPackage,
20
+ findPnpUnpluggedPackages,
21
+ globalCssSelectionNote,
15
22
  hasDependency,
16
23
  isPlainObject,
17
24
  isPnpProject,
25
+ outsideCssPackageResolution,
26
+ pnpProjectRoot,
18
27
  pnpRootOnlyDependency,
19
28
  projectFileExists,
20
29
  resolveBaseDir,
21
30
  rootDeclarationsAllowed,
22
31
  runScriptCommand,
32
+ selectGlobalCss,
33
+ tokensCssResolution,
23
34
  workspaceRootPackage,
24
35
  workspaceRunCommand
25
- } from "./chunk-I3HWV7Y5.js";
36
+ } from "./chunk-EBFXCLSV.js";
26
37
  import {
27
38
  V0Client
28
39
  } from "./chunk-2PNCUPCG.js";
@@ -1928,7 +1939,7 @@ async function readAxisConfig(baseDir) {
1928
1939
  }
1929
1940
 
1930
1941
  // ../axis-registry-core/src/template/tailwind-core.ts
1931
- import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync } from "fs";
1942
+ import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "fs";
1932
1943
  import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "path";
1933
1944
  function declaredRange(baseDir, pkg, name) {
1934
1945
  return declaredDependency(baseDir, pkg, name)?.range;
@@ -2059,6 +2070,157 @@ var TOKENS_CSS_IMPORT = /@import\s+(?:url\(\s*)?["'](@axis-ds\/tokens\/css(?:\/s
2059
2070
  function findTokensCssImport(css) {
2060
2071
  return TOKENS_CSS_IMPORT.exec(stripCssComments(css))?.[1];
2061
2072
  }
2073
+ var CSS_IMPORT_DEPTH_LIMIT = 32;
2074
+ var CSS_IMPORT_CYCLE_DEPTH_LIMIT = 100;
2075
+ var RELATIVE_CSS_IMPORT = /@import\s*(?:"(\.{1,2}\/[^"\n]*)"|'(\.{1,2}\/[^'\n]*)')(?=([^;{}]*))/g;
2076
+ function isConditionalCssImport(tail) {
2077
+ return tail !== void 0 && tail.trim() !== "";
2078
+ }
2079
+ function resolveRelativeCssImport(fromDir, spec) {
2080
+ if (spec.endsWith("/")) return void 0;
2081
+ const target = resolve(fromDir, spec);
2082
+ if (regularFile(target)) return target;
2083
+ if (regularFile(`${target}.css`)) return `${target}.css`;
2084
+ return void 0;
2085
+ }
2086
+ var CSS_IMPORT_SPEC = /@import\s+(?:url\(\s*)?(["'])([^"'\n]+)\1/g;
2087
+ function isPackageCssSpec(spec) {
2088
+ return !spec.startsWith("./") && !spec.startsWith("../") && !spec.startsWith("/") && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(spec);
2089
+ }
2090
+ function cssImportCycleIssue(files) {
2091
+ if (files.length === 1) return `${files[0]} \uC774 \uC790\uAE30 \uC790\uC2E0\uC744 @import \uD574 \uBE4C\uB4DC\uAC00 \uC21C\uD658\uC73C\uB85C \uC2E4\uD328\uD569\uB2C8\uB2E4 \u2192 \uC21C\uD658\uD558\uB294 @import \uB97C \uC5C6\uC560\uC138\uC694`;
2092
+ return `${files.join(" \xB7 ")} \uC774 \uC11C\uB85C @import \uD574 \uBE4C\uB4DC\uAC00 \uC21C\uD658\uC73C\uB85C \uC2E4\uD328\uD569\uB2C8\uB2E4 \u2192 \uC21C\uD658\uD558\uB294 @import \uB97C \uC5C6\uC560\uC138\uC694`;
2093
+ }
2094
+ function findImportCycle(start, edges) {
2095
+ const state = /* @__PURE__ */ new Map();
2096
+ const trail = [start];
2097
+ const stack = [{ node: start, next: 0 }];
2098
+ state.set(start, "open");
2099
+ while (stack.length > 0) {
2100
+ const top = stack[stack.length - 1];
2101
+ const out = edges.get(top.node) ?? [];
2102
+ if (top.next >= out.length) {
2103
+ state.set(top.node, "done");
2104
+ stack.pop();
2105
+ trail.pop();
2106
+ continue;
2107
+ }
2108
+ const to = out[top.next++];
2109
+ const s = state.get(to);
2110
+ if (s === "open") return trail.slice(trail.indexOf(to));
2111
+ if (s === "done") continue;
2112
+ state.set(to, "open");
2113
+ trail.push(to);
2114
+ stack.push({ node: to, next: 0 });
2115
+ }
2116
+ return void 0;
2117
+ }
2118
+ function regularFile(file) {
2119
+ try {
2120
+ return statSync(file).isFile();
2121
+ } catch {
2122
+ return false;
2123
+ }
2124
+ }
2125
+ function expandGlobalCss(baseDirInput, entryPath, entryRel, entryCssDir) {
2126
+ const baseDir = resolveBaseDir(baseDirInput);
2127
+ const entry = readFileSync(entryPath, "utf8");
2128
+ let entryReal = entryPath;
2129
+ try {
2130
+ entryReal = realpathSync(entryPath);
2131
+ } catch {
2132
+ }
2133
+ const raws = /* @__PURE__ */ new Map([[entryReal, entry]]);
2134
+ const files = [];
2135
+ const packageLines = [];
2136
+ const evidenceWalk = walkRelativeCssImports(baseDir, entryReal, raws, CSS_IMPORT_DEPTH_LIMIT, false);
2137
+ const relOfReal = /* @__PURE__ */ new Map([[entryReal, entryRel]]);
2138
+ for (const node of evidenceWalk.order) {
2139
+ const isEntry = node.real === entryReal;
2140
+ const rel3 = isEntry ? entryRel : node.rel;
2141
+ const css = stripCssComments(raws.get(node.real) ?? "");
2142
+ const file = { rel: rel3, path: node.real, css, depth: node.depth };
2143
+ const cssDir = isEntry ? entryCssDir : dirname(node.real);
2144
+ if (cssDir !== void 0) file.cssDir = cssDir;
2145
+ files.push(file);
2146
+ relOfReal.set(node.real, rel3);
2147
+ for (const m of css.matchAll(CSS_IMPORT_SPEC)) {
2148
+ if (!isPackageCssSpec(m[2])) continue;
2149
+ const line = { spec: m[2], file: rel3 };
2150
+ if (cssDir !== void 0) line.cssDir = cssDir;
2151
+ packageLines.push(line);
2152
+ }
2153
+ }
2154
+ const cycleWalk = walkRelativeCssImports(baseDir, entryReal, raws, CSS_IMPORT_CYCLE_DEPTH_LIMIT, true);
2155
+ const cycle = findImportCycle(entryReal, cycleWalk.edges);
2156
+ if (cycle !== void 0) {
2157
+ for (const node of cycleWalk.order) if (!relOfReal.has(node.real)) relOfReal.set(node.real, node.rel);
2158
+ const head = files[0];
2159
+ return {
2160
+ entry,
2161
+ evidence: entry,
2162
+ files: [head],
2163
+ packageLines: packageLines.filter((l) => l.file === head.rel),
2164
+ cycle: cycle.map((p) => relOfReal.get(p) ?? p)
2165
+ };
2166
+ }
2167
+ const evidence = files.length === 1 ? entry : files.map((f) => f.css).join("\n");
2168
+ return { entry, evidence, files, packageLines };
2169
+ }
2170
+ function walkRelativeCssImports(baseDir, entryReal, raws, limit, includeConditional) {
2171
+ const order = [];
2172
+ const edges = /* @__PURE__ */ new Map();
2173
+ const visited = /* @__PURE__ */ new Set([entryReal]);
2174
+ const queue = [{ real: entryReal, rel: "", depth: 0 }];
2175
+ while (queue.length > 0) {
2176
+ const cur = queue.shift();
2177
+ order.push(cur);
2178
+ const css = stripCssComments(raws.get(cur.real) ?? "");
2179
+ const out = [];
2180
+ edges.set(cur.real, out);
2181
+ for (const m of css.matchAll(RELATIVE_CSS_IMPORT)) {
2182
+ if (!includeConditional && isConditionalCssImport(m[3])) continue;
2183
+ const target = resolveRelativeCssImport(dirname(cur.real), m[1] ?? m[2]);
2184
+ if (target === void 0) continue;
2185
+ let real;
2186
+ try {
2187
+ real = realpathSync(target);
2188
+ } catch {
2189
+ continue;
2190
+ }
2191
+ if (visited.has(real)) {
2192
+ out.push(real);
2193
+ continue;
2194
+ }
2195
+ if (cur.depth >= limit) continue;
2196
+ const rel3 = relative(baseDir, real);
2197
+ if (rel3 === "" || rel3 === ".." || rel3.startsWith(`..${sep}`) || isAbsolute(rel3)) continue;
2198
+ if (!raws.has(real)) {
2199
+ try {
2200
+ raws.set(real, readFileSync(real, "utf8"));
2201
+ } catch {
2202
+ continue;
2203
+ }
2204
+ }
2205
+ visited.add(real);
2206
+ out.push(real);
2207
+ queue.push({ real, rel: rel3.split(sep).join("/"), depth: cur.depth + 1 });
2208
+ }
2209
+ }
2210
+ return { order, edges };
2211
+ }
2212
+ function evidenceOrigin(expanded, test) {
2213
+ return expanded.files.find((f) => test(f.css));
2214
+ }
2215
+ function packageLineCssDir(expanded, spec) {
2216
+ const line = expanded.packageLines.find((l) => l.spec === spec);
2217
+ return line !== void 0 ? line.cssDir : expanded.files[0]?.cssDir;
2218
+ }
2219
+ function viaFiles(origins) {
2220
+ const out = [];
2221
+ for (const o of origins) if (o !== void 0 && o.depth > 0 && !out.includes(o.rel)) out.push(o.rel);
2222
+ return out;
2223
+ }
2062
2224
  var TOKENS_CSS_JS_IMPORT = /(?:\bimport\s+|\bfrom\s+|\brequire\s*\(\s*)(["'])(@axis-ds\/tokens\/css(?:\/shadcn|\/tailwind4)?)\1/;
2063
2225
  var AXIS_VARIABLE_DEFINITION = /(?<![\w-])--axis-[\w-]+\s*:/;
2064
2226
  function findLayoutTokensCssImport(baseDirInput) {
@@ -2077,45 +2239,92 @@ function findLayoutTokensCssImport(baseDirInput) {
2077
2239
  }
2078
2240
  return void 0;
2079
2241
  }
2242
+ var TOKENS_PACKAGE_NAME = "@axis-ds/tokens";
2243
+ function outsideCssDir(selection) {
2244
+ if (selection.path === void 0 || selection.rel === void 0) return void 0;
2245
+ const outside = selection.rel === ".." || selection.rel.startsWith("../") || isAbsolute(selection.rel);
2246
+ return outside ? dirname(selection.path) : void 0;
2247
+ }
2248
+ function outsideCssUnresolvedIssue(spec, name) {
2249
+ return `${spec} import \uAC00 \uC788\uC9C0\uB9CC \uC774 \uD30C\uC77C \uC704\uCE58\uC5D0\uC11C ${name} \uB97C \uCC3E\uC9C0 \uBABB\uD574 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC124\uCE58\uBCF8\uC774 \uC571 \uCABD node_modules \uC5D0\uB9CC \uC788\uC74C) \u2192 \uC774 \uD30C\uC77C\uC758 \uC0C1\uC704 node_modules(\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8 \uB4F1)\uC5D0 \uC124\uCE58\uD558\uAC70\uB098 globalCss \uB97C \uC571 \uC548 \uD30C\uC77C\uB85C \uBC14\uAFB8\uC138\uC694`;
2250
+ }
2251
+ function tokensInstallPhrase(baseDir) {
2252
+ const g = dependencyInstallGuide(baseDir, TOKENS_PACKAGE_NAME, `^${TAILWIND4_MIN_AXIS_VERSION}`);
2253
+ if (g.kind === "install-at-root") return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0\uC11C ${g.installCommand}`;
2254
+ return g.kind === "install" ? g.installCommand : g.addCommand;
2255
+ }
2256
+ function tokensCssUnresolvedIssue(baseDir, pkg, spec, cssDir) {
2257
+ const r = tokensCssResolution(baseDir, pkg, TOKENS_PACKAGE_NAME);
2258
+ const head = `${spec} import \uAC00 \uC788\uC9C0\uB9CC`;
2259
+ if (r.kind === "pnp-root-only") return `${head} ${TOKENS_PACKAGE_NAME} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`;
2260
+ if (r.kind === "not-installed") {
2261
+ return `${head} ${TOKENS_PACKAGE_NAME} \uAC00 \uC124\uCE58\uB418\uC9C0 \uC54A\uC544 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 \uC124\uCE58(${tokensInstallPhrase(baseDir)})`;
2262
+ }
2263
+ if (cssDir !== void 0 && outsideCssPackageResolution(baseDir, cssDir, TOKENS_PACKAGE_NAME) === "unresolved") {
2264
+ return outsideCssUnresolvedIssue(spec, TOKENS_PACKAGE_NAME);
2265
+ }
2266
+ return void 0;
2267
+ }
2080
2268
  function checkGlobalsCss(baseDirInput) {
2081
2269
  const baseDir = resolveBaseDir(baseDirInput);
2082
- for (const candidate of TAILWIND_CSS_CANDIDATES) {
2083
- const file = join(baseDir, candidate);
2084
- if (!existsSync(file)) continue;
2085
- const content = readFileSync(file, "utf8");
2086
- const css = stripCssComments(content);
2087
- const imported = findTokensCssImport(content);
2088
- let pass;
2089
- if (imported !== void 0) pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${imported} import)`;
2090
- else if (AXIS_VARIABLE_DEFINITION.test(css)) pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218(--axis-*)\uAC00 \uC815\uC758\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4`;
2091
- else {
2092
- const layout = findLayoutTokensCssImport(baseDir);
2093
- if (layout) pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${layout.file}\uC758 ${layout.spec} import)`;
2094
- }
2095
- if (pass !== void 0) {
2096
- const tailwind = detectTailwind(baseDir, readPackageJson(baseDir));
2097
- const tailwind4 = tailwind.major !== void 0 && tailwind.major >= 4;
2098
- if (!tailwind4 && findCreateNextAppDefaults(content).some((d) => d.includes("font-family: Arial"))) {
2099
- return {
2100
- name: "globals.css",
2101
- severity: "warn",
2102
- message: `${pass}. \uB2E4\uB9CC create-next-app \uAE30\uBCF8 body font-family: Arial \uC774 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4 \u2192 \uC9C0\uC6B0\uC138\uC694(AXIS \uAE00\uAF34 \uB300\uC2E0 Arial \uC774 \uC801\uC6A9\uB428)`
2103
- };
2104
- }
2105
- return { name: "globals.css", severity: "pass", message: pass };
2106
- }
2107
- if (css.includes("--background") && css.includes("--primary")) {
2270
+ const selection = selectCheckGlobalCss(baseDir);
2271
+ const note = globalCssSelectionNote(selection);
2272
+ if (selection.path !== void 0 && selection.rel !== void 0) {
2273
+ const candidate = selection.rel;
2274
+ const expanded = expandGlobalCss(baseDir, selection.path, candidate, outsideCssDir(selection));
2275
+ const judged = judgeGlobalsCss(baseDir, selection, candidate, expanded);
2276
+ if (expanded.cycle !== void 0) {
2277
+ return { name: "globals.css", severity: "warn", message: `${judged.message} / ${cssImportCycleIssue(expanded.cycle)}${note}` };
2278
+ }
2279
+ return { name: "globals.css", severity: judged.severity, message: `${judged.message}${note}` };
2280
+ }
2281
+ return { name: "globals.css", severity: "fail", message: `globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4${note}` };
2282
+ }
2283
+ function judgeGlobalsCss(baseDir, selection, candidate, expanded) {
2284
+ const content = expanded.entry;
2285
+ const css = stripCssComments(content);
2286
+ const imported = findTokensCssImport(expanded.evidence);
2287
+ const pkg = readPackageJson(baseDir);
2288
+ let pass;
2289
+ let unresolved;
2290
+ let via = "";
2291
+ if (imported !== void 0) {
2292
+ const origin = evidenceOrigin(expanded, (f) => findTokensCssImport(f) === imported);
2293
+ via = cssImportViaNote(viaFiles([origin]));
2294
+ const issue = tokensCssUnresolvedIssue(baseDir, pkg, imported, origin !== void 0 ? origin.cssDir : outsideCssDir(selection));
2295
+ if (issue !== void 0) unresolved = `${candidate}\uC5D0 ${issue}`;
2296
+ else pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${imported} import)`;
2297
+ } else if (AXIS_VARIABLE_DEFINITION.test(stripCssComments(expanded.evidence))) {
2298
+ via = cssImportViaNote(viaFiles([evidenceOrigin(expanded, (f) => AXIS_VARIABLE_DEFINITION.test(f))]));
2299
+ pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218(--axis-*)\uAC00 \uC815\uC758\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4`;
2300
+ } else {
2301
+ const layout = findLayoutTokensCssImport(baseDir);
2302
+ if (layout) {
2303
+ const issue = tokensCssUnresolvedIssue(baseDir, pkg, layout.spec);
2304
+ if (issue !== void 0) unresolved = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uACE0 ${layout.file}\uC758 ${issue}`;
2305
+ else pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${layout.file}\uC758 ${layout.spec} import)`;
2306
+ }
2307
+ }
2308
+ const head = pass ?? unresolved;
2309
+ if (head !== void 0) {
2310
+ const tailwind = detectTailwind(baseDir, pkg);
2311
+ const tailwind4 = tailwind.major !== void 0 && tailwind.major >= 4;
2312
+ if (!tailwind4 && findCreateNextAppDefaults(content).some((d) => d.includes("font-family: Arial"))) {
2108
2313
  return {
2109
- name: "globals.css",
2110
2314
  severity: "warn",
2111
- message: `${candidate}\uC5D0 \uC790\uCCB4 CSS \uBCC0\uC218(--background \xB7 --primary)\uB9CC \uC788\uACE0 AXIS \uD1A0\uD070\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 @axis-ds/tokens CSS \uB97C import \uD558\uC138\uC694`
2315
+ message: `${head}. \uB2E4\uB9CC create-next-app \uAE30\uBCF8 body font-family: Arial \uC774 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4 \u2192 \uC9C0\uC6B0\uC138\uC694(AXIS \uAE00\uAF34 \uB300\uC2E0 Arial \uC774 \uC801\uC6A9\uB428)${via}`
2112
2316
  };
2113
2317
  }
2114
- return { name: "globals.css", severity: "warn", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4` };
2318
+ return { severity: pass !== void 0 ? "pass" : "warn", message: `${head}${via}` };
2319
+ }
2320
+ if (css.includes("--background") && css.includes("--primary")) {
2321
+ return {
2322
+ severity: "warn",
2323
+ message: `${candidate}\uC5D0 \uC790\uCCB4 CSS \uBCC0\uC218(--background \xB7 --primary)\uB9CC \uC788\uACE0 AXIS \uD1A0\uD070\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 @axis-ds/tokens CSS \uB97C import \uD558\uC138\uC694`
2324
+ };
2115
2325
  }
2116
- return { name: "globals.css", severity: "fail", message: "globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" };
2326
+ return { severity: "warn", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4` };
2117
2327
  }
2118
- var TAILWIND4_MIN_AXIS_VERSION = "1.4.0";
2119
2328
  var TAILWIND4_AXIS_PACKAGES = ["@axis-ds/tokens", "@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2120
2329
  function lowerOf(token) {
2121
2330
  const v = versionParts(token);
@@ -2348,66 +2557,134 @@ function readPackageJson(baseDir) {
2348
2557
  return void 0;
2349
2558
  }
2350
2559
  }
2351
- function tailwind4Issues(css, pkg, versions, baseDir) {
2560
+ function tailwind4Issues(input, pkg, versions, baseDir) {
2561
+ const { entry, evidence } = input;
2562
+ const cssDirOf = input.cssDirOf ?? (() => void 0);
2352
2563
  const issues = [];
2353
- const hasEntry = hasTokensTailwind4Import(css);
2564
+ const tailwindDir = cssDirOf("tailwindcss");
2565
+ if (tailwindDir !== void 0 && outsideCssPackageResolution(baseDir, tailwindDir, "tailwindcss") === "unresolved") {
2566
+ issues.push(outsideCssUnresolvedIssue("tailwindcss", "tailwindcss"));
2567
+ }
2568
+ const hasEntry = hasTokensTailwind4Import(evidence);
2354
2569
  if (!hasEntry) issues.push(`AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${TOKENS_TAILWIND4_IMPORT_LINE}`);
2355
- const missing = TAILWIND4_COMPONENT_PACKAGES.filter(
2356
- (name) => declaredRange(baseDir, pkg, name) !== void 0 && !hasComponentTailwindImport(css, name) && !hasComponentSource(css, name)
2357
- );
2570
+ const resolutions = new Map(TAILWIND4_COMPONENT_PACKAGES.map((name) => [name, componentCssResolution(baseDir, pkg, name)]));
2571
+ const missing = [];
2572
+ const unresolvedMissing = [];
2573
+ for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2574
+ const r = resolutions.get(name);
2575
+ if (r.kind === "undeclared" || r.kind === "app-lockfile") continue;
2576
+ if (hasComponentTailwindImport(evidence, name) || hasComponentSource(evidence, name)) continue;
2577
+ if (componentCssResolvable(r)) missing.push(name);
2578
+ else unresolvedMissing.push(componentCssMissingIssue(baseDir, name, r));
2579
+ }
2358
2580
  if (missing.length > 0) {
2359
2581
  issues.push(`\uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCE94 \uACBD\uB85C(tailwind.css)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${missing.map(componentTailwindImportLine).join(" ")}`);
2360
2582
  }
2583
+ issues.push(...unresolvedMissing);
2361
2584
  if (hasEntry) {
2362
- const defaults = findCreateNextAppDefaults(css);
2585
+ const defaults = findCreateNextAppDefaults(entry);
2363
2586
  if (defaults.length > 0) issues.push(`create-next-app \uAE30\uBCF8 \uC815\uC758\uAC00 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4(${defaults.join(" \xB7 ")}) \u2192 \uC9C0\uC6B0\uC138\uC694`);
2364
2587
  if (versions.some((v) => v.name === TAILWIND4_AXIS_PACKAGES[0])) {
2365
2588
  issues.push(`AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC9C0\uB9CC ${TAILWIND4_AXIS_PACKAGES[0]} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2366
2589
  }
2367
2590
  }
2368
2591
  for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2369
- if (hasComponentTailwindImport(css, name) && versions.some((v) => v.name === name)) {
2370
- issues.push(`${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC ${name} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2371
- }
2592
+ if (!hasComponentTailwindImport(evidence, name)) continue;
2593
+ const unresolved = componentCssPresentIssue(baseDir, name, resolutions.get(name));
2594
+ const componentDir = cssDirOf(`${name}/tailwind.css`);
2595
+ if (unresolved !== void 0) issues.push(unresolved);
2596
+ else if (componentDir !== void 0 && outsideCssPackageResolution(baseDir, componentDir, name, "tailwind.css") === "unresolved") {
2597
+ issues.push(outsideCssUnresolvedIssue(`${name}/tailwind.css`, name));
2598
+ } else if (versions.some((v) => v.name === name)) issues.push(`${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC ${name} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2372
2599
  }
2373
2600
  return issues;
2374
2601
  }
2602
+ function componentInstallPhrase(baseDir, name, range) {
2603
+ const g = dependencyInstallGuide(baseDir, name, range);
2604
+ return g.kind === "install-at-root" ? `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0\uC11C ${g.installCommand}` : g.installCommand;
2605
+ }
2606
+ function componentCssMissingIssue(baseDir, name, r) {
2607
+ const line = componentTailwindImportLine(name);
2608
+ switch (r.kind) {
2609
+ case "pnp-root-only":
2610
+ return `${name} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD55C \uB4A4 ${line}`;
2611
+ case "root-not-installed":
2612
+ return `${name} \uAC00 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 \u2192 \uC124\uCE58(${componentInstallPhrase(baseDir, name, r.range)}) \uB4A4 ${line}`;
2613
+ case "root-old":
2614
+ return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${name} \uC758 \uC124\uCE58\uBCF8(${r.installedVersion === "?" ? "\uBC84\uC804 \uBAA8\uB984" : r.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(${TAILWIND4_MIN_AXIS_VERSION} \uBD80\uD130 \uC81C\uACF5) \u2192 ${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9B0 \uB4A4 ${line}`;
2615
+ case "root-missing-file":
2616
+ return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${name} \uC758 \uC124\uCE58\uBCF8(${r.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(\uC124\uCE58\uAC00 \uC190\uC0C1\uB410\uC744 \uC218 \uC788\uC74C) \u2192 \uB2E4\uC2DC \uC124\uCE58(${componentInstallPhrase(baseDir, name, r.range)}) \uB4A4 ${line}`;
2617
+ default:
2618
+ return line;
2619
+ }
2620
+ }
2621
+ function componentCssPresentIssue(baseDir, name, r) {
2622
+ const head = `${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC`;
2623
+ switch (r.kind) {
2624
+ case "pnp-root-only":
2625
+ return `${head} ${name} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`;
2626
+ case "root-not-installed":
2627
+ return `${head} ${name} \uAC00 \uC124\uCE58\uB418\uC9C0 \uC54A\uC544 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 \uC124\uCE58(${componentInstallPhrase(baseDir, name, r.range)})`;
2628
+ case "root-missing-file":
2629
+ return `${head} ${name} \uC124\uCE58\uBCF8(${r.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 \uB2E4\uC2DC \uC124\uCE58(${componentInstallPhrase(baseDir, name, r.range)})`;
2630
+ case "root-old":
2631
+ return r.installedVersion === "?" ? `${head} ${name} \uC124\uCE58\uBCF8(\uBC84\uC804 \uBAA8\uB984)\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 ${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9AC\uC138\uC694` : void 0;
2632
+ default:
2633
+ return void 0;
2634
+ }
2635
+ }
2375
2636
  function checkTailwindSetup(baseDirInput) {
2376
2637
  const baseDir = resolveBaseDir(baseDirInput);
2377
2638
  const pkg = readPackageJson(baseDir);
2378
2639
  const tailwind = detectTailwind(baseDir, pkg);
2379
2640
  if (tailwind.major !== void 0 && tailwind.major >= 4) {
2380
2641
  const label = `Tailwind ${tailwind.major}`;
2381
- for (const candidate of TAILWIND_CSS_CANDIDATES) {
2382
- const file = join(baseDir, candidate);
2383
- if (!existsSync(file)) continue;
2384
- const css = readFileSync(file, "utf8");
2385
- if (!hasTailwindImport(css)) {
2642
+ const selection = selectCheckGlobalCss(baseDir);
2643
+ const note = globalCssSelectionNote(selection);
2644
+ if (selection.path !== void 0 && selection.rel !== void 0) {
2645
+ const candidate = selection.rel;
2646
+ const expanded = expandGlobalCss(baseDir, selection.path, candidate, outsideCssDir(selection));
2647
+ if (!hasTailwindImport(expanded.evidence)) {
2386
2648
  return {
2387
2649
  name: "tailwind.config",
2388
2650
  severity: "warn",
2389
- message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4`
2651
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4${note}`
2390
2652
  };
2391
2653
  }
2654
+ const via = cssImportViaNote(
2655
+ viaFiles([
2656
+ evidenceOrigin(expanded, hasTailwindImport),
2657
+ evidenceOrigin(expanded, hasTokensTailwind4Import),
2658
+ ...TAILWIND4_COMPONENT_PACKAGES.flatMap((name) => [
2659
+ evidenceOrigin(expanded, (f) => hasComponentTailwindImport(f, name)),
2660
+ evidenceOrigin(expanded, (f) => hasComponentSource(f, name))
2661
+ ])
2662
+ ])
2663
+ );
2392
2664
  const versions = axisPackagesBelowTailwind4(pkg, baseDir);
2393
- const issues = tailwind4Issues(css, pkg, versions, baseDir);
2665
+ const issues = tailwind4Issues(
2666
+ { entry: expanded.entry, evidence: expanded.evidence, cssDirOf: (spec) => packageLineCssDir(expanded, spec) },
2667
+ pkg,
2668
+ versions,
2669
+ baseDir
2670
+ );
2394
2671
  if (issues.length > 0) {
2395
2672
  return {
2396
2673
  name: "tailwind.config",
2397
2674
  severity: "warn",
2398
- message: `${label}: ${candidate}\uC5D0 ${issues.join(" / ")}${tailwind4VersionNote(versions)}`
2675
+ message: `${label}: ${candidate}\uC5D0 ${issues.join(" / ")}${tailwind4VersionNote(versions)}${via}${note}`
2399
2676
  };
2400
2677
  }
2401
2678
  return {
2402
2679
  name: "tailwind.config",
2403
2680
  severity: "pass",
2404
- message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uC640 AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC2B5\uB2C8\uB2E4`
2681
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uC640 AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC2B5\uB2C8\uB2E4${via}${note}`
2405
2682
  };
2406
2683
  }
2407
2684
  return {
2408
2685
  name: "tailwind.config",
2409
2686
  severity: "warn",
2410
- message: `${label}: @import "tailwindcss" \uB97C \uD655\uC778\uD560 CSS \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4`
2687
+ message: `${label}: @import "tailwindcss" \uB97C \uD655\uC778\uD560 CSS \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4${note}`
2411
2688
  };
2412
2689
  }
2413
2690
  for (const file of TAILWIND_CONFIG_FILES) {
@@ -2453,31 +2730,45 @@ function readAxisConfigLike(baseDir) {
2453
2730
  return void 0;
2454
2731
  }
2455
2732
  }
2456
- function contentGlobToRegExp(glob) {
2733
+ function selectCheckGlobalCss(baseDir) {
2734
+ return selectGlobalCss(baseDir, readAxisConfigLike(baseDir)?.globalCss, TAILWIND_CSS_CANDIDATES, { requireStylesheet: true });
2735
+ }
2736
+ function contentGlobToRegExp(glob, opts = {}) {
2737
+ const noDot = opts.noDot === true;
2457
2738
  const g = glob.trim().replace(/^\.\//, "");
2739
+ const lead = (i) => noDot && (i === 0 || g[i - 1] === "/") ? "(?!\\.)" : "";
2458
2740
  let re = "";
2459
2741
  for (let i = 0; i < g.length; i++) {
2460
2742
  const c = g[i];
2461
2743
  if (c === "*") {
2462
2744
  if (g[i + 1] === "*") {
2463
2745
  const slash = g[i + 2] === "/";
2464
- re += slash ? "(?:.*/)?" : ".*";
2746
+ if (noDot) re += slash ? `(?:${lead(i)}[^/]*/(?:(?!\\.)[^/]*/)*)?` : `${lead(i)}[^/]*(?:/(?!\\.)[^/]*)*`;
2747
+ else re += slash ? "(?:.*/)?" : ".*";
2465
2748
  i += slash ? 2 : 1;
2466
- } else re += "[^/]*";
2467
- } else if (c === "?") re += "[^/]";
2749
+ } else re += `${lead(i)}[^/]*`;
2750
+ } else if (c === "?") re += `${lead(i)}[^/]`;
2468
2751
  else if (c === "{") {
2469
2752
  const end = g.indexOf("}", i);
2470
2753
  if (end === -1) {
2471
2754
  re += "\\{";
2472
2755
  continue;
2473
2756
  }
2474
- re += `(?:${g.slice(i + 1, end).split(",").map((part) => part.replace(/[.+^$()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*")).join("|")})`;
2757
+ const atStart = lead(i) !== "";
2758
+ re += `(?:${g.slice(i + 1, end).split(",").map((part) => {
2759
+ const body = part.replace(/[.+^$()|[\]\\]/g, "\\$&");
2760
+ if (!noDot) return body.replace(/\*/g, "[^/]*");
2761
+ return body.replace(
2762
+ /(\/)?\*/g,
2763
+ (_m, slash, off) => slash !== void 0 || off === 0 && atStart ? `${slash ?? ""}(?!\\.)[^/]*` : "[^/]*"
2764
+ );
2765
+ }).join("|")})`;
2475
2766
  i = end;
2476
2767
  } else if (c === "[") {
2477
2768
  const end = g.indexOf("]", i);
2478
2769
  if (end === -1) re += "\\[";
2479
2770
  else {
2480
- re += g.slice(i, end + 1);
2771
+ re += lead(i) + g.slice(i, end + 1);
2481
2772
  i = end;
2482
2773
  }
2483
2774
  } else re += c.replace(/[.+^$()|\\]/g, "\\$&");
@@ -2546,22 +2837,46 @@ function realpathPosix(p) {
2546
2837
  }
2547
2838
  return void 0;
2548
2839
  }
2549
- function contentGlobBranchCovers(glob, sample, baseDir) {
2840
+ function contentGlobBranchCovers(glob, sample, baseDir, noDot) {
2550
2841
  if (isAbsoluteGlob(glob)) {
2551
- const re = contentGlobToRegExp(glob.trim().replace(/\\/g, "/"));
2842
+ const re = contentGlobToRegExp(glob.trim().replace(/\\/g, "/"), { noDot });
2552
2843
  const absSample = resolve(baseDir, sample);
2553
2844
  if (re.test(absSample.split(sep).join("/"))) return true;
2554
2845
  const real = realpathPosix(absSample);
2555
2846
  return real !== void 0 && re.test(real);
2556
2847
  }
2557
- return leadingParentSegments(glob) === leadingParentSegments(sample) && contentGlobToRegExp(glob).test(sample);
2848
+ return leadingParentSegments(glob) === leadingParentSegments(sample) && contentGlobToRegExp(glob, { noDot }).test(sample);
2558
2849
  }
2559
- function contentGlobCovers(glob, sample, baseDirInput) {
2850
+ function contentGlobCovers(glob, sample, baseDirInput, opts = {}) {
2560
2851
  const baseDir = resolveBaseDir(baseDirInput);
2561
2852
  const branches = expandBraces(glob.trim()) ?? [glob];
2562
- return branches.some((b) => contentGlobBranchCovers(b, sample, baseDir));
2853
+ return branches.some((b) => contentGlobBranchCovers(b, sample, baseDir, opts.noDot === true));
2854
+ }
2855
+ var CONTENT_PARENT_PATH_NOTE = "\uC0C1\uC704 \uACBD\uB85C\uB294 Tailwind 3 \uAC00 \uBE4C\uB4DC\uB97C \uC2E4\uD589\uD55C \uB514\uB809\uD130\uB9AC \uAE30\uC900\uC73C\uB85C \uD480\uB9BD\uB2C8\uB2E4. \uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8 \uB4F1 \uC571 \uB514\uB809\uD130\uB9AC \uBC16\uC5D0\uC11C \uBE4C\uB4DC\uD558\uBA74 content: { relative: true, files: [...] } \uB85C \uC124\uC815 \uD30C\uC77C \uAE30\uC900\uC73C\uB85C \uBC14\uAFB8\uC138\uC694";
2856
+ function contentPnpNote(names, unplugged = []) {
2857
+ const list = [...new Set(typeof names === "string" ? [names] : names)];
2858
+ const dyn = list.map((n) => `path.join(path.dirname(require.resolve("${n}")), "..", "dist/**/*.{js,mjs}")`).join(", ");
2859
+ const needsSetup = list.filter((n) => !unplugged.includes(n));
2860
+ const meta = needsSetup.map((n) => `"${n}": { "unplugged": true }`).join(", ");
2861
+ const setup = needsSetup.length > 0 ? ` \uC815\uC801 \uACBD\uB85C\uB97C \uC4F0\uB824\uBA74 package.json \uC758 dependenciesMeta \uC5D0 ${meta} \uB97C \uB450\uACE0 yarn install \uB4A4 .yarn/unplugged \uACBD\uB85C\uB97C \uB123\uC73C\uC138\uC694` : "";
2862
+ return `Plug'n'Play \uC5D0\uB294 node_modules \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. content \uC5D0 ${dyn} \uCC98\uB7FC \uC2E4\uC81C \uC704\uCE58\uB97C \uB123\uC73C\uC138\uC694(\uC815\uC801\uC73C\uB85C \uD655\uC778\uD560 \uC218 \uC5C6\uC5B4 WARN \uC73C\uB85C \uB0A8\uC2B5\uB2C8\uB2E4).${setup}`;
2863
+ }
2864
+ function contentPnpNoteTargets(targets) {
2865
+ return targets.filter((t) => t.pnp !== void 0 && t.pnp.state !== "rootOnly").map((t) => t.label);
2866
+ }
2867
+ function contentPnpNoteFor(targets) {
2868
+ const names = contentPnpNoteTargets(targets);
2869
+ if (names.length === 0) return void 0;
2870
+ const unplugged = targets.filter((t) => t.pnp?.state === "unplugged").map((t) => t.label);
2871
+ return contentPnpNote(names, unplugged);
2872
+ }
2873
+ var CONTENT_PNP_ROOT_ONLY_ACTION = "\uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694";
2874
+ var CONTENT_PNP_NO_NODE_MODULES = "Plug'n'Play \uB77C node_modules \uACBD\uB85C\uAC00 \uC5C6\uC74C";
2875
+ function contentTargetText(t) {
2876
+ if (t.pnp?.state === "noNodeModules") return `${t.label}(${CONTENT_PNP_NO_NODE_MODULES})`;
2877
+ if (t.pnp?.state === "rootOnly") return `${t.label} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 ${CONTENT_PNP_ROOT_ONLY_ACTION}`;
2878
+ return `${t.label} \u2192 "${t.suggest}"${t.note === void 0 ? "" : ` (${t.note})`}`;
2563
2879
  }
2564
- var CONTENT_PARENT_PATH_NOTE = "\uC0C1\uC704 \uACBD\uB85C\uB294 Tailwind 3 \uAC00 \uBE4C\uB4DC\uB97C \uC2E4\uD589\uD55C \uB514\uB809\uD130\uB9AC \uAE30\uC900\uC73C\uB85C \uD480\uB824\uC694. \uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8 \uB4F1 \uC571 \uB514\uB809\uD130\uB9AC \uBC16\uC5D0\uC11C \uBE4C\uB4DC\uD558\uBA74 content: { relative: true, files: [...] } \uB85C \uC124\uC815 \uD30C\uC77C \uAE30\uC900\uC73C\uB85C \uBC14\uAFB8\uC138\uC694";
2565
2880
  function relDirOf(baseDir, dir) {
2566
2881
  const rel3 = relative(baseDir, dir);
2567
2882
  return rel3 === "" || isAbsolute(rel3) ? void 0 : rel3.split(sep).join("/");
@@ -2581,8 +2896,26 @@ function packageVersion(dir) {
2581
2896
  return void 0;
2582
2897
  }
2583
2898
  }
2584
- function packageContentTarget(baseDir, name) {
2585
- const found = isPnpProject(baseDir) ? void 0 : findInstalledPackage(baseDir, name);
2899
+ function pnpPackageContentTarget(baseDir, name, pkg) {
2900
+ const legacySample = `node_modules/${name}/dist/index.js`;
2901
+ const none = { sample: "", suggest: "", alternates: [], others: [] };
2902
+ if (pnpRootOnlyDependency(baseDir, pkg, name) !== void 0) return { ...none, pnp: { state: "rootOnly", legacySample } };
2903
+ const range = pkg === void 0 ? void 0 : hasDependency(pkg, name);
2904
+ const rels = (range === void 0 ? [] : findPnpUnpluggedPackages(baseDir, name, range)).map((d) => relDirOf(baseDir, d)).filter((r) => r !== void 0);
2905
+ const root = pnpProjectRoot(baseDir);
2906
+ if (rels.length === 0 || root === void 0) return { ...none, pnp: { state: "noNodeModules", legacySample } };
2907
+ const rootRel = relDirOf(baseDir, root);
2908
+ return {
2909
+ sample: `${rels[0]}/dist/index.js`,
2910
+ suggest: `${rootRel === void 0 ? "." : rootRel}/.yarn/unplugged/*/node_modules/${name}/dist/**/*.{js,mjs}`,
2911
+ alternates: rels.slice(1).map((r) => `${r}/dist/index.js`),
2912
+ others: [],
2913
+ pnp: { state: "unplugged", legacySample }
2914
+ };
2915
+ }
2916
+ function packageContentTarget(baseDir, name, pkg) {
2917
+ if (isPnpProject(baseDir)) return pnpPackageContentTarget(baseDir, name, pkg);
2918
+ const found = findInstalledPackage(baseDir, name);
2586
2919
  const relDir = (found === void 0 ? void 0 : relDirOf(baseDir, found.dir)) ?? `node_modules/${name}`;
2587
2920
  const alternates = [];
2588
2921
  const others = [];
@@ -2603,7 +2936,7 @@ function packageContentTarget(baseDir, name) {
2603
2936
  const versions = theirs === void 0 || ours === void 0 ? "" : theirs === ours ? `(\uBC84\uC804\uC740 ${ours} \uB85C \uAC19\uC74C)` : `(${theirs}, \uC571 ${ours})`;
2604
2937
  others.push({
2605
2938
  sample: `${rel3}/dist/index.js`,
2606
- note: `\uC0C1\uC704 \uACBD\uB85C ${rel3} \uB294 \uC571\uC774 \uC4F0\uB294 \uC124\uCE58\uBCF8\uACFC \uB2E4\uB978 \uC124\uCE58\uBCF8${versions}\uC774\uB77C \uB36E\uC74C\uC73C\uB85C \uBCF4\uC9C0 \uC54A\uC544\uC694`
2939
+ note: `\uC0C1\uC704 \uACBD\uB85C ${rel3} \uB294 \uC571\uC774 \uC4F0\uB294 \uC124\uCE58\uBCF8\uACFC \uB2E4\uB978 \uC124\uCE58\uBCF8${versions}\uC774\uB77C \uB36E\uC74C\uC73C\uB85C \uBCF4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4`
2607
2940
  });
2608
2941
  }
2609
2942
  }
@@ -2680,7 +3013,7 @@ function inspectTailwindContent(baseDirInput) {
2680
3013
  }
2681
3014
  for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2682
3015
  if (declaredRange(baseDir, pkg, name) !== void 0) {
2683
- targets.push({ label: name, ...packageContentTarget(baseDir, name) });
3016
+ targets.push({ label: name, ...packageContentTarget(baseDir, name, pkg) });
2684
3017
  }
2685
3018
  }
2686
3019
  if (targets.length === 0) return null;
@@ -2690,11 +3023,14 @@ function inspectTailwindContent(baseDirInput) {
2690
3023
  configFile,
2691
3024
  unreadable: read === void 0,
2692
3025
  dynamic: read?.dynamic ?? false,
2693
- targets: targets.map(({ alternates = [], others = [], ...t }) => {
3026
+ targets: targets.map(({ alternates = [], others = [], pnp, ...t }) => {
2694
3027
  const covers = (sample) => globs.some((g) => contentGlobCovers(g, sample, baseDir));
2695
- const covered = [t.sample, ...alternates].some(covers);
3028
+ const samples = pnp !== void 0 && pnp.state !== "unplugged" ? [] : [t.sample, ...alternates];
3029
+ const coversSample = pnp?.state === "unplugged" ? (sample) => globs.some((g) => contentGlobCovers(g, sample, baseDir, { noDot: true })) : covers;
3030
+ const covered = samples.some(coversSample);
2696
3031
  const note = covered ? void 0 : others.find((o) => covers(o.sample))?.note;
2697
- return note === void 0 ? { ...t, covered } : { ...t, covered, note };
3032
+ const base = note === void 0 ? { ...t, covered } : { ...t, covered, note };
3033
+ return pnp === void 0 ? base : { ...base, pnp: { state: pnp.state, legacyCovered: covers(pnp.legacySample) } };
2698
3034
  })
2699
3035
  };
2700
3036
  }
@@ -2709,19 +3045,30 @@ function checkTailwindContent(baseDir) {
2709
3045
  message: `${r.configFile}\uC758 content \uAC00 ${r.targets.map((t) => t.label).join(" \xB7 ")} \uB97C \uB36E\uC2B5\uB2C8\uB2E4`
2710
3046
  };
2711
3047
  }
2712
- const list = missing.map((t) => `${t.label} \u2192 "${t.suggest}"${t.note === void 0 ? "" : ` (${t.note})`}`).join(" \xB7 ");
3048
+ const list = missing.map(contentTargetText).join(" \xB7 ");
2713
3049
  const note = missing.some((t) => t.suggest.startsWith("../")) ? ` (${CONTENT_PARENT_PATH_NOTE})` : "";
3050
+ const pnpText = contentPnpNoteFor(missing);
3051
+ const pnpNote = pnpText === void 0 ? "" : ` (${pnpText})`;
2714
3052
  if (r.unreadable || r.dynamic) {
2715
3053
  return {
2716
3054
  name: "tailwind.content",
2717
3055
  severity: "warn",
2718
- message: `${r.configFile}\uC758 content \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${list}${note}`
3056
+ message: `${r.configFile}\uC758 content \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${list}${note}${pnpNote}`
3057
+ };
3058
+ }
3059
+ if (missing.every((t) => t.pnp?.legacyCovered === true)) {
3060
+ const labels = missing.map((t) => t.label).join(" \xB7 ");
3061
+ const details = missing.filter((t) => t.pnp?.state !== "noNodeModules").map(contentTargetText);
3062
+ return {
3063
+ name: "tailwind.content",
3064
+ severity: "warn",
3065
+ message: `${r.configFile}\uC758 content \uAC00 Plug'n'Play \uC5D0\uC11C \uC2A4\uCE94\uB418\uC9C0 \uC54A\uB294 \uACBD\uB85C\uB85C ${labels} \uB97C \uB36E\uC2B5\uB2C8\uB2E4(node_modules \uACBD\uB85C \xB7 ./** \uB294 \uC2A4\uCE94\uB418\uC9C0 \uC54A\uC74C)${details.length > 0 ? `: ${details.join(" \xB7 ")}` : ""}${note}${pnpNote}`
2719
3066
  };
2720
3067
  }
2721
3068
  return {
2722
3069
  name: "tailwind.content",
2723
3070
  severity: "fail",
2724
- message: `${r.configFile}\uC758 content \uAC00 \uCEF4\uD3EC\uB10C\uD2B8 \uACBD\uB85C\uB97C \uB36E\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uD074\uB798\uC2A4\uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC74C): ${list}${note}`
3071
+ message: `${r.configFile}\uC758 content \uAC00 \uCEF4\uD3EC\uB10C\uD2B8 \uACBD\uB85C\uB97C \uB36E\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uD074\uB798\uC2A4\uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC74C): ${list}${note}${pnpNote}`
2725
3072
  };
2726
3073
  }
2727
3074
  var ANIMATION_PLUGIN_TAILWIND3 = "tailwindcss-animate";
@@ -2779,24 +3126,30 @@ function checkAnimationPlugin(baseDirInput) {
2779
3126
  return void 0;
2780
3127
  };
2781
3128
  if (tailwind.major !== void 0 && tailwind.major >= 4) {
2782
- const file = TAILWIND_CSS_CANDIDATES.find((c) => existsSync(join(baseDir, c)));
2783
- const css = file !== void 0 ? stripCssComments(readFileSync(join(baseDir, file), "utf8")) : "";
3129
+ const selection = selectCheckGlobalCss(baseDir);
3130
+ const note = globalCssSelectionNote(selection);
3131
+ const file = selection.rel;
3132
+ const expanded = selection.path !== void 0 && selection.rel !== void 0 ? expandGlobalCss(baseDir, selection.path, selection.rel, outsideCssDir(selection)) : void 0;
3133
+ const css = expanded !== void 0 ? stripCssComments(expanded.evidence) : "";
3134
+ const twAnimate = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${ANIMATION_PLUGIN_TAILWIND4}["']`);
3135
+ const animatePlugin = new RegExp(`@plugin\\s+["']${ANIMATION_PLUGIN_TAILWIND3}["']`);
2784
3136
  const lines = [];
2785
- if (new RegExp(`@import\\s+(?:url\\(\\s*)?["']${ANIMATION_PLUGIN_TAILWIND4}["']`).test(css)) lines.push(ANIMATION_PLUGIN_TAILWIND4);
2786
- if (new RegExp(`@plugin\\s+["']${ANIMATION_PLUGIN_TAILWIND3}["']`).test(css)) lines.push(ANIMATION_PLUGIN_TAILWIND3);
3137
+ if (twAnimate.test(css)) lines.push(ANIMATION_PLUGIN_TAILWIND4);
3138
+ if (animatePlugin.test(css)) lines.push(ANIMATION_PLUGIN_TAILWIND3);
3139
+ const via = expanded !== void 0 ? cssImportViaNote(viaFiles([evidenceOrigin(expanded, (f) => twAnimate.test(f)), evidenceOrigin(expanded, (f) => animatePlugin.test(f))])) : "";
2787
3140
  if (lines.length === 0) {
2788
3141
  return {
2789
3142
  name: "animation",
2790
3143
  severity: "warn",
2791
- message: `\uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(${by}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 ${ANIMATION_PLUGIN_TAILWIND4} \uC124\uCE58 \uB4A4 @import "${ANIMATION_PLUGIN_TAILWIND4}";`
3144
+ message: `\uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(${by}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 ${ANIMATION_PLUGIN_TAILWIND4} \uC124\uCE58 \uB4A4 @import "${ANIMATION_PLUGIN_TAILWIND4}";${note}`
2792
3145
  };
2793
3146
  }
2794
3147
  const problems = lines.map((name) => ({ name, why: pluginProblem(name) })).filter((x) => x.why !== void 0);
2795
- if (problems.length === 0) return { name: "animation", severity: "pass", message: `${file}\uC5D0 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4` };
3148
+ if (problems.length === 0) return { name: "animation", severity: "pass", message: `${file}\uC5D0 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4${via}${note}` };
2796
3149
  return {
2797
3150
  name: "animation",
2798
3151
  severity: "warn",
2799
- message: `${file}\uC758 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${by}, ${problems.map((x) => x.why).join(" \xB7 ")}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uACE0 \uBE4C\uB4DC\uAC00 \uC2E4\uD328\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4 \u2192 ${problems.map((x) => x.name).join(" \xB7 ")} \uB97C package.json \uC5D0 \uC120\uC5B8 \xB7 \uC124\uCE58\uD558\uC138\uC694`
3152
+ message: `${file}\uC758 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${by}, ${problems.map((x) => x.why).join(" \xB7 ")}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uACE0 \uBE4C\uB4DC\uAC00 \uC2E4\uD328\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4 \u2192 ${problems.map((x) => x.name).join(" \xB7 ")} \uB97C package.json \uC5D0 \uC120\uC5B8 \xB7 \uC124\uCE58\uD558\uC138\uC694${via}${note}`
2800
3153
  };
2801
3154
  }
2802
3155
  const configFile = TAILWIND_CONFIG_FILES.find((f) => existsSync(join(baseDir, f)));
@@ -3227,7 +3580,7 @@ import path10 from "path";
3227
3580
  import chalk7 from "chalk";
3228
3581
 
3229
3582
  // src/theme/brand-core.ts
3230
- import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "fs";
3583
+ import { existsSync as existsSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
3231
3584
  import { createRequire } from "module";
3232
3585
  import { dirname as dirname2, join as join2, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
3233
3586
  var TOKENS_PACKAGE = "@axis-ds/tokens";
@@ -3291,27 +3644,16 @@ function brandImportPath(entry) {
3291
3644
  }
3292
3645
  function isFile(file) {
3293
3646
  try {
3294
- return statSync(file).isFile();
3647
+ return statSync2(file).isFile();
3295
3648
  } catch {
3296
3649
  return false;
3297
3650
  }
3298
3651
  }
3299
3652
  function findGlobalCss(baseDir, config) {
3300
- const root = resolve2(baseDir);
3653
+ const selection = selectGlobalCss(resolve2(baseDir), config?.globalCss, GLOBAL_CSS_CANDIDATES);
3301
3654
  const result = {};
3302
- const configured = config?.globalCss;
3303
- if (typeof configured === "string" && configured.trim() !== "") {
3304
- const file = resolve2(root, configured);
3305
- if (isFile(file)) return { path: file };
3306
- result.configuredMissing = configured;
3307
- }
3308
- for (const candidate of GLOBAL_CSS_CANDIDATES) {
3309
- const file = join2(root, candidate);
3310
- if (isFile(file)) {
3311
- result.path = file;
3312
- break;
3313
- }
3314
- }
3655
+ if (selection.configuredMissing !== void 0) result.configuredMissing = selection.configuredMissing;
3656
+ if (selection.path !== void 0) result.path = selection.path;
3315
3657
  return result;
3316
3658
  }
3317
3659
  function findBrandImports(css) {
@@ -3338,18 +3680,24 @@ function inspectBrandImport(baseDir) {
3338
3680
  const brand = config?.brand;
3339
3681
  if (typeof brand !== "string" || brand.trim() === "") return null;
3340
3682
  const root = resolve2(baseDir);
3341
- const location = findGlobalCss(root, config);
3683
+ const location = selectGlobalCss(root, config?.globalCss, GLOBAL_CSS_CANDIDATES, { requireStylesheet: true });
3342
3684
  const base = { brand };
3343
3685
  if (location.configuredMissing !== void 0) base.configuredMissing = location.configuredMissing;
3686
+ if (location.configuredRejected !== void 0) base.configuredRejected = location.configuredRejected;
3344
3687
  if (!location.path) {
3345
3688
  return { ...base, state: "css-missing", imports: [] };
3346
3689
  }
3347
3690
  const cssFile = relative2(root, location.path).split(sep2).join("/");
3348
- const imports = [...new Set(findBrandImports(readFileSync2(location.path, "utf8")))];
3691
+ const expanded = expandGlobalCss(root, location.path, cssFile);
3692
+ const imports = [...new Set(findBrandImports(expanded.evidence))];
3349
3693
  let state;
3350
3694
  if (imports.includes(brand)) state = "match";
3351
3695
  else if (imports.length === 0) state = "import-missing";
3352
3696
  else state = "other-brand";
3697
+ const sub = expanded.files.filter((f) => f.depth > 0);
3698
+ const via = state === "match" ? sub.filter((f) => findBrandImports(f.css).includes(brand)).slice(0, 1) : state === "other-brand" ? sub.filter((f) => findBrandImports(f.css).length > 0) : [];
3699
+ const entryHas = state === "match" && findBrandImports(expanded.files[0].css).includes(brand);
3700
+ if (via.length > 0 && !entryHas) return { ...base, state, cssFile, imports, importVia: via.map((f) => f.rel) };
3353
3701
  return { ...base, state, cssFile, imports };
3354
3702
  }
3355
3703
  function isMissingBuildFile(err) {
@@ -3707,13 +4055,14 @@ function checkBrand(baseDir) {
3707
4055
  }
3708
4056
  function brandCheckResult(check) {
3709
4057
  const { brand } = check;
3710
- const suffix = check.configuredMissing !== void 0 ? ` (globalCss ${check.configuredMissing} \uC5C6\uC74C)` : "";
4058
+ const suffix = globalCssSelectionNote(check);
4059
+ const via = cssImportViaNote(check.importVia);
3711
4060
  switch (check.state) {
3712
4061
  case "match":
3713
4062
  return {
3714
4063
  name: "brand",
3715
4064
  severity: "pass",
3716
- message: `brand "${brand}" import \uAC00 ${check.cssFile}\uC5D0 \uC788\uC2B5\uB2C8\uB2E4${suffix}`
4065
+ message: `brand "${brand}" import \uAC00 ${check.cssFile}\uC5D0 \uC788\uC2B5\uB2C8\uB2E4${via}${suffix}`
3717
4066
  };
3718
4067
  case "css-missing":
3719
4068
  return {
@@ -3731,7 +4080,7 @@ function brandCheckResult(check) {
3731
4080
  return {
3732
4081
  name: "brand",
3733
4082
  severity: "warn",
3734
- message: `brand "${brand}" \uC640 import(${check.imports.join(", ")})\uAC00 \uB2E4\uB985\uB2C8\uB2E4${suffix}`
4083
+ message: `brand "${brand}" \uC640 import(${check.imports.join(", ")})\uAC00 \uB2E4\uB985\uB2C8\uB2E4${via}${suffix}`
3735
4084
  };
3736
4085
  }
3737
4086
  }
@@ -3876,12 +4225,11 @@ import chalk9 from "chalk";
3876
4225
  import path12 from "path";
3877
4226
 
3878
4227
  // src/theme/theme-add.ts
3879
- import { accessSync, constants, existsSync as existsSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, statSync as statSync2, unlinkSync, writeFileSync } from "fs";
4228
+ import { accessSync, constants, existsSync as existsSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, unlinkSync, writeFileSync } from "fs";
3880
4229
  import path11 from "path";
3881
4230
  var TOKENS_DEPENDENCY_RANGE = `^${TOKENS_DEPENDENCY_MIN_VERSION}`;
3882
4231
  var TAILWIND3_BASE_IMPORTS = [`${TOKENS_PACKAGE}/css`, `${TOKENS_PACKAGE}/css/shadcn`];
3883
4232
  var THEME_MIN_VERSION = "1.3.0";
3884
- var COMPONENT_TAILWIND_CSS_MIN_VERSION = "1.4.0";
3885
4233
  function readText(file) {
3886
4234
  return existsSync3(file) ? readFileSync4(file, "utf8") : null;
3887
4235
  }
@@ -3980,13 +4328,6 @@ function realPathInside(baseDir, file) {
3980
4328
  const inside = relPath !== "" && !relPath.startsWith("..") && !path11.isAbsolute(relPath);
3981
4329
  return { inside, realPath };
3982
4330
  }
3983
- function isFile2(file) {
3984
- try {
3985
- return statSync2(file).isFile();
3986
- } catch {
3987
- return false;
3988
- }
3989
- }
3990
4331
  function isStylesheet(realPath) {
3991
4332
  return path11.extname(realPath).toLowerCase() === ".css";
3992
4333
  }
@@ -4062,16 +4403,11 @@ function planThemeAdd(baseDirInput, brand) {
4062
4403
  const deferredComponentImports = [];
4063
4404
  if (tailwind4 && pkg !== void 0) {
4064
4405
  for (const n of TAILWIND4_COMPONENT_PACKAGES) {
4065
- if (hasDependency(pkg, n) !== void 0) {
4066
- componentImports.push(n);
4067
- continue;
4068
- }
4069
- if (declaredDependency(baseDir, pkg, n, rootPkg)?.source !== "root") continue;
4070
- const installed2 = findInstalledPackage(baseDir, n);
4071
- if (installed2 !== void 0 && isFile2(path11.join(installed2.dir, "tailwind.css"))) {
4072
- componentImports.push(n);
4073
- } else {
4074
- deferredComponentImports.push(installed2 === void 0 ? { name: n } : { name: n, installedVersion: installed2.version ?? "?" });
4406
+ const r = componentCssResolution(baseDir, pkg, n);
4407
+ if (r.kind === "app" || r.kind === "root-ok") componentImports.push(n);
4408
+ else if (r.kind === "root-not-installed") deferredComponentImports.push({ name: n });
4409
+ else if (r.kind === "root-old" || r.kind === "root-missing-file") {
4410
+ deferredComponentImports.push({ name: n, installedVersion: r.installedVersion });
4075
4411
  }
4076
4412
  }
4077
4413
  }
@@ -4487,13 +4823,7 @@ function renderThemeAdd(plan, written) {
4487
4823
  );
4488
4824
  }
4489
4825
  for (const d of css?.deferredComponentImports ?? []) {
4490
- const line = `@import "${d.name}/tailwind.css";`;
4491
- const current = d.installedVersion !== void 0 && d.installedVersion !== "?" && (compareExactVersions(d.installedVersion, COMPONENT_TAILWIND_CSS_MIN_VERSION) ?? -1) >= 0;
4492
- lines.push(
4493
- chalk9.yellow(
4494
- d.installedVersion === void 0 ? ` ! ${d.name} \uAC00 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC544 ${line} \uB97C \uB123\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58(${plan.installCommand}) \uB4A4 theme add \uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uC9C1\uC811 \uB123\uC73C\uC138\uC694.` : current ? ` ! \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${d.name} \uC758 \uC124\uCE58\uBCF8(${d.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4 ${line} \uB97C \uB123\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uAC00 \uC190\uC0C1\uB410\uC744 \uC218 \uC788\uC73C\uB2C8 \uB2E4\uC2DC \uC124\uCE58(${plan.installCommand}) \uB4A4 theme add \uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uC9C1\uC811 \uB123\uC73C\uC138\uC694.` : ` ! \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${d.name} \uC758 \uC124\uCE58\uBCF8(${d.installedVersion === "?" ? "\uBC84\uC804 \uBAA8\uB984" : d.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4(${COMPONENT_TAILWIND_CSS_MIN_VERSION} \uBD80\uD130 \uC81C\uACF5) ${line} \uB97C \uB123\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. ${COMPONENT_TAILWIND_CSS_MIN_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9B0 \uB4A4 theme add \uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uC9C1\uC811 \uB123\uC73C\uC138\uC694.`
4495
- )
4496
- );
4826
+ lines.push(chalk9.yellow(` ! ${deferredComponentImportReason(d, plan.installCommand)}`));
4497
4827
  }
4498
4828
  if (css && css.otherBrands.length > 0) {
4499
4829
  lines.push(
@@ -4551,13 +4881,14 @@ function renderThemeAdd(plan, written) {
4551
4881
  );
4552
4882
  }
4553
4883
  for (const t of tw.contentMissing) {
4554
- lines.push(
4555
- chalk9.yellow(
4556
- ` ! content \uAC00 ${t.label} \uB97C \uB36E\uC9C0 ${tw.contentUnknown ? "\uC54A\uB294 \uAC83\uC73C\uB85C \uBCF4\uC785\uB2C8\uB2E4(\uC815\uC801 \uD310\uB3C5)" : "\uC54A\uC2B5\uB2C8\uB2E4"}. \uB123\uC73C\uC138\uC694: "${t.suggest}"`
4557
- )
4558
- );
4884
+ const not = tw.contentUnknown ? "\uC54A\uB294 \uAC83\uC73C\uB85C \uBCF4\uC785\uB2C8\uB2E4(\uC815\uC801 \uD310\uB3C5)" : "\uC54A\uC2B5\uB2C8\uB2E4";
4885
+ if (t.pnp?.state === "noNodeModules") lines.push(chalk9.yellow(` ! content \uAC00 ${t.label} \uB97C Plug'n'Play \uC5D0\uC11C \uC2A4\uCE94\uB418\uB294 \uACBD\uB85C\uB85C \uB36E\uC9C0 ${not}`));
4886
+ else if (t.pnp?.state === "rootOnly") lines.push(chalk9.yellow(` ! ${t.label} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 ${CONTENT_PNP_ROOT_ONLY_ACTION}`));
4887
+ else lines.push(chalk9.yellow(` ! content \uAC00 ${t.label} \uB97C \uB36E\uC9C0 ${not}. \uB123\uC73C\uC138\uC694: "${t.suggest}"`));
4559
4888
  }
4560
4889
  if (tw.contentMissing.some((t) => t.suggest.startsWith("../"))) lines.push(chalk9.gray(` ${CONTENT_PARENT_PATH_NOTE}`));
4890
+ const pnpNote = contentPnpNoteFor(tw.contentMissing);
4891
+ if (pnpNote !== void 0) lines.push(chalk9.gray(` ${pnpNote}`));
4561
4892
  }
4562
4893
  const act = plan.activation;
4563
4894
  lines.push("", chalk9.bold(`\uBE0C\uB79C\uB4DC \uCF1C\uAE30 (ThemeProvider \uB294 ${THEME_PACKAGE} ${THEME_MIN_VERSION} \uC774\uC0C1)`));
package/dist/monet.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  getMonetMcpConfig,
5
5
  getMonetUrl,
6
6
  monetImportNextSteps
7
- } from "./chunk-I3HWV7Y5.js";
7
+ } from "./chunk-EBFXCLSV.js";
8
8
  import "./chunk-HFMMK6R4.js";
9
9
  export {
10
10
  MONET_CATEGORIES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axis-ds/cli",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "type": "module",
5
5
  "description": "AXIS Design System - CLI for adding components (shadcn compatible)",
6
6
  "publishConfig": {