@axis-ds/cli 1.3.0 → 1.4.0

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.
Files changed (3) hide show
  1. package/README.md +33 -2
  2. package/dist/index.js +353 -42
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,6 +23,28 @@ axis add button
23
23
  axis list
24
24
  ```
25
25
 
26
+ ## 프로젝트 초기화 (`axis init`)
27
+
28
+ - `axis.config.json` 과 `cn()` 을 담은 `utils.ts` 를 만들고, `clsx` · `tailwind-merge` 가 `package.json` 에 없으면 `dependencies` 에 더합니다. 설치는 실행하지 않고 명령만 안내합니다.
29
+ - `tailwind-merge` 범위와 아래 안내는 프로젝트의 Tailwind 버전(설치본, 없으면 `package.json` 선언)으로 정합니다. 판정하지 못하면 Tailwind 3 으로 봅니다.
30
+ - Tailwind 4 이상이면 전역 CSS 에 넣을 줄을 안내합니다. CSS 파일은 고치지 않습니다.
31
+
32
+ ```css
33
+ @import "tailwindcss";
34
+ @import "@axis-ds/tokens/css/tailwind4";
35
+ @import "@axis-ds/ui-react/tailwind.css"; /* npm 패키지 컴포넌트를 쓸 때, agentic-ui 도 같은 방식 */
36
+ ```
37
+
38
+ - 컴포넌트 패키지 줄은 `package.json` 에 선언된 `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 만큼 냅니다. 선언이 없으면 예시 한 줄만 냅니다(`axis add` 로 복사한 컴포넌트는 프로젝트 소스라 따로 넣을 필요가 없습니다).
39
+ - create-next-app 기본 `globals.css` 의 `:root` · `@theme inline` · `@media (prefers-color-scheme: dark)` · `body` 블록이 AXIS 와 같은 이름(`--background` · `--foreground` · `--color-background` · `--font-sans` · `--font-mono`)을 다른 값으로 정의하니 지우라는 주의를 함께 냅니다(Quick Start 가이드 「Tailwind CSS 4」와 같은 내용).
40
+ - `@axis-ds/tokens` 가 `package.json` 에 없으면 lockfile 에 맞춘 설치 명령(예: `pnpm add "@axis-ds/tokens@^1.4.0"`)을 안내합니다. 토큰 진입점은 1.4.0 부터 있습니다.
41
+ - `package.json` 에 선언된 `@axis-ds/tokens` · `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 의 버전 조건(`axis check` 와 같은 함수 · 같은 판정). 설치본을 먼저 보고, 없으면 선언 범위를 봅니다:
42
+ - 설치본: `node_modules/<패키지>/package.json` 의 `version` 이 정확한 버전이면 그것으로 판정합니다. 1.4.0 이상이면 선언이 `^1.3.0` · `1.x` · `catalog:` 여도 안내하지 않고, 미만이면(`1.4.0-beta.1` 포함) 설치본 버전을 보여 주며 `^1.4.0` 으로 올리는 명령을 냅니다(예: `설치본(@axis-ds/tokens 1.3.0, 선언 ^1.4.0)을 올리세요: pnpm add "@axis-ds/tokens@^1.4.0"`). 다만 설치본이 미만이어도 선언 하한을 읽을 수 없으면(`catalog:` · `npm:` · `latest` · `||` 등) 명령을 내지 않고 선언이 가리키는 버전을 1.4.0 이상으로 올린 뒤 다시 설치하라는 줄만 냅니다(선언을 덮어쓰지 않음).
43
+ - 설치본이 없을 때 선언 범위의 하한이 1.4.0 미만이면(예: `^1.3.0` · `1.x` · `1.3.0 - 1.5.0` · `^1.4.0-beta.1`) 그 패키지들을 `^1.4.0` 으로 올리는 명령(예: `pnpm add "@axis-ds/tokens@^1.4.0"`)을 냅니다. 하이픈 범위는 왼쪽 버전을, 비교자 여러 개(`>=1.2 >=1.4`)는 가장 높은 하한을 봅니다.
44
+ - 하한을 읽을 수 없으면(`catalog:` · `latest` · `||` · `<2` · `*` 등) 선언을 덮어쓰는 명령 대신 「설치된 버전이 1.4.0 이상인지 확인하세요」를 냅니다.
45
+ - `workspace:` 프로토콜은 모노레포 링크라 설치본과 관계없이 안내하지 않습니다(링크된 패키지의 `version` 은 배포 전 소스의 값이라 1.4.0 미만이어도 진입점이 없다는 근거가 아닙니다).
46
+ - Tailwind 4 에는 설정 파일이 없으므로 `axis.config.json` 에 `tailwindConfig` 를 적지 않고, 대화형 실행에서도 묻지 않습니다. Tailwind 3 은 종전과 같습니다.
47
+
26
48
  ## 브랜드 테마 (`axis theme`)
27
49
 
28
50
  프로젝트에 설치된 `@axis-ds/tokens`(1.3.0 이상)의 브랜드를 조회하고 적용합니다. 세 명령 모두 `-d, --dir <path>` 로 대상 디렉터리를 받습니다(기본: 현재 디렉터리).
@@ -46,8 +68,17 @@ axis theme add ontology
46
68
 
47
69
  ## 프로젝트 검증 (`axis check`)
48
70
 
49
- - Tailwind 구성 항목: Tailwind 4 이상(설치본, 없으면 `package.json` 선언으로 판정)은 설정 파일 대신 전역 CSS(`globals.css` · `app/globals.css` · `src/app/globals.css` · `src/globals.css` · `styles/globals.css` 순)의 `@import "tailwindcss"` 를 봅니다. Tailwind 3 이하는 `tailwind.config.ts` · `.js` · `.mjs` · `.cjs` 를 찾습니다.
50
- - 이 항목은 Tailwind 진입 import 만 봅니다. `@axis-ds/tokens` 의 Tailwind 연동은 Tailwind 3 preset(`@axis-ds/tokens/tailwind`)뿐이라, Tailwind 4 에서 PASS 여도 `bg-primary` 같은 AXIS 색 클래스가 만들어진다는 뜻은 아닙니다.
71
+ - 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` 를 찾습니다.
72
+ - Tailwind 4: `@import "tailwindcss"` 가 있는 CSS 에서 아래 문제가 하나도 없으면 PASS 이고, 하나라도 있으면 WARN 입니다(블록 주석 안은 없는 것으로 봅니다). `@import "tailwindcss"` 가 없으면 그 WARN 하나만 냅니다.
73
+ 1. 토큰 진입점 `@import "@axis-ds/tokens/css/tailwind4";` 이 없음 → 문구에 넣을 import 줄. 진입점이 없으면 `bg-primary` · `rounded-md` 같은 AXIS 매핑이 만들어지지 않습니다. `@axis-ds/tokens/css` · `css/shadcn` · `tailwind`(Tailwind 3 preset)는 진입점으로 치지 않습니다.
74
+ 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 쪽으로 판정).
75
+ 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 기본 블록 이름이라, 같은 선언이 다른 선택자에 있어도 그 이름으로 표시됩니다.
76
+ 4. 진입점이 있는데 `@axis-ds/tokens` 가 아래 버전 조건에 걸림.
77
+ 5. `@axis-ds/ui-react/tailwind.css` · `@axis-ds/agentic-ui/tailwind.css` 를 import 하는데 그 패키지가 아래 버전 조건에 걸림(`tailwind.css` 는 1.4.0 부터 있음). 직접 `@source` 로 스캔하는 패키지는 `tailwind.css` 가 필요 없으므로 이 문제를 만들지 않습니다.
78
+ - 문제가 여럿이면 위 순서대로 ` / ` 로 이어 한 항목에 냅니다. 끝에는 `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:` 는 넣지 않습니다.
79
+ - 문구 예: `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)`
80
+ - Tailwind 3: `tailwind.config.ts` 에 `--background` · `hsl(var(` 가 있으면 종전 문구로 PASS 입니다. 그렇지 않아도 설정 파일(`.ts` · `.js` · `.mjs` · `.cjs`)이 `@axis-ds/tokens/tailwind` preset 을 `require` · `import` 하면(주석 밖) `tailwind.config.mjs에 AXIS 토큰이 설정되어 있습니다 (@axis-ds/tokens/tailwind preset)` 처럼 PASS 입니다. 1.3.0 에서는 preset 만 쓰는 `.ts` 가 WARN(`AXIS 토큰 설정이 없습니다`), `.js` · `.mjs` · `.cjs` 가 WARN(`TypeScript 권장`)이었습니다.
81
+ - globals.css 항목: 같은 후보에서 처음 찾은 CSS 하나를 봅니다. `--background` 와 `--primary` 를 직접 정의하거나, `@axis-ds/tokens/css` · `@axis-ds/tokens/css/shadcn` · `@axis-ds/tokens/css/tailwind4` 를 import 하면(블록 주석 밖, 작은따옴표 · `url()` · `layer()` 표기 포함) PASS 입니다. import 로 인정한 경우 문구 끝에 `(@axis-ds/tokens/css/tailwind4 import)` 처럼 경로가 붙습니다. 1.4.0 부터이고, 이전에는 import 만 있으면 WARN 이었습니다.
51
82
  - FAIL 이 하나라도 있으면 종료 코드 1 로 끝납니다. WARN 만 있으면 0 입니다(1.3.0 부터, 이전에는 결과와 관계없이 0).
52
83
  - MCP `axis.check_project` 도 같은 항목 · 문구를 냅니다.
53
84
 
package/dist/index.js CHANGED
@@ -1580,7 +1580,7 @@ function createInitConfig(answers, registry) {
1580
1580
  return {
1581
1581
  $schema: AXIS_CONFIG_SCHEMA_URL,
1582
1582
  componentsDir: answers.componentsDir,
1583
- tailwindConfig: answers.tailwindConfig,
1583
+ ...answers.tailwindConfig !== void 0 ? { tailwindConfig: answers.tailwindConfig } : {},
1584
1584
  globalCss: answers.globalCss,
1585
1585
  registry
1586
1586
  };
@@ -2220,9 +2220,222 @@ var TAILWIND_CSS_CANDIDATES = [
2220
2220
  "styles/globals.css"
2221
2221
  ];
2222
2222
  var TAILWIND_IMPORT = /@import\s+(?:url\(\s*)?["']tailwindcss["']/;
2223
+ function stripCssComments(css) {
2224
+ return css.replace(/\/\*[\s\S]*?\*\//g, "");
2225
+ }
2223
2226
  function hasTailwindImport(css) {
2224
- return TAILWIND_IMPORT.test(css.replace(/\/\*[\s\S]*?\*\//g, ""));
2227
+ return TAILWIND_IMPORT.test(stripCssComments(css));
2228
+ }
2229
+ var TOKENS_TAILWIND4_ENTRY = "@axis-ds/tokens/css/tailwind4";
2230
+ var TOKENS_TAILWIND4_IMPORT_LINE = `@import "${TOKENS_TAILWIND4_ENTRY}";`;
2231
+ var TOKENS_TAILWIND4_IMPORT = /@import\s+(?:url\(\s*)?["']@axis-ds\/tokens\/css\/tailwind4["']/;
2232
+ function hasTokensTailwind4Import(css) {
2233
+ return TOKENS_TAILWIND4_IMPORT.test(stripCssComments(css));
2234
+ }
2235
+ var TOKENS_CSS_IMPORT = /@import\s+(?:url\(\s*)?["'](@axis-ds\/tokens\/css(?:\/shadcn|\/tailwind4)?)["']/;
2236
+ function findTokensCssImport(css) {
2237
+ return TOKENS_CSS_IMPORT.exec(stripCssComments(css))?.[1];
2238
+ }
2239
+ function checkGlobalsCss(baseDirInput) {
2240
+ const baseDir = resolve2(baseDirInput);
2241
+ for (const candidate of TAILWIND_CSS_CANDIDATES) {
2242
+ const file = join2(baseDir, candidate);
2243
+ if (!existsSync2(file)) continue;
2244
+ const content = readFileSync2(file, "utf8");
2245
+ if (content.includes("--background") && content.includes("--primary")) {
2246
+ return { name: "globals.css", severity: "pass", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC815\uC758\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4` };
2247
+ }
2248
+ const imported = findTokensCssImport(content);
2249
+ if (imported !== void 0) {
2250
+ return { name: "globals.css", severity: "pass", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${imported} import)` };
2251
+ }
2252
+ return { name: "globals.css", severity: "warn", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4` };
2253
+ }
2254
+ return { name: "globals.css", severity: "fail", message: "globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" };
2255
+ }
2256
+ var TAILWIND4_MIN_AXIS_VERSION = "1.4.0";
2257
+ var TAILWIND4_AXIS_PACKAGES = ["@axis-ds/tokens", "@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2258
+ function lowerOf(token) {
2259
+ const v = versionParts(token);
2260
+ if (!v) return void 0;
2261
+ const num = (part) => isWild(part) ? 0 : Number(part);
2262
+ const pre = /^v?\d+(?:\.(?:\d+|[xX*]))?(?:\.(?:\d+|[xX*]))?-/.test(token);
2263
+ return { core: [v.major, num(v.minor), num(v.patch)], pre };
2264
+ }
2265
+ function compareLower(a, b) {
2266
+ for (let i = 0; i < 3; i++) {
2267
+ if (a.core[i] !== b.core[i]) return a.core[i] - b.core[i];
2268
+ }
2269
+ return Number(b.pre) - Number(a.pre);
2270
+ }
2271
+ function rangeLowerVersion(range) {
2272
+ const r = range.trim();
2273
+ if (r === "" || r.includes("||")) return void 0;
2274
+ const simple = new RegExp(`^(?:\\^|~|=)?\\s*(${VERSION})$`).exec(r);
2275
+ if (simple) return lowerOf(simple[1]);
2276
+ if (new RegExp(`^(${VERSION})\\s+-\\s+(${VERSION})$`).test(r)) {
2277
+ const [a, b] = r.split(/\s+-\s+/);
2278
+ return lowerOf(b) ? lowerOf(a) : void 0;
2279
+ }
2280
+ const tokens = r.replace(/(>=|<=|>|<|=)\s+/g, "$1").split(/\s+/);
2281
+ let lower;
2282
+ for (const t of tokens) {
2283
+ const c = /^(>=|<=|>|<|=)(.+)$/.exec(t);
2284
+ if (!c) return void 0;
2285
+ const v = versionParts(c[2]);
2286
+ const low = lowerOf(c[2]);
2287
+ if (!v || !low) return void 0;
2288
+ let candidate;
2289
+ if (c[1] === ">=" || c[1] === "=") candidate = low;
2290
+ else if (c[1] === ">") {
2291
+ if (isWild(v.minor)) candidate = { core: [v.major + 1, 0, 0], pre: false };
2292
+ else if (isWild(v.patch)) candidate = { core: [v.major, Number(v.minor) + 1, 0], pre: false };
2293
+ else candidate = { core: [v.major, Number(v.minor), Number(v.patch) + 1], pre: false };
2294
+ }
2295
+ if (candidate && (!lower || compareLower(candidate, lower) > 0)) lower = candidate;
2296
+ }
2297
+ return lower;
2298
+ }
2299
+ function rangeLowerAtLeast(range, min) {
2300
+ const lower = rangeLowerVersion(range);
2301
+ const target = lowerOf(min);
2302
+ if (!lower || !target) return void 0;
2303
+ return compareLower(lower, target) >= 0;
2304
+ }
2305
+ function exactVersion(version) {
2306
+ if (typeof version !== "string") return void 0;
2307
+ const v = version.trim();
2308
+ if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(v) || /^v?0\.0\.0-/.test(v)) return void 0;
2309
+ return v;
2310
+ }
2311
+ function installedVersion(baseDir, name) {
2312
+ try {
2313
+ const parsed = JSON.parse(readFileSync2(join2(baseDir, "node_modules", ...name.split("/"), "package.json"), "utf8"));
2314
+ return isPlainObject(parsed) ? exactVersion(parsed.version) : void 0;
2315
+ } catch {
2316
+ return void 0;
2317
+ }
2318
+ }
2319
+ function axisPackagesBelowTailwind4(pkg, baseDirInput) {
2320
+ if (!pkg) return [];
2321
+ const baseDir = baseDirInput !== void 0 ? resolve2(baseDirInput) : void 0;
2322
+ const out = [];
2323
+ const min = lowerOf(TAILWIND4_MIN_AXIS_VERSION);
2324
+ for (const name of TAILWIND4_AXIS_PACKAGES) {
2325
+ const range = hasDependency(pkg, name);
2326
+ if (range === void 0 || range.trim().startsWith("workspace:")) continue;
2327
+ const installed = baseDir !== void 0 ? installedVersion(baseDir, name) : void 0;
2328
+ const installedLower = installed !== void 0 ? lowerOf(installed) : void 0;
2329
+ if (installed !== void 0 && installedLower !== void 0) {
2330
+ if (compareLower(installedLower, min) < 0) out.push({ name, range, status: "below", source: "installed", installed });
2331
+ continue;
2332
+ }
2333
+ const atLeast = rangeLowerAtLeast(range, TAILWIND4_MIN_AXIS_VERSION);
2334
+ if (atLeast === false) out.push({ name, range, status: "below", source: "declared" });
2335
+ else if (atLeast === void 0) out.push({ name, range, status: "unknown", source: "declared" });
2336
+ }
2337
+ return out;
2225
2338
  }
2339
+ function axisPackageVersionLabel(issue) {
2340
+ return issue.source === "installed" ? `${issue.name} \uC124\uCE58\uBCF8 ${issue.installed}` : `${issue.name} ${issue.range}`;
2341
+ }
2342
+ var declaredList = (issues) => issues.map(axisPackageVersionLabel).join(" \xB7 ");
2343
+ function tailwind4VersionNote(issues) {
2344
+ const below = issues.filter((i) => i.status === "below");
2345
+ const unknown = issues.filter((i) => i.status === "unknown");
2346
+ let note = "";
2347
+ if (below.length > 0) note += ` (${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1 \uD544\uC694: ${declaredList(below)})`;
2348
+ if (unknown.length > 0) note += ` (\uC124\uCE58\uB41C \uBC84\uC804\uC774 ${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1\uC778\uC9C0 \uD655\uC778\uD558\uC138\uC694: ${declaredList(unknown)})`;
2349
+ return note;
2350
+ }
2351
+ var TAILWIND4_COMPONENT_PACKAGES = ["@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2352
+ function componentTailwindImportLine(name) {
2353
+ return `@import "${name}/tailwind.css";`;
2354
+ }
2355
+ var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2356
+ function hasComponentTailwindImport(css, name) {
2357
+ const re = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${escapeRegExp(name)}/tailwind\\.css["']`);
2358
+ return re.test(stripCssComments(css));
2359
+ }
2360
+ function hasComponentSource(css, name) {
2361
+ const re = new RegExp(`@source\\s+(["'])(?!@axis-ds/)[^"'\\n]*${escapeRegExp(name)}(?:[/\\\\][^"'\\n]*)?\\1`);
2362
+ return re.test(stripCssComments(css));
2363
+ }
2364
+ var CREATE_NEXT_APP_DEFAULTS = [
2365
+ {
2366
+ prefix: ":root \uC0C9\uAC12",
2367
+ pattern: /(?<![\w-])(--(?:background|foreground))\s*:\s*(?:#[0-9a-fA-F]{3,8}\b|(?:rgba?|hsla?|hwb|oklch|oklab|lab|lch|color)\()/g,
2368
+ item: (m) => m[1]
2369
+ },
2370
+ {
2371
+ prefix: "@theme",
2372
+ pattern: /(?<![\w-])(--color-(?:background|foreground))\s*:\s*var\(\s*(--(?:background|foreground))\s*\)/g,
2373
+ item: (m) => `${m[1]}: var(${m[2]})`
2374
+ },
2375
+ {
2376
+ prefix: "@theme",
2377
+ pattern: /(?<![\w-])(--font-(?:sans|mono))\s*:\s*var\(\s*(--font-geist-(?:sans|mono))\s*\)/g,
2378
+ item: (m) => `${m[1]}: var(${m[2]})`
2379
+ },
2380
+ {
2381
+ prefix: "body",
2382
+ pattern: /(?<![\w-])(background|background-color|color)\s*:\s*var\(\s*(--(?:background|foreground))\s*\)/g,
2383
+ item: (m) => `${m[1]}: var(${m[2]})`
2384
+ }
2385
+ ];
2386
+ function findCreateNextAppDefaults(css) {
2387
+ const body = stripCssComments(css);
2388
+ const labels = [];
2389
+ for (const d of CREATE_NEXT_APP_DEFAULTS) {
2390
+ const items = [];
2391
+ for (const m of body.matchAll(d.pattern)) {
2392
+ const item = d.item(m);
2393
+ if (!items.includes(item)) items.push(item);
2394
+ }
2395
+ if (items.length > 0) labels.push(`${d.prefix} ${items.join(", ")}`);
2396
+ }
2397
+ return labels;
2398
+ }
2399
+ function stripJsComments(src) {
2400
+ let out = "";
2401
+ let i = 0;
2402
+ const n = src.length;
2403
+ while (i < n) {
2404
+ const c = src[i];
2405
+ const d = src[i + 1];
2406
+ if (c === '"' || c === "'" || c === "`") {
2407
+ let j = i + 1;
2408
+ while (j < n && src[j] !== c) {
2409
+ if (src[j] === "\\") j++;
2410
+ else if (c !== "`" && src[j] === "\n") break;
2411
+ j++;
2412
+ }
2413
+ out += src.slice(i, j + 1);
2414
+ i = j + 1;
2415
+ continue;
2416
+ }
2417
+ if (c === "/" && d === "/") {
2418
+ const end = src.indexOf("\n", i);
2419
+ i = end === -1 ? n : end;
2420
+ continue;
2421
+ }
2422
+ if (c === "/" && d === "*") {
2423
+ const end = src.indexOf("*/", i + 2);
2424
+ i = end === -1 ? n : end + 2;
2425
+ out += " ";
2426
+ continue;
2427
+ }
2428
+ out += c;
2429
+ i++;
2430
+ }
2431
+ return out;
2432
+ }
2433
+ var TOKENS_TAILWIND3_PRESET = "@axis-ds/tokens/tailwind";
2434
+ var PRESET_REFERENCE = /(?:\brequire\s*\(\s*|\bimport\s*\(\s*|\bfrom\s*|\bimport\s+)(["'])@axis-ds\/tokens\/tailwind\1/;
2435
+ function hasTokensPresetReference(source) {
2436
+ return PRESET_REFERENCE.test(stripJsComments(source));
2437
+ }
2438
+ var PRESET_PASS_SUFFIX = `\uC5D0 AXIS \uD1A0\uD070\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (${TOKENS_TAILWIND3_PRESET} preset)`;
2226
2439
  function readPackageJson(baseDir) {
2227
2440
  try {
2228
2441
  const parsed = JSON.parse(readFileSync2(join2(baseDir, "package.json"), "utf8"));
@@ -2231,19 +2444,60 @@ function readPackageJson(baseDir) {
2231
2444
  return void 0;
2232
2445
  }
2233
2446
  }
2447
+ function tailwind4Issues(css, pkg, versions) {
2448
+ const issues = [];
2449
+ const hasEntry = hasTokensTailwind4Import(css);
2450
+ if (!hasEntry) issues.push(`AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${TOKENS_TAILWIND4_IMPORT_LINE}`);
2451
+ const missing = TAILWIND4_COMPONENT_PACKAGES.filter(
2452
+ (name) => pkg !== void 0 && hasDependency(pkg, name) !== void 0 && !hasComponentTailwindImport(css, name) && !hasComponentSource(css, name)
2453
+ );
2454
+ if (missing.length > 0) {
2455
+ issues.push(`\uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCE94 \uACBD\uB85C(tailwind.css)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${missing.map(componentTailwindImportLine).join(" ")}`);
2456
+ }
2457
+ if (hasEntry) {
2458
+ const defaults = findCreateNextAppDefaults(css);
2459
+ 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`);
2460
+ if (versions.some((v) => v.name === TAILWIND4_AXIS_PACKAGES[0])) {
2461
+ 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`);
2462
+ }
2463
+ }
2464
+ for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2465
+ if (hasComponentTailwindImport(css, name) && versions.some((v) => v.name === name)) {
2466
+ issues.push(`${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC ${name} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2467
+ }
2468
+ }
2469
+ return issues;
2470
+ }
2234
2471
  function checkTailwindSetup(baseDirInput) {
2235
2472
  const baseDir = resolve2(baseDirInput);
2236
- const tailwind = detectTailwind(baseDir, readPackageJson(baseDir));
2473
+ const pkg = readPackageJson(baseDir);
2474
+ const tailwind = detectTailwind(baseDir, pkg);
2237
2475
  if (tailwind.major !== void 0 && tailwind.major >= 4) {
2238
2476
  const label = `Tailwind ${tailwind.major}`;
2239
2477
  for (const candidate of TAILWIND_CSS_CANDIDATES) {
2240
2478
  const file = join2(baseDir, candidate);
2241
2479
  if (!existsSync2(file)) continue;
2242
- const found = hasTailwindImport(readFileSync2(file, "utf8"));
2480
+ const css = readFileSync2(file, "utf8");
2481
+ if (!hasTailwindImport(css)) {
2482
+ return {
2483
+ name: "tailwind.config",
2484
+ severity: "warn",
2485
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4`
2486
+ };
2487
+ }
2488
+ const versions = axisPackagesBelowTailwind4(pkg, baseDir);
2489
+ const issues = tailwind4Issues(css, pkg, versions);
2490
+ if (issues.length > 0) {
2491
+ return {
2492
+ name: "tailwind.config",
2493
+ severity: "warn",
2494
+ message: `${label}: ${candidate}\uC5D0 ${issues.join(" / ")}${tailwind4VersionNote(versions)}`
2495
+ };
2496
+ }
2243
2497
  return {
2244
2498
  name: "tailwind.config",
2245
- severity: found ? "pass" : "warn",
2246
- message: found ? `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC788\uC2B5\uB2C8\uB2E4` : `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4`
2499
+ severity: "pass",
2500
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uC640 AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC2B5\uB2C8\uB2E4`
2247
2501
  };
2248
2502
  }
2249
2503
  return {
@@ -2257,14 +2511,20 @@ function checkTailwindSetup(baseDirInput) {
2257
2511
  if (existsSync2(tsPath)) {
2258
2512
  const content = readFileSync2(tsPath, "utf8");
2259
2513
  const hasTokens = content.includes("--background") || content.includes("hsl(var(");
2260
- return {
2261
- name: "tailwind.config",
2262
- severity: hasTokens ? "pass" : "warn",
2263
- message: hasTokens ? "tailwind.config.ts\uC5D0 AXIS \uD1A0\uD070\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4" : "tailwind.config.ts\uC5D0 AXIS \uD1A0\uD070 \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4"
2264
- };
2514
+ if (hasTokens) {
2515
+ return { name: "tailwind.config", severity: "pass", message: "tailwind.config.ts\uC5D0 AXIS \uD1A0\uD070\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4" };
2516
+ }
2517
+ if (hasTokensPresetReference(content)) {
2518
+ return { name: "tailwind.config", severity: "pass", message: `tailwind.config.ts${PRESET_PASS_SUFFIX}` };
2519
+ }
2520
+ return { name: "tailwind.config", severity: "warn", message: "tailwind.config.ts\uC5D0 AXIS \uD1A0\uD070 \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" };
2265
2521
  }
2266
2522
  for (const file of otherFiles) {
2267
- if (existsSync2(join2(baseDir, file))) {
2523
+ const path13 = join2(baseDir, file);
2524
+ if (existsSync2(path13)) {
2525
+ if (hasTokensPresetReference(readFileSync2(path13, "utf8"))) {
2526
+ return { name: "tailwind.config", severity: "pass", message: `${file}${PRESET_PASS_SUFFIX}` };
2527
+ }
2268
2528
  return { name: "tailwind.config", severity: "warn", message: `${file} \uC874\uC7AC (TypeScript \uAD8C\uC7A5)` };
2269
2529
  }
2270
2530
  }
@@ -2282,7 +2542,7 @@ async function collectCheckResults(dir) {
2282
2542
  const results = [];
2283
2543
  results.push(await checkFileExists(baseDir, "axis.config.json", "axis.config.json"));
2284
2544
  results.push(checkTailwindSetup(baseDir));
2285
- results.push(await checkGlobalsCss(baseDir));
2545
+ results.push(checkGlobalsCss(baseDir));
2286
2546
  results.push(await checkUtils(baseDir));
2287
2547
  results.push(await checkDependencies(baseDir));
2288
2548
  const brand = checkBrand(baseDir);
@@ -2330,32 +2590,6 @@ async function checkFileExists(baseDir, fileName, label) {
2330
2590
  message: exists ? `${label} \uD30C\uC77C\uC774 \uC874\uC7AC\uD569\uB2C8\uB2E4` : `${label} \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 'axis init'\uC744 \uC2E4\uD589\uD558\uC138\uC694`
2331
2591
  };
2332
2592
  }
2333
- async function checkGlobalsCss(baseDir) {
2334
- const candidates = [
2335
- "globals.css",
2336
- "app/globals.css",
2337
- "src/app/globals.css",
2338
- "src/globals.css",
2339
- "styles/globals.css"
2340
- ];
2341
- for (const candidate of candidates) {
2342
- const fullPath = path8.join(baseDir, candidate);
2343
- if (await fs9.pathExists(fullPath)) {
2344
- const content = await fs9.readFile(fullPath, "utf-8");
2345
- const hasTokens = content.includes("--background") && content.includes("--primary");
2346
- return {
2347
- name: "globals.css",
2348
- severity: hasTokens ? "pass" : "warn",
2349
- message: hasTokens ? `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC815\uC758\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4` : `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4`
2350
- };
2351
- }
2352
- }
2353
- return {
2354
- name: "globals.css",
2355
- severity: "fail",
2356
- message: "globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4"
2357
- };
2358
- }
2359
2593
  async function checkUtils(baseDir) {
2360
2594
  const candidates = [
2361
2595
  "lib/utils.ts",
@@ -3189,6 +3423,70 @@ var INIT_DEFAULTS = {
3189
3423
  globalCss: "./src/app/globals.css"
3190
3424
  };
3191
3425
  var INIT_NEXT_STEP_COMMAND = "npx @axis-ds/cli add button";
3426
+ function isTailwind4(tailwind) {
3427
+ return tailwind.major !== void 0 && tailwind.major >= 4;
3428
+ }
3429
+ var TAILWIND4_TOKENS_VERSION = TAILWIND4_MIN_AXIS_VERSION;
3430
+ var TAILWIND4_CONFLICT_LINE = " ! create-next-app \uAE30\uBCF8 globals.css \uC758 :root \xB7 @theme inline \xB7 @media (prefers-color-scheme: dark) \xB7 body \uBE14\uB85D\uC740 AXIS \uC640 \uAC19\uC740 \uC774\uB984(--background \xB7 --foreground \xB7 --color-background \xB7 --font-sans \xB7 --font-mono)\uC744 \uB2E4\uB978 \uAC12\uC73C\uB85C \uC815\uC758\uD569\uB2C8\uB2E4. \uC774 \uBE14\uB85D\uB4E4\uC744 \uC9C0\uC6B0\uC138\uC694.";
3431
+ function readPackageJsonSafe(baseDir) {
3432
+ try {
3433
+ const text = readFileSync4(path11.join(baseDir, "package.json"), "utf8");
3434
+ const parsed = JSON.parse(text.startsWith(BOM) ? text.slice(1) : text);
3435
+ return isPlainObject2(parsed) ? parsed : void 0;
3436
+ } catch {
3437
+ return void 0;
3438
+ }
3439
+ }
3440
+ function tailwind4GuideLines(pkg, installCommand, globalCss, baseDir) {
3441
+ const used = TAILWIND4_COMPONENT_PACKAGES.filter((name) => pkg !== void 0 && hasDependency2(pkg, name) !== void 0);
3442
+ const lines = [
3443
+ `Tailwind 4: \uC804\uC5ED CSS(${globalCss}) \uB9E8 \uC704\uC5D0 \uB2E4\uC74C \uC904\uC744 \uB123\uC73C\uC138\uC694(\uD30C\uC77C\uC740 \uACE0\uCE58\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4).`,
3444
+ ' @import "tailwindcss";',
3445
+ ` ${TOKENS_TAILWIND4_IMPORT_LINE}`,
3446
+ ...used.map((name) => ` ${componentTailwindImportLine(name)}`)
3447
+ ];
3448
+ if (used.length === 0) {
3449
+ lines.push(
3450
+ ` npm \uD328\uD0A4\uC9C0 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uC4F0\uBA74 \uADF8 \uD328\uD0A4\uC9C0\uC758 tailwind.css \uB3C4 \uB123\uC73C\uC138\uC694(\uC608: @import "${TAILWIND4_COMPONENT_PACKAGES[0]}/tailwind.css";).`
3451
+ );
3452
+ }
3453
+ lines.push(TAILWIND4_CONFLICT_LINE);
3454
+ if (pkg === void 0 || hasDependency2(pkg, TOKENS_PACKAGE) === void 0) {
3455
+ const add = addCommandFor(installCommand, { [TOKENS_PACKAGE]: `^${TAILWIND4_TOKENS_VERSION}` });
3456
+ lines.push(` ${TOKENS_PACKAGE} \uC124\uCE58: ${add}`);
3457
+ }
3458
+ const versions = axisPackagesBelowTailwind4(pkg, baseDir);
3459
+ const unknown = versions.filter((v) => v.status === "unknown");
3460
+ const declaredOf = (list) => list.map(axisPackageVersionLabel).join(" \xB7 ");
3461
+ const upgradeOf = (list) => addCommandFor(installCommand, Object.fromEntries(list.map((b) => [b.name, `^${TAILWIND4_MIN_AXIS_VERSION}`])));
3462
+ const belowDeclared = versions.filter((v) => v.status === "below" && v.source === "declared");
3463
+ const readableRange = (v) => rangeLowerAtLeast(v.range, TAILWIND4_MIN_AXIS_VERSION) !== void 0;
3464
+ const installedBelow = versions.filter((v) => v.status === "below" && v.source === "installed");
3465
+ const belowInstalled = installedBelow.filter(readableRange);
3466
+ const belowInstalledOpaque = installedBelow.filter((v) => !readableRange(v));
3467
+ const installedOf = (list) => list.map((v) => `${v.name} ${v.installed}, \uC120\uC5B8 ${v.range}`).join(" \xB7 ");
3468
+ if (belowDeclared.length > 0) {
3469
+ lines.push(
3470
+ ` ! Tailwind 4 \uC9C4\uC785\uC810\uC740 ${TAILWIND4_MIN_AXIS_VERSION} \uBD80\uD130 \uC788\uC2B5\uB2C8\uB2E4. \uC120\uC5B8(${declaredOf(belowDeclared)})\uC744 \uC62C\uB9AC\uC138\uC694: ${upgradeOf(belowDeclared)}`
3471
+ );
3472
+ }
3473
+ if (belowInstalled.length > 0) {
3474
+ lines.push(
3475
+ ` ! Tailwind 4 \uC9C4\uC785\uC810\uC740 ${TAILWIND4_MIN_AXIS_VERSION} \uBD80\uD130 \uC788\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uBCF8(${installedOf(belowInstalled)})\uC744 \uC62C\uB9AC\uC138\uC694: ${upgradeOf(belowInstalled)}`
3476
+ );
3477
+ }
3478
+ if (belowInstalledOpaque.length > 0) {
3479
+ lines.push(
3480
+ ` ! Tailwind 4 \uC9C4\uC785\uC810\uC740 ${TAILWIND4_MIN_AXIS_VERSION} \uBD80\uD130 \uC788\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uBCF8(${installedOf(belowInstalledOpaque)})\uC774 ${TAILWIND4_MIN_AXIS_VERSION} \uBBF8\uB9CC\uC785\uB2C8\uB2E4. \uC120\uC5B8\uC774 \uAC00\uB9AC\uD0A4\uB294 \uBC84\uC804\uC744 ${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9B0 \uB4A4 \uB2E4\uC2DC \uC124\uCE58\uD558\uC138\uC694(\uC120\uC5B8\uC740 \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4).`
3481
+ );
3482
+ }
3483
+ if (unknown.length > 0) {
3484
+ lines.push(
3485
+ ` ! Tailwind 4 \uC9C4\uC785\uC810\uC740 ${TAILWIND4_MIN_AXIS_VERSION} \uBD80\uD130 \uC788\uC2B5\uB2C8\uB2E4. \uC120\uC5B8(${declaredOf(unknown)})\uC73C\uB85C\uB294 \uBC84\uC804\uC744 \uD310\uC815\uD560 \uC218 \uC5C6\uC73C\uB2C8 \uC124\uCE58\uB41C \uBC84\uC804\uC774 ${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1\uC778\uC9C0 \uD655\uC778\uD558\uC138\uC694.`
3486
+ );
3487
+ }
3488
+ return lines;
3489
+ }
3192
3490
  var BOM = "\uFEFF";
3193
3491
  function addCommandFor(installCommand, deps) {
3194
3492
  const specs = Object.entries(deps).map(([name, range]) => `"${name}@${range}"`).join(" ");
@@ -3318,10 +3616,12 @@ function reportDependencies(plan, log) {
3318
3616
  }
3319
3617
  async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
3320
3618
  const baseDir = path11.resolve(baseDirInput);
3619
+ const plan = planUtilsDependencies(baseDir);
3620
+ const tailwind4 = isTailwind4(plan.tailwind);
3321
3621
  const axisConfig = createInitConfig(
3322
3622
  {
3323
3623
  componentsDir: answers.componentsDir,
3324
- tailwindConfig: answers.tailwindConfig,
3624
+ tailwindConfig: tailwind4 ? void 0 : answers.tailwindConfig,
3325
3625
  globalCss: answers.globalCss
3326
3626
  },
3327
3627
  registryUrl
@@ -3332,7 +3632,6 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
3332
3632
  await fs10.ensureDir(path11.dirname(utilsPath));
3333
3633
  await fs10.writeFile(utilsPath, UTILS_TS_CONTENT);
3334
3634
  log(chalk9.green("\u2713 utils.ts \uC0DD\uC131 \uC644\uB8CC"));
3335
- const plan = planUtilsDependencies(baseDir);
3336
3635
  try {
3337
3636
  applyUtilsDependencies(baseDir, plan);
3338
3637
  } catch (err) {
@@ -3343,6 +3642,17 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
3343
3642
  return;
3344
3643
  }
3345
3644
  reportDependencies(plan, log);
3645
+ if (tailwind4) {
3646
+ const [head, ...rest] = tailwind4GuideLines(
3647
+ readPackageJsonSafe(baseDir),
3648
+ plan.installCommand,
3649
+ answers.globalCss,
3650
+ baseDir
3651
+ );
3652
+ log(chalk9.blue(`
3653
+ ${head}`));
3654
+ for (const line of rest) log(line.startsWith(" !") ? chalk9.yellow(line) : chalk9.cyan(line));
3655
+ }
3346
3656
  log(chalk9.blue("\n\uCD08\uAE30\uD654 \uC644\uB8CC! \uC774\uC81C \uB2E4\uC74C \uBA85\uB839\uC5B4\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD558\uC138\uC694:"));
3347
3657
  log(chalk9.cyan(` ${INIT_NEXT_STEP_COMMAND}
3348
3658
  `));
@@ -3352,6 +3662,7 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
3352
3662
  function registerInitCommand(program2, registryUrl) {
3353
3663
  program2.command("init").description("\uD504\uB85C\uC81D\uD2B8\uC5D0 AXIS Design System \uCD08\uAE30\uD654").option("-y, --yes", "\uAE30\uBCF8\uAC12\uC73C\uB85C \uC790\uB3D9 \uC124\uC815").action(async (options) => {
3354
3664
  console.log(chalk10.blue("\n\u{1F3A8} AXIS Design System \uCD08\uAE30\uD654\n"));
3665
+ const tailwind4 = isTailwind4(planUtilsDependencies(process.cwd()).tailwind);
3355
3666
  const config = options.yes ? { ...INIT_DEFAULTS } : await prompts2([
3356
3667
  {
3357
3668
  type: "text",
@@ -3360,7 +3671,7 @@ function registerInitCommand(program2, registryUrl) {
3360
3671
  initial: INIT_DEFAULTS.componentsDir
3361
3672
  },
3362
3673
  {
3363
- type: "text",
3674
+ type: tailwind4 ? null : "text",
3364
3675
  name: "tailwindConfig",
3365
3676
  message: "Tailwind \uC124\uC815 \uD30C\uC77C:",
3366
3677
  initial: INIT_DEFAULTS.tailwindConfig
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axis-ds/cli",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "description": "AXIS Design System - CLI for adding components (shadcn compatible)",
6
6
  "publishConfig": {