@axis-ds/cli 1.7.5 → 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 +14 -7
- package/dist/{chunk-YYSEFWYD.js → chunk-EBFXCLSV.js} +252 -4
- package/dist/index.js +593 -169
- package/dist/monet.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,27 +89,34 @@ axis theme add ontology
|
|
|
89
89
|
- 브랜드 CSS 는 `[data-brand="<brand>"]` 선택자로 적용되므로 루트 레이아웃의 `<html data-brand>` 나 `ThemeProvider` 의 `setBrand` 가 있어야 화면이 바뀝니다. 코드는 고치지 않고, 레이아웃에서 찾지 못하면 두 방법의 예시를 냅니다(`defaultBrand` 는 `@axis-ds/theme` 1.6.2 이하에서 `setBrand(undefined)` 로 끈 상태가 새로고침 뒤 유지되지 않아 예시에 쓰지 않습니다). 마크업 `data-brand` 와 `ThemeProvider` 를 함께 쓰는 레이아웃이면 theme 1.7.0 이상이 필요하다고 한 줄 더 알립니다(1.6.1 부터). Tailwind 3 이면 preset · content 가 빠졌을 때 넣을 줄도 안내합니다(설정 파일은 고치지 않음). preset 안내는 1.6.1 부터 CommonJS 에서 모든 tokens 버전에 되는 `require("@axis-ds/tokens/tailwind").default` 입니다(1.6.0 의 `.default` 없는 형태는 tokens 1.6.2 이하에서 preset 이 빠짐).
|
|
90
90
|
- 넣은 import 줄의 출력은 1.6.1 부터 실제 위치 · 분류를 따릅니다. 컴포넌트 `tailwind.css` 는 「컴포넌트 스캔 경로, 위치 무관」, 기본 토큰 CSS 는 브랜드 줄보다 앞에 들어갔을 때만 「브랜드 앞」이고 뒤에 들어가면 옮기라고 경고합니다.
|
|
91
91
|
- `@axis-ds/tokens` 가 없거나 1.3.0 미만이면 목록 대신 설치 · 업그레이드 안내를 내고 종료 코드 1 로 끝납니다.
|
|
92
|
+
- 1.7.6 부터 `theme list` · `theme info` 의 안내는 선언 출처를 따릅니다. 앱이나 pnpm · yarn 워크스페이스 루트에 선언돼 있으면 「설치: <pm> install」, npm 워크스페이스 루트에 선언돼 있으면 「설치: 워크스페이스 루트에서 npm install」, 루트 선언이 1.3.0 미만이면 루트 선언을 올리라는 줄, 빌드 전 재설치도 install 입니다. Yarn PnP 에서 앱에 선언이 있으면 설치본을 판정하지 못했다는 한 줄을 냅니다. 선언이 없으면(또는 PnP · 앱 자체 lockfile 이라 루트 선언을 보지 않으면) 종전 추가 명령입니다.
|
|
93
|
+
- 같은 1.7.6 부터 `theme add` 는 앱에 선언된 tokens 가 설치되지 않았으면 「설치: <pm> install」을, 설치본이 빌드 전이면 선언 출처(앱 · 루트)와 상관없이 `theme list` 와 같은 재설치 안내(npm 워크스페이스 루트 선언이면 「워크스페이스 루트에서 npm install」)를 더합니다. Yarn PnP 에서 앱에 선언이 있으면 설치 · 재설치 · 업그레이드 줄 대신 `theme list` 와 같은 판정하지 못했다는 한 줄을 냅니다. 루트 선언 미설치의 「설치: <pm> install」 줄은 npm 이어도 앱 디렉터리 명령 그대로입니다.
|
|
92
94
|
- `axis check` 는 `axis.config.json` 의 `brand` 와 CSS 의 브랜드 import 가 맞는지도 봅니다.
|
|
93
95
|
|
|
94
96
|
## 프로젝트 검증 (`axis check`)
|
|
95
97
|
|
|
96
|
-
- Tailwind 구성
|
|
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` 를 찾습니다.
|
|
97
101
|
- Tailwind 4: `@import "tailwindcss"` 가 있는 CSS 에서 아래 문제가 하나도 없으면 PASS 이고, 하나라도 있으면 WARN 입니다(블록 주석 안은 없는 것으로 봅니다). `@import "tailwindcss"` 가 없으면 그 WARN 하나만 냅니다.
|
|
98
102
|
1. 토큰 진입점 `@import "@axis-ds/tokens/css/tailwind4";` 이 없음 → 문구에 넣을 import 줄. 진입점이 없으면 `bg-primary` · `rounded-md` 같은 AXIS 매핑이 만들어지지 않습니다. `@axis-ds/tokens/css` · `css/shadcn` · `tailwind`(Tailwind 3 preset)는 진입점으로 치지 않습니다.
|
|
99
|
-
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 쪽으로 판정).
|
|
100
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 기본 블록 이름이라, 같은 선언이 다른 선택자에 있어도 그 이름으로 표시됩니다.
|
|
101
105
|
4. 진입점이 있는데 `@axis-ds/tokens` 가 아래 버전 조건에 걸림.
|
|
102
|
-
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 이거나 앱 위치에서 설치본을 찾지 못하면 이 판정을 하지 않습니다.
|
|
103
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:` 는 넣지 않습니다.
|
|
104
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)`
|
|
105
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 참조가 없는 자체 매핑도 통과했습니다).
|
|
106
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 입니다.
|
|
107
|
-
- globals.css 항목:
|
|
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 기본 정의로 냅니다).
|
|
108
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 까지는 선언만 보고 「모두 설치되어 있습니다」였습니다.
|
|
114
|
+
- 1.7.6 부터 워크스페이스 루트 `package.json` 에 선언한 `@axis-ds/*`(앱에 없는 것)도 설치 확인 대상이고, 설치되지 않았으면 「워크스페이스 루트에 선언돼 있지만 설치되지 않았습니다」 WARN 입니다(종료 코드는 그대로, 1.7.5 까지는 앱 선언만 봐서 PASS).
|
|
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` 설정은 읽지 않습니다.
|
|
109
116
|
- 해당할 때만 더하는 항목(1.6.0 부터, brand 항목 뒤 이 순서):
|
|
110
|
-
- `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 이하는 마운트 때 속성을 지움).
|
|
111
|
-
- `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 부터, 설치본이
|
|
112
|
-
- `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 입니다.
|
|
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` 판정도 같음).
|
|
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` 에서만 봅니다.
|
|
113
120
|
- FAIL 이 하나라도 있으면 종료 코드 1 로 끝납니다. WARN 만 있으면 0 입니다(1.3.0 부터, 이전에는 결과와 관계없이 0).
|
|
114
121
|
- MCP `axis.check_project` 도 같은 항목 · 문구를 냅니다.
|
|
115
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);
|
|
@@ -109,14 +109,56 @@ function workspaceRootPackage(baseDirInput) {
|
|
|
109
109
|
return void 0;
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
-
function declaredDependency(baseDir, pkg, name, rootPkg) {
|
|
112
|
+
function declaredDependency(baseDir, pkg, name, rootPkg, options = {}) {
|
|
113
113
|
if (pkg === void 0) return void 0;
|
|
114
114
|
const own = hasDependency(pkg, name);
|
|
115
115
|
if (own !== void 0) return { range: own, source: "app" };
|
|
116
|
+
if (options.appOnlyInPnp && isPnpProject(baseDir)) return void 0;
|
|
116
117
|
const root = rootPkg === void 0 ? workspaceRootPackage(baseDir) : rootPkg ?? void 0;
|
|
117
118
|
const fromRoot = root !== void 0 ? hasDependency(root, name) : void 0;
|
|
118
119
|
return fromRoot !== void 0 ? { range: fromRoot, source: "root" } : void 0;
|
|
119
120
|
}
|
|
121
|
+
function pnpRootOnlyDependency(baseDir, pkg, name, rootPkg) {
|
|
122
|
+
if (pkg === void 0 || hasDependency(pkg, name) !== void 0 || !isPnpProject(baseDir)) return void 0;
|
|
123
|
+
const found = declaredDependency(baseDir, pkg, name, rootPkg);
|
|
124
|
+
return found?.source === "root" ? found : void 0;
|
|
125
|
+
}
|
|
126
|
+
var PNP_ROOT_ONLY_NOTE = "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0\uB9CC \uC120\uC5B8\uB3FC Plug'n'Play \uC5D0\uC11C \uC571\uC774 \uD574\uC11D\uD558\uC9C0 \uBABB\uD569\uB2C8\uB2E4";
|
|
127
|
+
var APP_LOCKFILES = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"];
|
|
128
|
+
function hasAppLockfile(baseDirInput) {
|
|
129
|
+
const baseDir = path.resolve(baseDirInput);
|
|
130
|
+
return APP_LOCKFILES.some((f) => existsSync(path.join(baseDir, f)));
|
|
131
|
+
}
|
|
132
|
+
function rootDeclarationsAllowed(baseDir, pkg) {
|
|
133
|
+
return pkg !== void 0 && !isPnpProject(baseDir) && !hasAppLockfile(baseDir);
|
|
134
|
+
}
|
|
135
|
+
function readPackageJsonAt(dir) {
|
|
136
|
+
try {
|
|
137
|
+
const parsed = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf8"));
|
|
138
|
+
return isPlainObject(parsed) ? parsed : void 0;
|
|
139
|
+
} catch {
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function dependencyInstallGuide(baseDirInput, name, range) {
|
|
144
|
+
const baseDir = resolveBaseDir(baseDirInput);
|
|
145
|
+
const installCommand = detectInstallCommand(baseDir);
|
|
146
|
+
const base = { installCommand, addCommand: addCommandFor(installCommand, { [name]: range }) };
|
|
147
|
+
const pkg = readPackageJsonAt(baseDir);
|
|
148
|
+
if (pkg === void 0) return { kind: "add", ...base };
|
|
149
|
+
const own = hasDependency(pkg, name);
|
|
150
|
+
if (isPnpProject(baseDir)) return own !== void 0 ? { kind: "pnp", source: "app", range: own, ...base } : { kind: "add", ...base };
|
|
151
|
+
if (own !== void 0) return { kind: "install", source: "app", range: own, ...base };
|
|
152
|
+
if (!rootDeclarationsAllowed(baseDir, pkg)) return { kind: "add", ...base };
|
|
153
|
+
const declared = declaredDependency(baseDir, pkg, name);
|
|
154
|
+
if (declared?.source !== "root") return { kind: "add", ...base };
|
|
155
|
+
return {
|
|
156
|
+
kind: packageManagerOf(installCommand) === "npm" ? "install-at-root" : "install",
|
|
157
|
+
source: "root",
|
|
158
|
+
range: declared.range,
|
|
159
|
+
...base
|
|
160
|
+
};
|
|
161
|
+
}
|
|
120
162
|
function isPlainObject(value) {
|
|
121
163
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
122
164
|
}
|
|
@@ -185,6 +227,32 @@ function isDirectory(dir) {
|
|
|
185
227
|
function findFirstFile(baseDir, candidates) {
|
|
186
228
|
return candidates.find((c) => isFile(path.join(baseDir, c)));
|
|
187
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
|
+
}
|
|
188
256
|
function detectProjectPaths(baseDirInput) {
|
|
189
257
|
const baseDir = path.resolve(baseDirInput);
|
|
190
258
|
const hasSrc = isDirectory(path.join(baseDir, "src"));
|
|
@@ -244,14 +312,179 @@ function findInstalledPackage(baseDirInput, name) {
|
|
|
244
312
|
}
|
|
245
313
|
}
|
|
246
314
|
function isPnpProject(baseDirInput) {
|
|
315
|
+
return pnpProjectRoot(baseDirInput) !== void 0;
|
|
316
|
+
}
|
|
317
|
+
function pnpProjectRoot(baseDirInput) {
|
|
247
318
|
let dir = resolveBaseDir(baseDirInput);
|
|
248
319
|
for (; ; ) {
|
|
249
|
-
if (isFile(path.join(dir, ".pnp.cjs")) || isFile(path.join(dir, ".pnp.js"))) return
|
|
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;
|
|
250
322
|
const parent = path.dirname(dir);
|
|
251
|
-
if (parent === dir) return
|
|
323
|
+
if (parent === dir) return void 0;
|
|
252
324
|
dir = parent;
|
|
253
325
|
}
|
|
254
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
|
+
}
|
|
255
488
|
function compareExactVersions(a, b) {
|
|
256
489
|
const parse = (v) => {
|
|
257
490
|
const m = /^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?/.exec(v.trim());
|
|
@@ -453,17 +686,32 @@ export {
|
|
|
453
686
|
hasDependency,
|
|
454
687
|
workspaceRootPackage,
|
|
455
688
|
declaredDependency,
|
|
689
|
+
pnpRootOnlyDependency,
|
|
690
|
+
PNP_ROOT_ONLY_NOTE,
|
|
691
|
+
rootDeclarationsAllowed,
|
|
692
|
+
dependencyInstallGuide,
|
|
456
693
|
isPlainObject,
|
|
457
694
|
addCommandPrefix,
|
|
458
695
|
addCommandFor,
|
|
459
696
|
runScriptCommand,
|
|
460
697
|
workspaceRunCommand,
|
|
461
698
|
GLOBAL_CSS_CANDIDATES,
|
|
699
|
+
selectGlobalCss,
|
|
700
|
+
globalCssSelectionNote,
|
|
701
|
+
cssImportViaNote,
|
|
462
702
|
detectProjectPaths,
|
|
463
703
|
projectFileExists,
|
|
464
704
|
ROOT_LAYOUT_FILES,
|
|
465
705
|
findInstalledPackage,
|
|
466
706
|
isPnpProject,
|
|
707
|
+
pnpProjectRoot,
|
|
708
|
+
TAILWIND4_MIN_AXIS_VERSION,
|
|
709
|
+
componentCssResolution,
|
|
710
|
+
componentCssResolvable,
|
|
711
|
+
tokensCssResolution,
|
|
712
|
+
outsideCssPackageResolution,
|
|
713
|
+
deferredComponentImportReason,
|
|
714
|
+
findPnpUnpluggedPackages,
|
|
467
715
|
compareExactVersions,
|
|
468
716
|
MONET_CATEGORIES,
|
|
469
717
|
MonetClient,
|