@axis-ds/cli 1.5.4 → 1.6.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.
package/dist/index.js CHANGED
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-ZVVACWEW.js";
5
5
  import {
6
6
  V0Client
7
- } from "./chunk-AHQT77FJ.js";
7
+ } from "./chunk-NNFSJISD.js";
8
8
  import {
9
9
  registerSyncCommand
10
- } from "./chunk-QORBOSUA.js";
10
+ } from "./chunk-7CDXEBEE.js";
11
11
  import {
12
12
  AXIS_QUICK_START_URL,
13
13
  AXIS_SITE_URL,
@@ -411,7 +411,7 @@ var ShadcnCollector = class extends BaseCollector {
411
411
  ];
412
412
  }
413
413
  return meta;
414
- } catch (error) {
414
+ } catch {
415
415
  return this.createBaseMeta(name, `shadcn/ui ${name} component`, [], {
416
416
  registry: "@shadcn",
417
417
  url: `${this.baseUrl}/docs/components/${name}`
@@ -546,7 +546,8 @@ var MonetCollector = class extends BaseCollector {
546
546
  sourceType = "monet";
547
547
  baseUrl = "https://www.monet.design";
548
548
  registryUrl = "https://registry.monet.design";
549
- constructor(options = {}) {
549
+ // 예전 판은 쓰지 않는 `options: { apiKey?: string }` 인자를 받았어요. 호출부는 인자 없이 만들고 있어 뺐습니다(WI-0039)
550
+ constructor() {
550
551
  super();
551
552
  }
552
553
  /**
@@ -584,7 +585,7 @@ var MonetCollector = class extends BaseCollector {
584
585
  const name = id.replace("monet-", "");
585
586
  const category = MONET_CATEGORIES.find((c) => c.id === name);
586
587
  let example;
587
- for (const [catId, examples] of Object.entries(MONET_EXAMPLES)) {
588
+ for (const [, examples] of Object.entries(MONET_EXAMPLES)) {
588
589
  const found = examples.find((e) => e.id === name);
589
590
  if (found) {
590
591
  example = found;
@@ -690,7 +691,6 @@ var V0Collector = class extends BaseCollector {
690
691
  const content = await fs.readFile(file, "utf-8");
691
692
  if (this.isV0Code(content)) {
692
693
  const name = path.basename(file, path.extname(file));
693
- const relativePath = path.relative(this.v0Directory, file);
694
694
  items.push({
695
695
  id: `v0-${this.generateSlug(name)}`,
696
696
  name: this.formatName(name),
@@ -1283,7 +1283,8 @@ async function readJson(file) {
1283
1283
  return JSON.parse(await fs4.readFile(file, "utf-8"));
1284
1284
  }
1285
1285
  function withoutUpdatedAt(c) {
1286
- const { updatedAt: _ignored, ...rest } = c;
1286
+ const rest = { ...c };
1287
+ delete rest.updatedAt;
1287
1288
  return JSON.parse(serialize(rest));
1288
1289
  }
1289
1290
  function computeStats(entries) {
@@ -1601,7 +1602,7 @@ var LibraryCurator = class {
1601
1602
  */
1602
1603
  async generateIndex() {
1603
1604
  const components = [];
1604
- for (const [source, collector] of this.collectors) {
1605
+ for (const [, collector] of this.collectors) {
1605
1606
  try {
1606
1607
  const list = await collector.listComponents();
1607
1608
  for (const item of list) {
@@ -1832,7 +1833,7 @@ function createTemplateConfig(template) {
1832
1833
  template,
1833
1834
  componentsDir: "./components/ui",
1834
1835
  tailwindConfig: "./tailwind.config.ts",
1835
- globalCss: "./globals.css"
1836
+ globalCss: "./app/globals.css"
1836
1837
  };
1837
1838
  }
1838
1839
  async function readAxisConfig(baseDir) {
@@ -1841,142 +1842,807 @@ async function readAxisConfig(baseDir) {
1841
1842
  return await fs7.readJSON(configPath);
1842
1843
  }
1843
1844
 
1844
- // src/template/template-apply.ts
1845
- async function applyTemplate(slug, options) {
1846
- const spinner = ora3(`'${slug}' \uD15C\uD50C\uB9BF \uAC00\uC838\uC624\uB294 \uC911...`).start();
1847
- let detail;
1848
- try {
1849
- detail = await fetchTemplateDetail(slug);
1850
- spinner.succeed(`'${detail.name}' \uD15C\uD50C\uB9BF \uB85C\uB4DC \uC644\uB8CC (${detail.files.length}\uAC1C \uD30C\uC77C)`);
1851
- } catch (err) {
1852
- spinner.fail(err.message);
1853
- process.exit(1);
1854
- }
1855
- const baseDir = path8.resolve(options.dir || ".");
1856
- const conflicts = [];
1857
- for (const file of detail.files) {
1858
- const dest = path8.join(baseDir, file.path);
1859
- if (await fs8.pathExists(dest)) {
1860
- conflicts.push(file.path);
1845
+ // ../axis-registry-core/src/template/tailwind-core.ts
1846
+ import { existsSync, lstatSync, readdirSync, readFileSync } from "fs";
1847
+ import { join, resolve } from "path";
1848
+ function hasDependency(pkg, name) {
1849
+ for (const field of ["dependencies", "devDependencies"]) {
1850
+ const deps = pkg[field];
1851
+ if (typeof deps === "object" && deps !== null && name in deps) {
1852
+ return String(deps[name]);
1861
1853
  }
1862
1854
  }
1863
- if (conflicts.length > 0 && !options.yes && !options.dryRun) {
1864
- console.log(chalk5.yellow(`
1865
- \uAE30\uC874 \uD30C\uC77C\uACFC \uCDA9\uB3CC\uD558\uB294 ${conflicts.length}\uAC1C \uD30C\uC77C:`));
1866
- for (const c of conflicts) {
1867
- console.log(chalk5.gray(` - ${c}`));
1868
- }
1869
- const { proceed } = await prompts({
1870
- type: "confirm",
1871
- name: "proceed",
1872
- message: "\uCDA9\uB3CC \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
1873
- initial: false
1874
- });
1875
- if (!proceed) {
1876
- console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
1877
- return;
1855
+ return void 0;
1856
+ }
1857
+ function isPlainObject(value) {
1858
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1859
+ }
1860
+ var VERSION = String.raw`v?(\d+)(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?`;
1861
+ function versionParts(token) {
1862
+ const m = new RegExp(`^${VERSION}$`).exec(token);
1863
+ if (!m) return void 0;
1864
+ if (/^v?0\.0\.0-/.test(token)) return void 0;
1865
+ return { major: Number(m[1]), minor: m[2], patch: m[3] };
1866
+ }
1867
+ var isWild = (part) => part === void 0 || /^[xX*]$/.test(part);
1868
+ function comparatorBounds(op, v) {
1869
+ switch (op) {
1870
+ case ">=":
1871
+ return { lower: v.major };
1872
+ case ">":
1873
+ return { lower: isWild(v.minor) ? v.major + 1 : v.major };
1874
+ case "<=":
1875
+ return { upper: v.major };
1876
+ case "<": {
1877
+ const zeroOrWild = (part) => isWild(part) || Number(part) === 0;
1878
+ return { upper: zeroOrWild(v.minor) && zeroOrWild(v.patch) ? v.major - 1 : v.major };
1878
1879
  }
1880
+ default:
1881
+ return { lower: v.major, upper: v.major };
1879
1882
  }
1880
- if (options.dryRun) {
1881
- console.log(chalk5.blue("\n[\uBBF8\uB9AC\uBCF4\uAE30] \uC0DD\uC131\uB420 \uD30C\uC77C:"));
1882
- for (const file of detail.files) {
1883
- const dest = path8.join(baseDir, file.path);
1884
- const exists = conflicts.includes(file.path);
1885
- const label = exists ? chalk5.yellow("\uB36E\uC5B4\uC4F0\uAE30") : chalk5.green("\uC0DD\uC131");
1886
- console.log(` ${label} ${dest}`);
1887
- }
1888
- if (detail.postInstall?.length) {
1889
- console.log(chalk5.blue("\n[\uBBF8\uB9AC\uBCF4\uAE30] postInstall \uD328\uCE58:"));
1890
- for (const p of detail.postInstall) {
1891
- console.log(chalk5.gray(` ${p.type}: ${p.file}`));
1892
- }
1893
- }
1894
- return;
1883
+ }
1884
+ function tailwindMajorBounds(range) {
1885
+ const r = range.trim();
1886
+ if (r === "" || r.includes("||")) return void 0;
1887
+ const simple = new RegExp(`^(?:\\^|~|=)?\\s*(${VERSION})$`).exec(r);
1888
+ if (simple) {
1889
+ const v = versionParts(simple[1]);
1890
+ return v ? { lower: v.major, upper: v.major } : void 0;
1895
1891
  }
1896
- const writeSpinner = ora3("\uD30C\uC77C \uC0DD\uC131 \uC911...").start();
1897
- for (const file of detail.files) {
1898
- const dest = path8.join(baseDir, file.path);
1899
- await fs8.ensureDir(path8.dirname(dest));
1900
- await fs8.writeFile(dest, file.content);
1892
+ const hyphen = new RegExp(`^(${VERSION})\\s+-\\s+(${VERSION})$`).exec(r);
1893
+ if (hyphen) {
1894
+ const tokens2 = r.split(/\s+-\s+/);
1895
+ const a = versionParts(tokens2[0]);
1896
+ const b = versionParts(tokens2[1]);
1897
+ return a && b && a.major <= b.major ? { lower: a.major, upper: b.major } : void 0;
1901
1898
  }
1902
- writeSpinner.succeed(`${detail.files.length}\uAC1C \uD30C\uC77C \uC0DD\uC131 \uC644\uB8CC`);
1903
- if (detail.postInstall?.length) {
1904
- console.log(chalk5.blue("\npostInstall \uD328\uCE58 \uC2E4\uD589 \uC911..."));
1905
- await runPostInstall(detail.postInstall, baseDir);
1899
+ const tokens = r.replace(/(>=|<=|>|<|=)\s+/g, "$1").split(/\s+/);
1900
+ const lowers = [];
1901
+ const uppers = [];
1902
+ for (const t of tokens) {
1903
+ const c = /^(>=|<=|>|<|=)(.+)$/.exec(t);
1904
+ if (!c) return void 0;
1905
+ const v = versionParts(c[2]);
1906
+ if (v === void 0) return void 0;
1907
+ const b = comparatorBounds(c[1], v);
1908
+ if (b.lower !== void 0) lowers.push(b.lower);
1909
+ if (b.upper !== void 0) uppers.push(b.upper);
1906
1910
  }
1907
- if (!options.skipDeps) {
1908
- const allDeps = { ...detail.dependencies, ...detail.devDependencies };
1909
- const depList = Object.entries(allDeps);
1910
- if (depList.length > 0) {
1911
- console.log(chalk5.yellow("\n\uD544\uC694\uD55C \uC758\uC874\uC131:"));
1912
- const deps = Object.entries(detail.dependencies || {}).map(([k, v]) => `${k}@${v}`).join(" ");
1913
- const devDeps = Object.entries(detail.devDependencies || {}).map(([k, v]) => `${k}@${v}`).join(" ");
1914
- if (deps) {
1915
- console.log(chalk5.cyan(` pnpm add ${deps}`));
1916
- }
1917
- if (devDeps) {
1918
- console.log(chalk5.cyan(` pnpm add -D ${devDeps}`));
1911
+ if (lowers.length === 0) return void 0;
1912
+ const lower = Math.max(...lowers);
1913
+ const upper = uppers.length > 0 ? Math.min(...uppers) : void 0;
1914
+ if (upper !== void 0 && upper < lower) return void 0;
1915
+ return upper !== void 0 ? { lower, upper } : { lower };
1916
+ }
1917
+ function tailwindMajorFromRange(range) {
1918
+ return tailwindMajorBounds(range)?.lower;
1919
+ }
1920
+ function majorWithin(major, bounds) {
1921
+ return major >= bounds.lower && (bounds.upper === void 0 || major <= bounds.upper);
1922
+ }
1923
+ function majorFromVersion(version) {
1924
+ if (typeof version !== "string") return void 0;
1925
+ const v = version.trim();
1926
+ const m = /^v?(\d+)\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]*)?$/.exec(v);
1927
+ if (!m || /^v?0\.0\.0-/.test(v)) return void 0;
1928
+ return Number(m[1]);
1929
+ }
1930
+ function detectTailwind(baseDirInput, pkg) {
1931
+ const baseDir = resolve(baseDirInput);
1932
+ const installedFile = join(baseDir, "node_modules", "tailwindcss", "package.json");
1933
+ const declared = pkg ? hasDependency(pkg, "tailwindcss") : void 0;
1934
+ const bounds = declared !== void 0 ? tailwindMajorBounds(declared) : void 0;
1935
+ const declaredMajor = bounds?.lower;
1936
+ try {
1937
+ const installed = JSON.parse(readFileSync(installedFile, "utf8"));
1938
+ const version = isPlainObject(installed) ? installed.version : void 0;
1939
+ const major = majorFromVersion(version);
1940
+ if (major !== void 0) {
1941
+ const found = { major, source: "installed", value: String(version) };
1942
+ if (declared !== void 0 && bounds !== void 0 && !majorWithin(major, bounds)) {
1943
+ found.mismatch = { declared, declaredMajor: bounds.lower };
1919
1944
  }
1945
+ return found;
1920
1946
  }
1947
+ } catch {
1921
1948
  }
1922
- console.log(chalk5.green(`
1923
- '${detail.name}' \uD15C\uD50C\uB9BF \uC801\uC6A9 \uC644\uB8CC!`));
1924
- }
1925
- async function initTemplate(slug, options) {
1926
- let selectedSlug = slug;
1927
- if (!selectedSlug) {
1928
- const { fetchTemplateIndex: fetchTemplateIndex2 } = await import("./registry-client-6WSGKPF2.js");
1929
- const index = await fetchTemplateIndex2();
1930
- const { choice } = await prompts({
1931
- type: "select",
1932
- name: "choice",
1933
- message: "\uC0AC\uC6A9\uD560 \uD15C\uD50C\uB9BF\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
1934
- choices: index.templates.map((t) => ({
1935
- title: `${t.name} (${t.category})`,
1936
- description: t.description,
1937
- value: t.slug
1938
- }))
1939
- });
1940
- if (!choice) {
1941
- console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
1942
- return;
1943
- }
1944
- selectedSlug = choice;
1949
+ if (declared !== void 0) {
1950
+ if (declaredMajor !== void 0) return { major: declaredMajor, source: "declared", value: declared };
1951
+ return { source: "none", value: declared };
1945
1952
  }
1946
- let targetDir = options.dir;
1947
- if (!targetDir) {
1948
- const { dir } = await prompts({
1949
- type: "text",
1950
- name: "dir",
1951
- message: "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC:",
1952
- initial: selectedSlug
1953
- });
1954
- if (!dir) {
1955
- console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
1956
- return;
1953
+ return { source: "none" };
1954
+ }
1955
+ var TAILWIND_CONFIG_FILES = [
1956
+ "tailwind.config.ts",
1957
+ "tailwind.config.js",
1958
+ "tailwind.config.mjs",
1959
+ "tailwind.config.cjs"
1960
+ ];
1961
+ var TAILWIND_CSS_CANDIDATES = [
1962
+ "globals.css",
1963
+ "app/globals.css",
1964
+ "src/app/globals.css",
1965
+ "src/globals.css",
1966
+ "styles/globals.css"
1967
+ ];
1968
+ var TAILWIND_IMPORT = /@import\s+(?:url\(\s*)?["']tailwindcss["']/;
1969
+ function stripCssComments(css) {
1970
+ return css.replace(/\/\*[\s\S]*?\*\//g, "");
1971
+ }
1972
+ function hasTailwindImport(css) {
1973
+ return TAILWIND_IMPORT.test(stripCssComments(css));
1974
+ }
1975
+ var TOKENS_TAILWIND4_ENTRY = "@axis-ds/tokens/css/tailwind4";
1976
+ var TOKENS_TAILWIND4_IMPORT_LINE = `@import "${TOKENS_TAILWIND4_ENTRY}";`;
1977
+ var TOKENS_TAILWIND4_IMPORT = /@import\s+(?:url\(\s*)?["']@axis-ds\/tokens\/css\/tailwind4["']/;
1978
+ function hasTokensTailwind4Import(css) {
1979
+ return TOKENS_TAILWIND4_IMPORT.test(stripCssComments(css));
1980
+ }
1981
+ var TOKENS_CSS_IMPORT = /@import\s+(?:url\(\s*)?["'](@axis-ds\/tokens\/css(?:\/shadcn|\/tailwind4)?)["']/;
1982
+ function findTokensCssImport(css) {
1983
+ return TOKENS_CSS_IMPORT.exec(stripCssComments(css))?.[1];
1984
+ }
1985
+ function checkGlobalsCss(baseDirInput) {
1986
+ const baseDir = resolve(baseDirInput);
1987
+ for (const candidate of TAILWIND_CSS_CANDIDATES) {
1988
+ const file = join(baseDir, candidate);
1989
+ if (!existsSync(file)) continue;
1990
+ const content = readFileSync(file, "utf8");
1991
+ if (content.includes("--background") && content.includes("--primary")) {
1992
+ return { name: "globals.css", severity: "pass", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC815\uC758\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4` };
1957
1993
  }
1958
- targetDir = dir;
1959
- }
1960
- const resolved = path8.resolve(targetDir);
1961
- if (await fs8.pathExists(resolved)) {
1962
- const entries = await fs8.readdir(resolved);
1963
- if (entries.length > 0) {
1964
- const { proceed } = await prompts({
1965
- type: "confirm",
1966
- name: "proceed",
1967
- message: `'${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?`,
1968
- initial: false
1969
- });
1970
- if (!proceed) return;
1994
+ const imported = findTokensCssImport(content);
1995
+ if (imported !== void 0) {
1996
+ return { name: "globals.css", severity: "pass", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${imported} import)` };
1971
1997
  }
1972
- } else {
1973
- await fs8.ensureDir(resolved);
1998
+ return { name: "globals.css", severity: "warn", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4` };
1974
1999
  }
1975
- await applyTemplate(selectedSlug, { dir: targetDir, yes: true });
1976
- const configPath = path8.join(resolved, "axis.config.json");
1977
- if (!await fs8.pathExists(configPath)) {
1978
- const axisConfig = createTemplateConfig(selectedSlug);
1979
- await fs8.writeJSON(configPath, axisConfig, { spaces: 2 });
2000
+ return { name: "globals.css", severity: "fail", message: "globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" };
2001
+ }
2002
+ var TAILWIND4_MIN_AXIS_VERSION = "1.4.0";
2003
+ var TAILWIND4_AXIS_PACKAGES = ["@axis-ds/tokens", "@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2004
+ function lowerOf(token) {
2005
+ const v = versionParts(token);
2006
+ if (!v) return void 0;
2007
+ const num = (part) => isWild(part) ? 0 : Number(part);
2008
+ const pre = /^v?\d+(?:\.(?:\d+|[xX*]))?(?:\.(?:\d+|[xX*]))?-/.test(token);
2009
+ return { core: [v.major, num(v.minor), num(v.patch)], pre };
2010
+ }
2011
+ function compareLower(a, b) {
2012
+ for (let i = 0; i < 3; i++) {
2013
+ if (a.core[i] !== b.core[i]) return a.core[i] - b.core[i];
2014
+ }
2015
+ return Number(b.pre) - Number(a.pre);
2016
+ }
2017
+ function rangeLowerVersion(range) {
2018
+ const r = range.trim();
2019
+ if (r === "" || r.includes("||")) return void 0;
2020
+ const simple = new RegExp(`^(?:\\^|~|=)?\\s*(${VERSION})$`).exec(r);
2021
+ if (simple) return lowerOf(simple[1]);
2022
+ if (new RegExp(`^(${VERSION})\\s+-\\s+(${VERSION})$`).test(r)) {
2023
+ const [a, b] = r.split(/\s+-\s+/);
2024
+ return lowerOf(b) ? lowerOf(a) : void 0;
2025
+ }
2026
+ const tokens = r.replace(/(>=|<=|>|<|=)\s+/g, "$1").split(/\s+/);
2027
+ let lower;
2028
+ for (const t of tokens) {
2029
+ const c = /^(>=|<=|>|<|=)(.+)$/.exec(t);
2030
+ if (!c) return void 0;
2031
+ const v = versionParts(c[2]);
2032
+ const low = lowerOf(c[2]);
2033
+ if (!v || !low) return void 0;
2034
+ let candidate;
2035
+ if (c[1] === ">=" || c[1] === "=") candidate = low;
2036
+ else if (c[1] === ">") {
2037
+ if (isWild(v.minor)) candidate = { core: [v.major + 1, 0, 0], pre: false };
2038
+ else if (isWild(v.patch)) candidate = { core: [v.major, Number(v.minor) + 1, 0], pre: false };
2039
+ else candidate = { core: [v.major, Number(v.minor), Number(v.patch) + 1], pre: false };
2040
+ }
2041
+ if (candidate && (!lower || compareLower(candidate, lower) > 0)) lower = candidate;
2042
+ }
2043
+ return lower;
2044
+ }
2045
+ function rangeLowerAtLeast(range, min) {
2046
+ const lower = rangeLowerVersion(range);
2047
+ const target = lowerOf(min);
2048
+ if (!lower || !target) return void 0;
2049
+ return compareLower(lower, target) >= 0;
2050
+ }
2051
+ function exactVersion(version) {
2052
+ if (typeof version !== "string") return void 0;
2053
+ const v = version.trim();
2054
+ if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(v) || /^v?0\.0\.0-/.test(v)) return void 0;
2055
+ return v;
2056
+ }
2057
+ function installedVersion(baseDir, name) {
2058
+ try {
2059
+ const parsed = JSON.parse(readFileSync(join(baseDir, "node_modules", ...name.split("/"), "package.json"), "utf8"));
2060
+ return isPlainObject(parsed) ? exactVersion(parsed.version) : void 0;
2061
+ } catch {
2062
+ return void 0;
2063
+ }
2064
+ }
2065
+ function axisPackagesBelowTailwind4(pkg, baseDirInput) {
2066
+ if (!pkg) return [];
2067
+ const baseDir = baseDirInput !== void 0 ? resolve(baseDirInput) : void 0;
2068
+ const out = [];
2069
+ const min = lowerOf(TAILWIND4_MIN_AXIS_VERSION);
2070
+ for (const name of TAILWIND4_AXIS_PACKAGES) {
2071
+ const range = hasDependency(pkg, name);
2072
+ if (range === void 0 || range.trim().startsWith("workspace:")) continue;
2073
+ const installed = baseDir !== void 0 ? installedVersion(baseDir, name) : void 0;
2074
+ const installedLower = installed !== void 0 ? lowerOf(installed) : void 0;
2075
+ if (installed !== void 0 && installedLower !== void 0) {
2076
+ if (compareLower(installedLower, min) < 0) out.push({ name, range, status: "below", source: "installed", installed });
2077
+ continue;
2078
+ }
2079
+ const atLeast = rangeLowerAtLeast(range, TAILWIND4_MIN_AXIS_VERSION);
2080
+ if (atLeast === false) out.push({ name, range, status: "below", source: "declared" });
2081
+ else if (atLeast === void 0) out.push({ name, range, status: "unknown", source: "declared" });
2082
+ }
2083
+ return out;
2084
+ }
2085
+ function axisPackageVersionLabel(issue) {
2086
+ return issue.source === "installed" ? `${issue.name} \uC124\uCE58\uBCF8 ${issue.installed}` : `${issue.name} ${issue.range}`;
2087
+ }
2088
+ var declaredList = (issues) => issues.map(axisPackageVersionLabel).join(" \xB7 ");
2089
+ function tailwind4VersionNote(issues) {
2090
+ const below = issues.filter((i) => i.status === "below");
2091
+ const unknown = issues.filter((i) => i.status === "unknown");
2092
+ let note = "";
2093
+ if (below.length > 0) note += ` (${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1 \uD544\uC694: ${declaredList(below)})`;
2094
+ 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)})`;
2095
+ return note;
2096
+ }
2097
+ var TAILWIND4_COMPONENT_PACKAGES = ["@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2098
+ function componentTailwindImportLine(name) {
2099
+ return `@import "${name}/tailwind.css";`;
2100
+ }
2101
+ var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2102
+ function hasComponentTailwindImport(css, name) {
2103
+ const re = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${escapeRegExp(name)}/tailwind\\.css["']`);
2104
+ return re.test(stripCssComments(css));
2105
+ }
2106
+ function hasComponentSource(css, name) {
2107
+ const re = new RegExp(`@source\\s+(["'])(?!@axis-ds/)[^"'\\n]*${escapeRegExp(name)}(?:[/\\\\][^"'\\n]*)?\\1`);
2108
+ return re.test(stripCssComments(css));
2109
+ }
2110
+ var CREATE_NEXT_APP_DEFAULTS = [
2111
+ {
2112
+ prefix: ":root \uC0C9\uAC12",
2113
+ pattern: /(?<![\w-])(--(?:background|foreground))\s*:\s*(?:#[0-9a-fA-F]{3,8}\b|(?:rgba?|hsla?|hwb|oklch|oklab|lab|lch|color)\()/g,
2114
+ item: (m) => m[1]
2115
+ },
2116
+ {
2117
+ prefix: "@theme",
2118
+ pattern: /(?<![\w-])(--color-(?:background|foreground))\s*:\s*var\(\s*(--(?:background|foreground))\s*\)/g,
2119
+ item: (m) => `${m[1]}: var(${m[2]})`
2120
+ },
2121
+ {
2122
+ prefix: "@theme",
2123
+ pattern: /(?<![\w-])(--font-(?:sans|mono))\s*:\s*var\(\s*(--font-geist-(?:sans|mono))\s*\)/g,
2124
+ item: (m) => `${m[1]}: var(${m[2]})`
2125
+ },
2126
+ {
2127
+ prefix: "body",
2128
+ pattern: /(?<![\w-])(background|background-color|color)\s*:\s*var\(\s*(--(?:background|foreground))\s*\)/g,
2129
+ item: (m) => `${m[1]}: var(${m[2]})`
2130
+ }
2131
+ ];
2132
+ function findCreateNextAppDefaults(css) {
2133
+ const body = stripCssComments(css);
2134
+ const labels = [];
2135
+ for (const d of CREATE_NEXT_APP_DEFAULTS) {
2136
+ const items = [];
2137
+ for (const m of body.matchAll(d.pattern)) {
2138
+ const item = d.item(m);
2139
+ if (!items.includes(item)) items.push(item);
2140
+ }
2141
+ if (items.length > 0) labels.push(`${d.prefix} ${items.join(", ")}`);
2142
+ }
2143
+ return labels;
2144
+ }
2145
+ function stripJsComments(src) {
2146
+ let out = "";
2147
+ let i = 0;
2148
+ const n = src.length;
2149
+ while (i < n) {
2150
+ const c = src[i];
2151
+ const d = src[i + 1];
2152
+ if (c === '"' || c === "'" || c === "`") {
2153
+ let j = i + 1;
2154
+ while (j < n && src[j] !== c) {
2155
+ if (src[j] === "\\") j++;
2156
+ else if (c !== "`" && src[j] === "\n") break;
2157
+ j++;
2158
+ }
2159
+ out += src.slice(i, j + 1);
2160
+ i = j + 1;
2161
+ continue;
2162
+ }
2163
+ if (c === "/" && d === "/") {
2164
+ const end = src.indexOf("\n", i);
2165
+ i = end === -1 ? n : end;
2166
+ continue;
2167
+ }
2168
+ if (c === "/" && d === "*") {
2169
+ const end = src.indexOf("*/", i + 2);
2170
+ i = end === -1 ? n : end + 2;
2171
+ out += " ";
2172
+ continue;
2173
+ }
2174
+ out += c;
2175
+ i++;
2176
+ }
2177
+ return out;
2178
+ }
2179
+ var TOKENS_TAILWIND3_PRESET = "@axis-ds/tokens/tailwind";
2180
+ var PRESET_REFERENCE = /(?:\brequire\s*\(\s*|\bimport\s*\(\s*|\bfrom\s*|\bimport\s+)(["'])@axis-ds\/tokens\/tailwind\1/;
2181
+ function hasTokensPresetReference(source) {
2182
+ return PRESET_REFERENCE.test(stripJsComments(source));
2183
+ }
2184
+ var PRESET_PASS_SUFFIX = `\uC5D0 AXIS \uD1A0\uD070\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (${TOKENS_TAILWIND3_PRESET} preset)`;
2185
+ function readPackageJson(baseDir) {
2186
+ try {
2187
+ const parsed = JSON.parse(readFileSync(join(baseDir, "package.json"), "utf8"));
2188
+ return isPlainObject(parsed) ? parsed : void 0;
2189
+ } catch {
2190
+ return void 0;
2191
+ }
2192
+ }
2193
+ function tailwind4Issues(css, pkg, versions) {
2194
+ const issues = [];
2195
+ const hasEntry = hasTokensTailwind4Import(css);
2196
+ if (!hasEntry) issues.push(`AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${TOKENS_TAILWIND4_IMPORT_LINE}`);
2197
+ const missing = TAILWIND4_COMPONENT_PACKAGES.filter(
2198
+ (name) => pkg !== void 0 && hasDependency(pkg, name) !== void 0 && !hasComponentTailwindImport(css, name) && !hasComponentSource(css, name)
2199
+ );
2200
+ if (missing.length > 0) {
2201
+ issues.push(`\uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCE94 \uACBD\uB85C(tailwind.css)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${missing.map(componentTailwindImportLine).join(" ")}`);
2202
+ }
2203
+ if (hasEntry) {
2204
+ const defaults = findCreateNextAppDefaults(css);
2205
+ 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`);
2206
+ if (versions.some((v) => v.name === TAILWIND4_AXIS_PACKAGES[0])) {
2207
+ 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`);
2208
+ }
2209
+ }
2210
+ for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2211
+ if (hasComponentTailwindImport(css, name) && versions.some((v) => v.name === name)) {
2212
+ issues.push(`${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC ${name} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2213
+ }
2214
+ }
2215
+ return issues;
2216
+ }
2217
+ function checkTailwindSetup(baseDirInput) {
2218
+ const baseDir = resolve(baseDirInput);
2219
+ const pkg = readPackageJson(baseDir);
2220
+ const tailwind = detectTailwind(baseDir, pkg);
2221
+ if (tailwind.major !== void 0 && tailwind.major >= 4) {
2222
+ const label = `Tailwind ${tailwind.major}`;
2223
+ for (const candidate of TAILWIND_CSS_CANDIDATES) {
2224
+ const file = join(baseDir, candidate);
2225
+ if (!existsSync(file)) continue;
2226
+ const css = readFileSync(file, "utf8");
2227
+ if (!hasTailwindImport(css)) {
2228
+ return {
2229
+ name: "tailwind.config",
2230
+ severity: "warn",
2231
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4`
2232
+ };
2233
+ }
2234
+ const versions = axisPackagesBelowTailwind4(pkg, baseDir);
2235
+ const issues = tailwind4Issues(css, pkg, versions);
2236
+ if (issues.length > 0) {
2237
+ return {
2238
+ name: "tailwind.config",
2239
+ severity: "warn",
2240
+ message: `${label}: ${candidate}\uC5D0 ${issues.join(" / ")}${tailwind4VersionNote(versions)}`
2241
+ };
2242
+ }
2243
+ return {
2244
+ name: "tailwind.config",
2245
+ severity: "pass",
2246
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uC640 AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC2B5\uB2C8\uB2E4`
2247
+ };
2248
+ }
2249
+ return {
2250
+ name: "tailwind.config",
2251
+ severity: "warn",
2252
+ message: `${label}: @import "tailwindcss" \uB97C \uD655\uC778\uD560 CSS \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4`
2253
+ };
2254
+ }
2255
+ for (const file of TAILWIND_CONFIG_FILES) {
2256
+ const path18 = join(baseDir, file);
2257
+ if (!existsSync(path18)) continue;
2258
+ const content = readFileSync(path18, "utf8");
2259
+ if (hasTokensPresetReference(content)) {
2260
+ return { name: "tailwind.config", severity: "pass", message: `${file}${PRESET_PASS_SUFFIX}` };
2261
+ }
2262
+ const ownMapping = content.includes("--background") || content.includes("hsl(var(");
2263
+ return {
2264
+ name: "tailwind.config",
2265
+ severity: "warn",
2266
+ message: ownMapping ? `${file}\uC5D0 AXIS preset(${TOKENS_TAILWIND3_PRESET})\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uCCB4 CSS \uBCC0\uC218 \uB9E4\uD551\uB9CC \uC788\uC74C, presets \uC5D0 \uB123\uC73C\uC138\uC694)` : `${file}\uC5D0 AXIS preset(${TOKENS_TAILWIND3_PRESET})\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (presets \uC5D0 \uB123\uC73C\uC138\uC694)`
2267
+ };
2268
+ }
2269
+ return { name: "tailwind.config", severity: "fail", message: "tailwind.config \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" };
2270
+ }
2271
+ function readAxisConfigLike(baseDir) {
2272
+ try {
2273
+ const parsed = JSON.parse(readFileSync(join(baseDir, "axis.config.json"), "utf8"));
2274
+ return isPlainObject(parsed) ? parsed : void 0;
2275
+ } catch {
2276
+ return void 0;
2277
+ }
2278
+ }
2279
+ function contentGlobToRegExp(glob) {
2280
+ const g = glob.trim().replace(/^\.\//, "");
2281
+ let re = "";
2282
+ for (let i = 0; i < g.length; i++) {
2283
+ const c = g[i];
2284
+ if (c === "*") {
2285
+ if (g[i + 1] === "*") {
2286
+ const slash = g[i + 2] === "/";
2287
+ re += slash ? "(?:.*/)?" : ".*";
2288
+ i += slash ? 2 : 1;
2289
+ } else re += "[^/]*";
2290
+ } else if (c === "?") re += "[^/]";
2291
+ else if (c === "{") {
2292
+ const end = g.indexOf("}", i);
2293
+ if (end === -1) {
2294
+ re += "\\{";
2295
+ continue;
2296
+ }
2297
+ re += `(?:${g.slice(i + 1, end).split(",").map((part) => part.replace(/[.+^$()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*")).join("|")})`;
2298
+ i = end;
2299
+ } else if (c === "[") {
2300
+ const end = g.indexOf("]", i);
2301
+ if (end === -1) re += "\\[";
2302
+ else {
2303
+ re += g.slice(i, end + 1);
2304
+ i = end;
2305
+ }
2306
+ } else re += c.replace(/[.+^$()|\\]/g, "\\$&");
2307
+ }
2308
+ return new RegExp(`^${re}$`);
2309
+ }
2310
+ function balancedSlice(src, start) {
2311
+ const open = src[start];
2312
+ const close = open === "[" ? "]" : "}";
2313
+ let depth = 0;
2314
+ for (let i = start; i < src.length; i++) {
2315
+ const c = src[i];
2316
+ if (c === '"' || c === "'" || c === "`") {
2317
+ let j = i + 1;
2318
+ while (j < src.length && src[j] !== c) j += src[j] === "\\" ? 2 : 1;
2319
+ i = j;
2320
+ continue;
2321
+ }
2322
+ if (c === open) depth++;
2323
+ else if (c === close && --depth === 0) return src.slice(start, i + 1);
2324
+ }
2325
+ return void 0;
2326
+ }
2327
+ function stringLiterals(src) {
2328
+ return [...src.matchAll(/(["'`])((?:\\.|(?!\1)[^\\])*)\1/g)].map((m) => m[2]).filter((s) => !s.includes("${"));
2329
+ }
2330
+ function readTailwindContent(source) {
2331
+ const src = stripJsComments(source);
2332
+ const key = /\bcontent\s*:\s*/g;
2333
+ for (const m of src.matchAll(key)) {
2334
+ let at = (m.index ?? 0) + m[0].length;
2335
+ let block = src[at] === "[" || src[at] === "{" ? balancedSlice(src, at) : void 0;
2336
+ if (block === void 0) continue;
2337
+ if (block.startsWith("{")) {
2338
+ const files = /\bfiles\s*:\s*\[/.exec(block);
2339
+ if (!files) return { globs: [], dynamic: true };
2340
+ at = files.index + files[0].length - 1;
2341
+ block = balancedSlice(block, at);
2342
+ if (block === void 0) return { globs: [], dynamic: true };
2343
+ }
2344
+ const inner = block.slice(1, -1);
2345
+ const globs = stringLiterals(inner);
2346
+ const rest = inner.replace(/(["'`])((?:\\.|(?!\1)[^\\])*)\1/g, "").replace(/[\s,]/g, "");
2347
+ return { globs, dynamic: rest !== "" };
2348
+ }
2349
+ return void 0;
2350
+ }
2351
+ function normalizeRel(p) {
2352
+ const s = p.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
2353
+ if (s === "" || s.startsWith("../") || s.startsWith("/") || /^[A-Za-z]:/.test(s)) return void 0;
2354
+ return s;
2355
+ }
2356
+ function inspectTailwindContent(baseDirInput) {
2357
+ const baseDir = resolve(baseDirInput);
2358
+ const pkg = readPackageJson(baseDir);
2359
+ const tailwind = detectTailwind(baseDir, pkg);
2360
+ if (tailwind.major !== void 0 && tailwind.major >= 4) return null;
2361
+ const configFile = TAILWIND_CONFIG_FILES.find((f) => existsSync(join(baseDir, f)));
2362
+ if (configFile === void 0) return null;
2363
+ const targets = [];
2364
+ const componentsDir = readAxisConfigLike(baseDir)?.componentsDir;
2365
+ const rel3 = typeof componentsDir === "string" ? normalizeRel(componentsDir) : void 0;
2366
+ if (rel3 !== void 0) {
2367
+ targets.push({ label: `componentsDir(${componentsDir})`, sample: `${rel3}/button.tsx`, suggest: `./${rel3}/**/*.{ts,tsx}` });
2368
+ }
2369
+ for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2370
+ if (pkg !== void 0 && hasDependency(pkg, name) !== void 0) {
2371
+ targets.push({ label: name, sample: `node_modules/${name}/dist/index.js`, suggest: `./node_modules/${name}/dist/**/*.{js,mjs}` });
2372
+ }
2373
+ }
2374
+ if (targets.length === 0) return null;
2375
+ const read = readTailwindContent(readFileSync(join(baseDir, configFile), "utf8"));
2376
+ const patterns = (read?.globs ?? []).filter((g) => !g.trim().startsWith("!")).map(contentGlobToRegExp);
2377
+ return {
2378
+ configFile,
2379
+ unreadable: read === void 0,
2380
+ dynamic: read?.dynamic ?? false,
2381
+ targets: targets.map((t) => ({ ...t, covered: patterns.some((re) => re.test(t.sample)) }))
2382
+ };
2383
+ }
2384
+ function checkTailwindContent(baseDir) {
2385
+ const r = inspectTailwindContent(baseDir);
2386
+ if (r === null) return null;
2387
+ const missing = r.targets.filter((t) => !t.covered);
2388
+ if (missing.length === 0) {
2389
+ return {
2390
+ name: "tailwind.content",
2391
+ severity: "pass",
2392
+ message: `${r.configFile}\uC758 content \uAC00 ${r.targets.map((t) => t.label).join(" \xB7 ")} \uB97C \uB36E\uC2B5\uB2C8\uB2E4`
2393
+ };
2394
+ }
2395
+ const list = missing.map((t) => `${t.label} \u2192 "${t.suggest}"`).join(" \xB7 ");
2396
+ if (r.unreadable || r.dynamic) {
2397
+ return {
2398
+ name: "tailwind.content",
2399
+ severity: "warn",
2400
+ 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}`
2401
+ };
2402
+ }
2403
+ return {
2404
+ name: "tailwind.content",
2405
+ severity: "fail",
2406
+ 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}`
2407
+ };
2408
+ }
2409
+ var ANIMATION_PLUGIN_TAILWIND3 = "tailwindcss-animate";
2410
+ var ANIMATION_PLUGIN_TAILWIND4 = "tw-animate-css";
2411
+ var ANIMATION_SCAN_ENTRY_LIMIT = 2e3;
2412
+ var ANIMATION_SCAN_DEPTH_LIMIT = 8;
2413
+ var ANIMATION_SCAN_FILE_BYTES = 512 * 1024;
2414
+ function componentsUseAnimation(baseDir, componentsDir) {
2415
+ const rel3 = normalizeRel(componentsDir);
2416
+ if (rel3 === void 0) return false;
2417
+ const stack = [[join(baseDir, rel3), 0]];
2418
+ let visited = 0;
2419
+ while (stack.length > 0) {
2420
+ const [dir, depth] = stack.pop();
2421
+ let names;
2422
+ try {
2423
+ names = readdirSync(dir);
2424
+ } catch {
2425
+ continue;
2426
+ }
2427
+ for (const name of names) {
2428
+ if (++visited > ANIMATION_SCAN_ENTRY_LIMIT) return false;
2429
+ const p = join(dir, name);
2430
+ let st;
2431
+ try {
2432
+ st = lstatSync(p);
2433
+ } catch {
2434
+ continue;
2435
+ }
2436
+ if (st.isSymbolicLink()) continue;
2437
+ if (st.isDirectory()) {
2438
+ if (name !== "node_modules" && depth < ANIMATION_SCAN_DEPTH_LIMIT) stack.push([p, depth + 1]);
2439
+ } else if (st.isFile() && st.size <= ANIMATION_SCAN_FILE_BYTES && /\.(tsx|ts|jsx|js)$/.test(name)) {
2440
+ if (/\banimate-(?:in|out)\b/.test(readFileSync(p, "utf8"))) return true;
2441
+ }
2442
+ }
2443
+ }
2444
+ return false;
2445
+ }
2446
+ function checkAnimationPlugin(baseDirInput) {
2447
+ const baseDir = resolve(baseDirInput);
2448
+ const pkg = readPackageJson(baseDir);
2449
+ const config = readAxisConfigLike(baseDir);
2450
+ const declared = pkg !== void 0 && hasDependency(pkg, "@axis-ds/ui-react") !== void 0;
2451
+ const copied = typeof config?.componentsDir === "string" && componentsUseAnimation(baseDir, config.componentsDir);
2452
+ if (!declared && !copied) return null;
2453
+ const by = declared ? "@axis-ds/ui-react \uC120\uC5B8" : "componentsDir \uC758 animate-in \uB958 \uD074\uB798\uC2A4";
2454
+ const tailwind = detectTailwind(baseDir, pkg);
2455
+ if (tailwind.major !== void 0 && tailwind.major >= 4) {
2456
+ const file = TAILWIND_CSS_CANDIDATES.find((c) => existsSync(join(baseDir, c)));
2457
+ const css = file !== void 0 ? stripCssComments(readFileSync(join(baseDir, file), "utf8")) : "";
2458
+ const ok = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${ANIMATION_PLUGIN_TAILWIND4}["']`).test(css) || new RegExp(`@plugin\\s+["']${ANIMATION_PLUGIN_TAILWIND3}["']`).test(css);
2459
+ return ok ? { name: "animation", severity: "pass", message: `${file}\uC5D0 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4` } : {
2460
+ name: "animation",
2461
+ severity: "warn",
2462
+ 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}";`
2463
+ };
2464
+ }
2465
+ const configFile = TAILWIND_CONFIG_FILES.find((f) => existsSync(join(baseDir, f)));
2466
+ const source = configFile !== void 0 ? stripJsComments(readFileSync(join(baseDir, configFile), "utf8")) : "";
2467
+ const pluginRef = new RegExp(`["']${ANIMATION_PLUGIN_TAILWIND3}["']`).test(source);
2468
+ const pluginDeclared = pkg !== void 0 && hasDependency(pkg, ANIMATION_PLUGIN_TAILWIND3) !== void 0;
2469
+ if (pluginRef && pluginDeclared) {
2470
+ return { name: "animation", severity: "pass", message: `${configFile}\uC5D0 ${ANIMATION_PLUGIN_TAILWIND3} \uD50C\uB7EC\uADF8\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4` };
2471
+ }
2472
+ const why = !pluginDeclared ? `${ANIMATION_PLUGIN_TAILWIND3} \uBBF8\uC124\uCE58` : `${configFile ?? "tailwind.config"} plugins \uC5D0 \uC5C6\uC74C`;
2473
+ return {
2474
+ name: "animation",
2475
+ severity: "warn",
2476
+ message: `\uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(${by}, ${why}): 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_TAILWIND3} \uC124\uCE58 \uB4A4 plugins \uC5D0 require("${ANIMATION_PLUGIN_TAILWIND3}")`
2477
+ };
2478
+ }
2479
+
2480
+ // src/template/template-apply.ts
2481
+ async function templateApplyWarnings(baseDir, detail) {
2482
+ const warnings = [];
2483
+ const pkgPath = path8.join(baseDir, "package.json");
2484
+ const templateRange = detail.devDependencies?.tailwindcss ?? detail.dependencies?.tailwindcss;
2485
+ const templateMajor = templateRange !== void 0 ? tailwindMajorFromRange(templateRange) : void 0;
2486
+ if (templateMajor !== void 0 && await fs8.pathExists(pkgPath)) {
2487
+ let pkg;
2488
+ try {
2489
+ pkg = await fs8.readJSON(pkgPath);
2490
+ } catch {
2491
+ pkg = void 0;
2492
+ }
2493
+ const project = detectTailwind(baseDir, pkg);
2494
+ if (project.major !== void 0 && project.major !== templateMajor) {
2495
+ warnings.push(
2496
+ `\uD15C\uD50C\uB9BF\uC740 Tailwind ${templateMajor} \uAE30\uC900\uC778\uB370 \uC774 \uD504\uB85C\uC81D\uD2B8\uB294 Tailwind ${project.major}(${project.value}) \uC785\uB2C8\uB2E4. \uBE4C\uB4DC \xB7 \uC2A4\uD0C0\uC77C\uC774 \uB9DE\uC9C0 \uC54A\uC744 \uC218 \uC788\uC5B4 \uBE48 \uB514\uB809\uD130\uB9AC\uC5D0 'axis template init' \uC73C\uB85C \uB9CC\uB4DC\uB294 \uAC83\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.`
2497
+ );
2498
+ }
2499
+ }
2500
+ const usesRootApp = detail.files.some((f) => f.path.startsWith("app/"));
2501
+ if (usesRootApp && await fs8.pathExists(path8.join(baseDir, "src", "app"))) {
2502
+ warnings.push(
2503
+ "src/app \uC774 \uC788\uB294\uB370 \uD15C\uD50C\uB9BF\uC740 \uB8E8\uD2B8 app/ \uC744 \uC501\uB2C8\uB2E4. Next.js \uB294 \uB8E8\uD2B8 app/ \uC774 \uC788\uC73C\uBA74 src/app \uC744 \uC4F0\uC9C0 \uC54A\uC544 \uAE30\uC874 \uB77C\uC6B0\uD2B8\uAC00 \uBE60\uC9D1\uB2C8\uB2E4."
2504
+ );
2505
+ }
2506
+ return warnings;
2507
+ }
2508
+ async function applyTemplate(slug, options) {
2509
+ const spinner = ora3(`'${slug}' \uD15C\uD50C\uB9BF \uAC00\uC838\uC624\uB294 \uC911...`).start();
2510
+ let detail;
2511
+ try {
2512
+ detail = await fetchTemplateDetail(slug);
2513
+ spinner.succeed(`'${detail.name}' \uD15C\uD50C\uB9BF \uB85C\uB4DC \uC644\uB8CC (${detail.files.length}\uAC1C \uD30C\uC77C)`);
2514
+ } catch (err) {
2515
+ spinner.fail(err.message);
2516
+ process.exit(1);
2517
+ }
2518
+ const baseDir = path8.resolve(options.dir || ".");
2519
+ for (const w of await templateApplyWarnings(baseDir, detail)) {
2520
+ console.log(chalk5.yellow(`! ${w}`));
2521
+ }
2522
+ const conflicts = [];
2523
+ for (const file of detail.files) {
2524
+ const dest = path8.join(baseDir, file.path);
2525
+ if (await fs8.pathExists(dest)) {
2526
+ conflicts.push(file.path);
2527
+ }
2528
+ }
2529
+ if (conflicts.length > 0 && !options.yes && !options.dryRun) {
2530
+ console.log(chalk5.yellow(`
2531
+ \uAE30\uC874 \uD30C\uC77C\uACFC \uCDA9\uB3CC\uD558\uB294 ${conflicts.length}\uAC1C \uD30C\uC77C:`));
2532
+ for (const c of conflicts) {
2533
+ console.log(chalk5.gray(` - ${c}`));
2534
+ }
2535
+ const { proceed } = await prompts({
2536
+ type: "confirm",
2537
+ name: "proceed",
2538
+ message: "\uCDA9\uB3CC \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
2539
+ initial: false
2540
+ });
2541
+ if (!proceed) {
2542
+ console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2543
+ return;
2544
+ }
2545
+ }
2546
+ if (options.dryRun) {
2547
+ console.log(chalk5.blue("\n[\uBBF8\uB9AC\uBCF4\uAE30] \uC0DD\uC131\uB420 \uD30C\uC77C:"));
2548
+ for (const file of detail.files) {
2549
+ const dest = path8.join(baseDir, file.path);
2550
+ const exists = conflicts.includes(file.path);
2551
+ const label = exists ? chalk5.yellow("\uB36E\uC5B4\uC4F0\uAE30") : chalk5.green("\uC0DD\uC131");
2552
+ console.log(` ${label} ${dest}`);
2553
+ }
2554
+ if (detail.postInstall?.length) {
2555
+ console.log(chalk5.blue("\n[\uBBF8\uB9AC\uBCF4\uAE30] postInstall \uD328\uCE58:"));
2556
+ for (const p of detail.postInstall) {
2557
+ console.log(chalk5.gray(` ${p.type}: ${p.file}`));
2558
+ }
2559
+ }
2560
+ return;
2561
+ }
2562
+ const writeSpinner = ora3("\uD30C\uC77C \uC0DD\uC131 \uC911...").start();
2563
+ for (const file of detail.files) {
2564
+ const dest = path8.join(baseDir, file.path);
2565
+ await fs8.ensureDir(path8.dirname(dest));
2566
+ await fs8.writeFile(dest, file.content);
2567
+ }
2568
+ writeSpinner.succeed(`${detail.files.length}\uAC1C \uD30C\uC77C \uC0DD\uC131 \uC644\uB8CC`);
2569
+ if (detail.postInstall?.length) {
2570
+ console.log(chalk5.blue("\npostInstall \uD328\uCE58 \uC2E4\uD589 \uC911..."));
2571
+ await runPostInstall(detail.postInstall, baseDir);
2572
+ }
2573
+ if (!options.skipDeps) {
2574
+ const allDeps = { ...detail.dependencies, ...detail.devDependencies };
2575
+ const depList = Object.entries(allDeps);
2576
+ if (depList.length > 0) {
2577
+ console.log(chalk5.yellow("\n\uD544\uC694\uD55C \uC758\uC874\uC131:"));
2578
+ const deps = Object.entries(detail.dependencies || {}).map(([k, v]) => `${k}@${v}`).join(" ");
2579
+ const devDeps = Object.entries(detail.devDependencies || {}).map(([k, v]) => `${k}@${v}`).join(" ");
2580
+ if (deps) {
2581
+ console.log(chalk5.cyan(` pnpm add ${deps}`));
2582
+ }
2583
+ if (devDeps) {
2584
+ console.log(chalk5.cyan(` pnpm add -D ${devDeps}`));
2585
+ }
2586
+ }
2587
+ }
2588
+ console.log(chalk5.green(`
2589
+ '${detail.name}' \uD15C\uD50C\uB9BF \uC801\uC6A9 \uC644\uB8CC!`));
2590
+ }
2591
+ async function initTemplate(slug, options) {
2592
+ let selectedSlug = slug;
2593
+ if (!selectedSlug) {
2594
+ const { fetchTemplateIndex: fetchTemplateIndex2 } = await import("./registry-client-6WSGKPF2.js");
2595
+ const index = await fetchTemplateIndex2();
2596
+ const { choice } = await prompts({
2597
+ type: "select",
2598
+ name: "choice",
2599
+ message: "\uC0AC\uC6A9\uD560 \uD15C\uD50C\uB9BF\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
2600
+ choices: index.templates.map((t) => ({
2601
+ title: `${t.name} (${t.category})`,
2602
+ description: t.description,
2603
+ value: t.slug
2604
+ }))
2605
+ });
2606
+ if (!choice) {
2607
+ console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2608
+ return;
2609
+ }
2610
+ selectedSlug = choice;
2611
+ }
2612
+ let targetDir = options.dir;
2613
+ if (!targetDir) {
2614
+ const { dir } = await prompts({
2615
+ type: "text",
2616
+ name: "dir",
2617
+ message: "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC:",
2618
+ initial: selectedSlug
2619
+ });
2620
+ if (!dir) {
2621
+ console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2622
+ return;
2623
+ }
2624
+ targetDir = dir;
2625
+ }
2626
+ const resolved = path8.resolve(targetDir);
2627
+ if (await fs8.pathExists(resolved)) {
2628
+ const entries = await fs8.readdir(resolved);
2629
+ if (entries.length > 0) {
2630
+ const { proceed } = await prompts({
2631
+ type: "confirm",
2632
+ name: "proceed",
2633
+ message: `'${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?`,
2634
+ initial: false
2635
+ });
2636
+ if (!proceed) return;
2637
+ }
2638
+ } else {
2639
+ await fs8.ensureDir(resolved);
2640
+ }
2641
+ await applyTemplate(selectedSlug, { dir: targetDir, yes: true });
2642
+ const configPath = path8.join(resolved, "axis.config.json");
2643
+ if (!await fs8.pathExists(configPath)) {
2644
+ const axisConfig = createTemplateConfig(selectedSlug);
2645
+ await fs8.writeJSON(configPath, axisConfig, { spaces: 2 });
1980
2646
  console.log(chalk5.green("axis.config.json \uC0DD\uC131 \uC644\uB8CC"));
1981
2647
  }
1982
2648
  console.log(chalk5.blue(`
@@ -2062,293 +2728,60 @@ async function diffTemplate(slug, options) {
2062
2728
  break;
2063
2729
  case "unchanged":
2064
2730
  unchanged++;
2065
- break;
2066
- case "extra":
2067
- extra++;
2068
- break;
2069
- }
2070
- }
2071
- console.log(chalk6.blue("\n\uC694\uC57D:"));
2072
- console.log(
2073
- ` \uCD94\uAC00 \uD544\uC694: ${chalk6.green(String(added))} | \uBCC0\uACBD\uB428: ${chalk6.yellow(String(modified))} | \uB3D9\uC77C: ${chalk6.gray(String(unchanged))}${extra > 0 ? ` | \uB85C\uCEEC \uC804\uC6A9: ${chalk6.cyan(String(extra))}` : ""}`
2074
- );
2075
- }
2076
- async function collectLocalFiles(dir, base) {
2077
- const root = base || dir;
2078
- const ignore = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", "dist"]);
2079
- const results = [];
2080
- let entries;
2081
- try {
2082
- entries = await fs9.readdir(dir, { withFileTypes: true });
2083
- } catch {
2084
- return results;
2085
- }
2086
- for (const entry of entries) {
2087
- if (ignore.has(entry.name)) continue;
2088
- const fullPath = path9.join(dir, entry.name);
2089
- if (entry.isDirectory()) {
2090
- const nested = await collectLocalFiles(fullPath, root);
2091
- results.push(...nested);
2092
- } else {
2093
- results.push(path9.relative(root, fullPath).replace(/\\/g, "/"));
2094
- }
2095
- }
2096
- return results;
2097
- }
2098
-
2099
- // src/template/template-check.ts
2100
- import fs10 from "fs-extra";
2101
- import path10 from "path";
2102
- import chalk7 from "chalk";
2103
-
2104
- // src/theme/brand-core.ts
2105
- import { existsSync, readFileSync, statSync } from "fs";
2106
- import { createRequire } from "module";
2107
- import { dirname, join, relative, resolve, sep } from "path";
2108
- var TOKENS_PACKAGE = "@axis-ds/tokens";
2109
- var BRAND_NAME_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
2110
- var MIN_BRAND_TOKENS_VERSION = "1.3.0";
2111
- var KEY_TOKENS = [
2112
- "--primary",
2113
- "--primary-foreground",
2114
- "--background",
2115
- "--foreground",
2116
- "--ring",
2117
- "--radius",
2118
- "--axis-surface-default",
2119
- "--axis-text-primary",
2120
- "--axis-text-brand",
2121
- "--axis-button-bg-default",
2122
- "--axis-border-focus",
2123
- "--axis-font-family-sans"
2124
- ];
2125
- var BRAND_ADJUSTMENT_REFERENCE = "WI-0025 PRD \uBD80\uB85D B (docs/workitems/WI-0025-brand-theme/prd.md)";
2126
- function readJsonFile(file) {
2127
- return JSON.parse(readFileSync(file, "utf8"));
2128
- }
2129
- function errorCode(err) {
2130
- return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
2131
- }
2132
- function findTokensPackageJson(baseDir) {
2133
- let dir = resolve(baseDir);
2134
- for (; ; ) {
2135
- const candidate = join(dir, "node_modules", TOKENS_PACKAGE, "package.json");
2136
- if (existsSync(candidate)) return candidate;
2137
- const parent = dirname(dir);
2138
- if (parent === dir) return void 0;
2139
- dir = parent;
2140
- }
2141
- }
2142
- function projectRequire(baseDir) {
2143
- return createRequire(join(resolve(baseDir), "package.json"));
2144
- }
2145
- function tokensSpecifier(subpath) {
2146
- return `${TOKENS_PACKAGE}/${subpath.replace(/^\.\//, "")}`;
2147
- }
2148
- function resolveTokens(baseDir) {
2149
- const pkgJson = findTokensPackageJson(baseDir);
2150
- if (!pkgJson) return { status: "not-installed" };
2151
- const pkg = readJsonFile(pkgJson);
2152
- const version = typeof pkg.version === "string" ? pkg.version : void 0;
2153
- try {
2154
- const indexPath = projectRequire(baseDir).resolve(tokensSpecifier("./brands"));
2155
- return { status: "ok", version, indexPath };
2156
- } catch (err) {
2157
- const code = errorCode(err);
2158
- if (code === "ERR_PACKAGE_PATH_NOT_EXPORTED") return { status: "unsupported", version };
2159
- if (code === "MODULE_NOT_FOUND") return { status: "not-built", version };
2160
- throw err;
2161
- }
2162
- }
2163
- function brandImportPath(entry) {
2164
- return tokensSpecifier(entry.css);
2165
- }
2166
- var GLOBAL_CSS_CANDIDATES = ["app/globals.css", "src/app/globals.css", "globals.css"];
2167
- function isFile(file) {
2168
- try {
2169
- return statSync(file).isFile();
2170
- } catch {
2171
- return false;
2172
- }
2173
- }
2174
- function findGlobalCss(baseDir, config) {
2175
- const root = resolve(baseDir);
2176
- const result = {};
2177
- const configured = config?.globalCss;
2178
- if (typeof configured === "string" && configured.trim() !== "") {
2179
- const file = resolve(root, configured);
2180
- if (isFile(file)) return { path: file };
2181
- result.configuredMissing = configured;
2182
- }
2183
- for (const candidate of GLOBAL_CSS_CANDIDATES) {
2184
- const file = join(root, candidate);
2185
- if (isFile(file)) {
2186
- result.path = file;
2187
- break;
2188
- }
2189
- }
2190
- return result;
2191
- }
2192
- function findBrandImports(css) {
2193
- const names = [];
2194
- const pattern = /@import\s+(?:url\(\s*)?["']?@axis-ds\/tokens\/css\/brands\/([a-z][a-z0-9-]*)(?:\.css)?["']?\s*\)?\s*(?=[;\s]|$)/g;
2195
- for (const m of stripComments(css).matchAll(pattern)) {
2196
- names.push(m[1]);
2197
- }
2198
- return names;
2199
- }
2200
- var AXIS_CONFIG_FILE_NAME = "axis.config.json";
2201
- function readBrandConfig(baseDir) {
2202
- let parsed;
2203
- try {
2204
- parsed = JSON.parse(readFileSync(join(resolve(baseDir), AXIS_CONFIG_FILE_NAME), "utf8"));
2205
- } catch {
2206
- return null;
2207
- }
2208
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
2209
- return parsed;
2210
- }
2211
- function inspectBrandImport(baseDir) {
2212
- const config = readBrandConfig(baseDir);
2213
- const brand = config?.brand;
2214
- if (typeof brand !== "string" || brand.trim() === "") return null;
2215
- const root = resolve(baseDir);
2216
- const location = findGlobalCss(root, config);
2217
- const base = { brand };
2218
- if (location.configuredMissing !== void 0) base.configuredMissing = location.configuredMissing;
2219
- if (!location.path) {
2220
- return { ...base, state: "css-missing", imports: [] };
2221
- }
2222
- const cssFile = relative(root, location.path).split(sep).join("/");
2223
- const imports = [...new Set(findBrandImports(readFileSync(location.path, "utf8")))];
2224
- let state;
2225
- if (imports.includes(brand)) state = "match";
2226
- else if (imports.length === 0) state = "import-missing";
2227
- else state = "other-brand";
2228
- return { ...base, state, cssFile, imports };
2229
- }
2230
- function isMissingBuildFile(err) {
2231
- const code = errorCode(err);
2232
- return code === "MODULE_NOT_FOUND" || code === "ENOENT";
2233
- }
2234
- function readBrandIndex(indexPath) {
2235
- const data = readJsonFile(indexPath);
2236
- if (!Array.isArray(data.brands)) {
2237
- throw new Error(`${indexPath}: "brands" \uBC30\uC5F4\uC774 \uC5C6\uC2B5\uB2C8\uB2E4`);
2238
- }
2239
- return data.brands;
2240
- }
2241
- function readSummary(baseDir, entry) {
2242
- const metaPath = projectRequire(baseDir).resolve(tokensSpecifier(entry.meta));
2243
- const meta = readJsonFile(metaPath);
2244
- return {
2245
- name: entry.name,
2246
- description: typeof meta.description === "string" ? meta.description : "",
2247
- themes: Array.isArray(meta.themes) ? meta.themes.map(String) : [],
2248
- a11y: typeof meta.a11y === "object" && meta.a11y !== null ? meta.a11y : {},
2249
- importPath: brandImportPath(entry)
2250
- };
2251
- }
2252
- function listBrands(baseDir) {
2253
- const tokens = resolveTokens(baseDir);
2254
- if (tokens.status !== "ok" || !tokens.indexPath) {
2255
- return { status: tokens.status, version: tokens.version, brands: [] };
2256
- }
2257
- const entries = readBrandIndex(tokens.indexPath);
2258
- let brands;
2259
- try {
2260
- brands = entries.map((entry) => readSummary(baseDir, entry));
2261
- } catch (err) {
2262
- if (isMissingBuildFile(err)) return { status: "not-built", version: tokens.version, brands: [] };
2263
- throw err;
2264
- }
2265
- return { status: "ok", version: tokens.version, brands };
2266
- }
2267
- function stripComments(css) {
2268
- return css.replace(/\/\*[\s\S]*?\*\//g, "");
2269
- }
2270
- function parseDeclarations(body) {
2271
- const out = {};
2272
- for (const m of body.matchAll(/(--[A-Za-z0-9_-]+)\s*:\s*([^;]+);/g)) {
2273
- out[m[1]] = m[2].trim();
2274
- }
2275
- return out;
2276
- }
2277
- function isDarkSelector(selector) {
2278
- return /\.dark\b/.test(selector) || /\[data-theme=["']?dark["']?\]/.test(selector);
2279
- }
2280
- function parseBrandCss(css, name) {
2281
- const result = { light: {}, dark: {} };
2282
- let lightFound = false;
2283
- let darkFound = false;
2284
- const brandAttr = new RegExp(`\\[data-brand=["']?${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']?\\]`);
2285
- for (const m of stripComments(css).matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
2286
- const selectors = m[1].split(",").map((s) => s.trim());
2287
- if (!selectors.some((s) => brandAttr.test(s))) continue;
2288
- if (selectors.some(isDarkSelector)) {
2289
- if (!darkFound) {
2290
- result.dark = parseDeclarations(m[2]);
2291
- darkFound = true;
2292
- }
2293
- } else if (!lightFound) {
2294
- result.light = parseDeclarations(m[2]);
2295
- lightFound = true;
2731
+ break;
2732
+ case "extra":
2733
+ extra++;
2734
+ break;
2296
2735
  }
2297
2736
  }
2298
- return result;
2299
- }
2300
- function pickKeyTokens(decls) {
2301
- const out = {};
2302
- for (const key of KEY_TOKENS) {
2303
- if (Object.prototype.hasOwnProperty.call(decls, key)) out[key] = decls[key];
2304
- }
2305
- return out;
2306
- }
2307
- function providerExample(name) {
2308
- return [
2309
- 'import { ThemeProvider } from "@axis-ds/theme";',
2310
- "",
2311
- `<ThemeProvider defaultBrand="${name}">{children}</ThemeProvider>`
2312
- ].join("\n");
2737
+ console.log(chalk6.blue("\n\uC694\uC57D:"));
2738
+ console.log(
2739
+ ` \uCD94\uAC00 \uD544\uC694: ${chalk6.green(String(added))} | \uBCC0\uACBD\uB428: ${chalk6.yellow(String(modified))} | \uB3D9\uC77C: ${chalk6.gray(String(unchanged))}${extra > 0 ? ` | \uB85C\uCEEC \uC804\uC6A9: ${chalk6.cyan(String(extra))}` : ""}`
2740
+ );
2313
2741
  }
2314
- function getBrand(baseDir, name) {
2315
- const tokens = resolveTokens(baseDir);
2316
- if (tokens.status !== "ok" || !tokens.indexPath) {
2317
- return { status: tokens.status, version: tokens.version, brand: null, available: [] };
2318
- }
2319
- const entries = readBrandIndex(tokens.indexPath);
2320
- const available = entries.map((e) => e.name);
2321
- const entry = entries.find((e) => e.name === name);
2322
- if (!entry) {
2323
- return { status: "ok", version: tokens.version, brand: null, available };
2324
- }
2325
- let summary;
2326
- let parsed;
2742
+ async function collectLocalFiles(dir, base) {
2743
+ const root = base || dir;
2744
+ const ignore = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", "dist"]);
2745
+ const results = [];
2746
+ let entries;
2327
2747
  try {
2328
- summary = readSummary(baseDir, entry);
2329
- const cssPath = projectRequire(baseDir).resolve(summary.importPath);
2330
- parsed = parseBrandCss(readFileSync(cssPath, "utf8"), name);
2331
- } catch (err) {
2332
- if (isMissingBuildFile(err)) return { status: "not-built", version: tokens.version, brand: null, available: [] };
2333
- throw err;
2748
+ entries = await fs9.readdir(dir, { withFileTypes: true });
2749
+ } catch {
2750
+ return results;
2334
2751
  }
2335
- return {
2336
- status: "ok",
2337
- version: tokens.version,
2338
- available,
2339
- brand: {
2340
- ...summary,
2341
- tokens: { light: pickKeyTokens(parsed.light), dark: pickKeyTokens(parsed.dark) },
2342
- providerExample: providerExample(name),
2343
- adjustments: { a11y: summary.a11y, reference: BRAND_ADJUSTMENT_REFERENCE }
2752
+ for (const entry of entries) {
2753
+ if (ignore.has(entry.name)) continue;
2754
+ const fullPath = path9.join(dir, entry.name);
2755
+ if (entry.isDirectory()) {
2756
+ const nested = await collectLocalFiles(fullPath, root);
2757
+ results.push(...nested);
2758
+ } else {
2759
+ results.push(path9.relative(root, fullPath).replace(/\\/g, "/"));
2344
2760
  }
2345
- };
2761
+ }
2762
+ return results;
2346
2763
  }
2347
2764
 
2348
- // ../axis-registry-core/src/template/tailwind-core.ts
2349
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
2350
- import { join as join2, resolve as resolve2 } from "path";
2351
- function hasDependency(pkg, name) {
2765
+ // src/template/template-check.ts
2766
+ import fs10 from "fs-extra";
2767
+ import path11 from "path";
2768
+ import chalk7 from "chalk";
2769
+
2770
+ // src/theme/brand-core.ts
2771
+ import { existsSync as existsSync3, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
2772
+ import { createRequire } from "module";
2773
+ import { dirname, join as join2, relative, resolve as resolve2, sep } from "path";
2774
+
2775
+ // ../axis-registry-core/src/project.ts
2776
+ import { existsSync as existsSync2, statSync } from "fs";
2777
+ import path10 from "path";
2778
+ function detectInstallCommand(baseDir) {
2779
+ if (existsSync2(path10.join(baseDir, "pnpm-lock.yaml"))) return "pnpm install";
2780
+ if (existsSync2(path10.join(baseDir, "yarn.lock"))) return "yarn install";
2781
+ if (existsSync2(path10.join(baseDir, "package-lock.json"))) return "npm install";
2782
+ return "pnpm install";
2783
+ }
2784
+ function hasDependency2(pkg, name) {
2352
2785
  for (const field of ["dependencies", "devDependencies"]) {
2353
2786
  const deps = pkg[field];
2354
2787
  if (typeof deps === "object" && deps !== null && name in deps) {
@@ -2357,424 +2790,352 @@ function hasDependency(pkg, name) {
2357
2790
  }
2358
2791
  return void 0;
2359
2792
  }
2360
- function isPlainObject(value) {
2793
+ function isPlainObject2(value) {
2361
2794
  return typeof value === "object" && value !== null && !Array.isArray(value);
2362
2795
  }
2363
- var VERSION = String.raw`v?(\d+)(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?`;
2364
- function versionParts(token) {
2365
- const m = new RegExp(`^${VERSION}$`).exec(token);
2366
- if (!m) return void 0;
2367
- if (/^v?0\.0\.0-/.test(token)) return void 0;
2368
- return { major: Number(m[1]), minor: m[2], patch: m[3] };
2369
- }
2370
- var isWild = (part) => part === void 0 || /^[xX*]$/.test(part);
2371
- function comparatorBounds(op, v) {
2372
- switch (op) {
2373
- case ">=":
2374
- return { lower: v.major };
2375
- case ">":
2376
- return { lower: isWild(v.minor) ? v.major + 1 : v.major };
2377
- case "<=":
2378
- return { upper: v.major };
2379
- case "<": {
2380
- const zeroOrWild = (part) => isWild(part) || Number(part) === 0;
2381
- return { upper: zeroOrWild(v.minor) && zeroOrWild(v.patch) ? v.major - 1 : v.major };
2382
- }
2383
- default:
2384
- return { lower: v.major, upper: v.major };
2385
- }
2386
- }
2387
- function tailwindMajorBounds(range) {
2388
- const r = range.trim();
2389
- if (r === "" || r.includes("||")) return void 0;
2390
- const simple = new RegExp(`^(?:\\^|~|=)?\\s*(${VERSION})$`).exec(r);
2391
- if (simple) {
2392
- const v = versionParts(simple[1]);
2393
- return v ? { lower: v.major, upper: v.major } : void 0;
2394
- }
2395
- const hyphen = new RegExp(`^(${VERSION})\\s+-\\s+(${VERSION})$`).exec(r);
2396
- if (hyphen) {
2397
- const tokens2 = r.split(/\s+-\s+/);
2398
- const a = versionParts(tokens2[0]);
2399
- const b = versionParts(tokens2[1]);
2400
- return a && b && a.major <= b.major ? { lower: a.major, upper: b.major } : void 0;
2401
- }
2402
- const tokens = r.replace(/(>=|<=|>|<|=)\s+/g, "$1").split(/\s+/);
2403
- const lowers = [];
2404
- const uppers = [];
2405
- for (const t of tokens) {
2406
- const c = /^(>=|<=|>|<|=)(.+)$/.exec(t);
2407
- if (!c) return void 0;
2408
- const v = versionParts(c[2]);
2409
- if (v === void 0) return void 0;
2410
- const b = comparatorBounds(c[1], v);
2411
- if (b.lower !== void 0) lowers.push(b.lower);
2412
- if (b.upper !== void 0) uppers.push(b.upper);
2413
- }
2414
- if (lowers.length === 0) return void 0;
2415
- const lower = Math.max(...lowers);
2416
- const upper = uppers.length > 0 ? Math.min(...uppers) : void 0;
2417
- if (upper !== void 0 && upper < lower) return void 0;
2418
- return upper !== void 0 ? { lower, upper } : { lower };
2419
- }
2420
- function majorWithin(major, bounds) {
2421
- return major >= bounds.lower && (bounds.upper === void 0 || major <= bounds.upper);
2422
- }
2423
- function majorFromVersion(version) {
2424
- if (typeof version !== "string") return void 0;
2425
- const v = version.trim();
2426
- const m = /^v?(\d+)\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]*)?$/.exec(v);
2427
- if (!m || /^v?0\.0\.0-/.test(v)) return void 0;
2428
- return Number(m[1]);
2429
- }
2430
- function detectTailwind(baseDirInput, pkg) {
2431
- const baseDir = resolve2(baseDirInput);
2432
- const installedFile = join2(baseDir, "node_modules", "tailwindcss", "package.json");
2433
- const declared = pkg ? hasDependency(pkg, "tailwindcss") : void 0;
2434
- const bounds = declared !== void 0 ? tailwindMajorBounds(declared) : void 0;
2435
- const declaredMajor = bounds?.lower;
2436
- try {
2437
- const installed = JSON.parse(readFileSync2(installedFile, "utf8"));
2438
- const version = isPlainObject(installed) ? installed.version : void 0;
2439
- const major = majorFromVersion(version);
2440
- if (major !== void 0) {
2441
- const found = { major, source: "installed", value: String(version) };
2442
- if (declared !== void 0 && bounds !== void 0 && !majorWithin(major, bounds)) {
2443
- found.mismatch = { declared, declaredMajor: bounds.lower };
2444
- }
2445
- return found;
2446
- }
2447
- } catch {
2448
- }
2449
- if (declared !== void 0) {
2450
- if (declaredMajor !== void 0) return { major: declaredMajor, source: "declared", value: declared };
2451
- return { source: "none", value: declared };
2452
- }
2453
- return { source: "none" };
2796
+ function addCommandFor(installCommand, deps) {
2797
+ const specs = Object.entries(deps).map(([name, range]) => `"${name}@${range}"`).join(" ");
2798
+ if (installCommand.startsWith("yarn")) return `yarn add ${specs}`;
2799
+ if (installCommand.startsWith("npm")) return `npm install ${specs}`;
2800
+ return `pnpm add ${specs}`;
2454
2801
  }
2455
- var TAILWIND_CONFIG_FILES = [
2802
+ var GLOBAL_CSS_CANDIDATES = [
2803
+ "app/globals.css",
2804
+ "src/app/globals.css",
2805
+ "styles/globals.css",
2806
+ "src/styles/globals.css",
2807
+ "globals.css"
2808
+ ];
2809
+ var TAILWIND_CONFIG_CANDIDATES = [
2456
2810
  "tailwind.config.ts",
2457
2811
  "tailwind.config.js",
2458
2812
  "tailwind.config.mjs",
2459
2813
  "tailwind.config.cjs"
2460
2814
  ];
2461
- var TAILWIND_CSS_CANDIDATES = [
2462
- "globals.css",
2463
- "app/globals.css",
2464
- "src/app/globals.css",
2465
- "src/globals.css",
2466
- "styles/globals.css"
2467
- ];
2468
- var TAILWIND_IMPORT = /@import\s+(?:url\(\s*)?["']tailwindcss["']/;
2469
- function stripCssComments(css) {
2470
- return css.replace(/\/\*[\s\S]*?\*\//g, "");
2815
+ function isFile(file) {
2816
+ try {
2817
+ return statSync(file).isFile();
2818
+ } catch {
2819
+ return false;
2820
+ }
2471
2821
  }
2472
- function hasTailwindImport(css) {
2473
- return TAILWIND_IMPORT.test(stripCssComments(css));
2822
+ function isDirectory(dir) {
2823
+ try {
2824
+ return statSync(dir).isDirectory();
2825
+ } catch {
2826
+ return false;
2827
+ }
2474
2828
  }
2475
- var TOKENS_TAILWIND4_ENTRY = "@axis-ds/tokens/css/tailwind4";
2476
- var TOKENS_TAILWIND4_IMPORT_LINE = `@import "${TOKENS_TAILWIND4_ENTRY}";`;
2477
- var TOKENS_TAILWIND4_IMPORT = /@import\s+(?:url\(\s*)?["']@axis-ds\/tokens\/css\/tailwind4["']/;
2478
- function hasTokensTailwind4Import(css) {
2479
- return TOKENS_TAILWIND4_IMPORT.test(stripCssComments(css));
2829
+ function findFirstFile(baseDir, candidates) {
2830
+ return candidates.find((c) => isFile(path10.join(baseDir, c)));
2480
2831
  }
2481
- var TOKENS_CSS_IMPORT = /@import\s+(?:url\(\s*)?["'](@axis-ds\/tokens\/css(?:\/shadcn|\/tailwind4)?)["']/;
2482
- function findTokensCssImport(css) {
2483
- return TOKENS_CSS_IMPORT.exec(stripCssComments(css))?.[1];
2832
+ function detectProjectPaths(baseDirInput) {
2833
+ const baseDir = path10.resolve(baseDirInput);
2834
+ const hasSrc = isDirectory(path10.join(baseDir, "src"));
2835
+ const css = findFirstFile(baseDir, GLOBAL_CSS_CANDIDATES);
2836
+ const tw = findFirstFile(baseDir, TAILWIND_CONFIG_CANDIDATES);
2837
+ return {
2838
+ componentsDir: hasSrc ? "./src/components/ui" : "./components/ui",
2839
+ globalCss: css !== void 0 ? `./${css}` : hasSrc ? "./src/app/globals.css" : "./app/globals.css",
2840
+ globalCssSource: css !== void 0 ? "found" : "default",
2841
+ tailwindConfig: tw !== void 0 ? `./${tw}` : "./tailwind.config.ts",
2842
+ tailwindConfigSource: tw !== void 0 ? "found" : "default"
2843
+ };
2484
2844
  }
2485
- function checkGlobalsCss(baseDirInput) {
2486
- const baseDir = resolve2(baseDirInput);
2487
- for (const candidate of TAILWIND_CSS_CANDIDATES) {
2488
- const file = join2(baseDir, candidate);
2489
- if (!existsSync2(file)) continue;
2490
- const content = readFileSync2(file, "utf8");
2491
- if (content.includes("--background") && content.includes("--primary")) {
2492
- return { name: "globals.css", severity: "pass", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC815\uC758\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4` };
2493
- }
2494
- const imported = findTokensCssImport(content);
2495
- if (imported !== void 0) {
2496
- return { name: "globals.css", severity: "pass", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${imported} import)` };
2497
- }
2498
- return { name: "globals.css", severity: "warn", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4` };
2499
- }
2500
- return { name: "globals.css", severity: "fail", message: "globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" };
2845
+ function projectFileExists(baseDir, relPath) {
2846
+ return isFile(path10.resolve(baseDir, relPath));
2501
2847
  }
2502
- var TAILWIND4_MIN_AXIS_VERSION = "1.4.0";
2503
- var TAILWIND4_AXIS_PACKAGES = ["@axis-ds/tokens", "@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2504
- function lowerOf(token) {
2505
- const v = versionParts(token);
2506
- if (!v) return void 0;
2507
- const num = (part) => isWild(part) ? 0 : Number(part);
2508
- const pre = /^v?\d+(?:\.(?:\d+|[xX*]))?(?:\.(?:\d+|[xX*]))?-/.test(token);
2509
- return { core: [v.major, num(v.minor), num(v.patch)], pre };
2848
+
2849
+ // src/theme/brand-core.ts
2850
+ var TOKENS_PACKAGE = "@axis-ds/tokens";
2851
+ var BRAND_NAME_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
2852
+ var MIN_BRAND_TOKENS_VERSION = "1.3.0";
2853
+ var TOKENS_DEPENDENCY_MIN_VERSION = "1.6.0";
2854
+ var KEY_TOKENS = [
2855
+ "--primary",
2856
+ "--primary-foreground",
2857
+ "--background",
2858
+ "--foreground",
2859
+ "--ring",
2860
+ "--radius",
2861
+ "--axis-surface-default",
2862
+ "--axis-text-primary",
2863
+ "--axis-text-brand",
2864
+ "--axis-button-bg-default",
2865
+ "--axis-border-focus",
2866
+ "--axis-font-family-sans"
2867
+ ];
2868
+ var BRAND_ADJUSTMENT_REFERENCE = "WI-0025 PRD \uBD80\uB85D B (docs/workitems/WI-0025-brand-theme/prd.md)";
2869
+ function readJsonFile(file) {
2870
+ return JSON.parse(readFileSync2(file, "utf8"));
2871
+ }
2872
+ function errorCode(err) {
2873
+ return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
2510
2874
  }
2511
- function compareLower(a, b) {
2512
- for (let i = 0; i < 3; i++) {
2513
- if (a.core[i] !== b.core[i]) return a.core[i] - b.core[i];
2875
+ function findTokensPackageJson(baseDir) {
2876
+ let dir = resolve2(baseDir);
2877
+ for (; ; ) {
2878
+ const candidate = join2(dir, "node_modules", TOKENS_PACKAGE, "package.json");
2879
+ if (existsSync3(candidate)) return candidate;
2880
+ const parent = dirname(dir);
2881
+ if (parent === dir) return void 0;
2882
+ dir = parent;
2514
2883
  }
2515
- return Number(b.pre) - Number(a.pre);
2516
2884
  }
2517
- function rangeLowerVersion(range) {
2518
- const r = range.trim();
2519
- if (r === "" || r.includes("||")) return void 0;
2520
- const simple = new RegExp(`^(?:\\^|~|=)?\\s*(${VERSION})$`).exec(r);
2521
- if (simple) return lowerOf(simple[1]);
2522
- if (new RegExp(`^(${VERSION})\\s+-\\s+(${VERSION})$`).test(r)) {
2523
- const [a, b] = r.split(/\s+-\s+/);
2524
- return lowerOf(b) ? lowerOf(a) : void 0;
2525
- }
2526
- const tokens = r.replace(/(>=|<=|>|<|=)\s+/g, "$1").split(/\s+/);
2527
- let lower;
2528
- for (const t of tokens) {
2529
- const c = /^(>=|<=|>|<|=)(.+)$/.exec(t);
2530
- if (!c) return void 0;
2531
- const v = versionParts(c[2]);
2532
- const low = lowerOf(c[2]);
2533
- if (!v || !low) return void 0;
2534
- let candidate;
2535
- if (c[1] === ">=" || c[1] === "=") candidate = low;
2536
- else if (c[1] === ">") {
2537
- if (isWild(v.minor)) candidate = { core: [v.major + 1, 0, 0], pre: false };
2538
- else if (isWild(v.patch)) candidate = { core: [v.major, Number(v.minor) + 1, 0], pre: false };
2539
- else candidate = { core: [v.major, Number(v.minor), Number(v.patch) + 1], pre: false };
2540
- }
2541
- if (candidate && (!lower || compareLower(candidate, lower) > 0)) lower = candidate;
2542
- }
2543
- return lower;
2885
+ function projectRequire(baseDir) {
2886
+ return createRequire(join2(resolve2(baseDir), "package.json"));
2544
2887
  }
2545
- function rangeLowerAtLeast(range, min) {
2546
- const lower = rangeLowerVersion(range);
2547
- const target = lowerOf(min);
2548
- if (!lower || !target) return void 0;
2549
- return compareLower(lower, target) >= 0;
2888
+ function tokensSpecifier(subpath) {
2889
+ return `${TOKENS_PACKAGE}/${subpath.replace(/^\.\//, "")}`;
2550
2890
  }
2551
- function exactVersion(version) {
2552
- if (typeof version !== "string") return void 0;
2553
- const v = version.trim();
2554
- if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(v) || /^v?0\.0\.0-/.test(v)) return void 0;
2555
- return v;
2891
+ function resolveTokens(baseDir) {
2892
+ const pkgJson = findTokensPackageJson(baseDir);
2893
+ if (!pkgJson) return { status: "not-installed" };
2894
+ const pkg = readJsonFile(pkgJson);
2895
+ const version = typeof pkg.version === "string" ? pkg.version : void 0;
2896
+ try {
2897
+ const indexPath = projectRequire(baseDir).resolve(tokensSpecifier("./brands"));
2898
+ return { status: "ok", version, indexPath };
2899
+ } catch (err) {
2900
+ const code = errorCode(err);
2901
+ if (code === "ERR_PACKAGE_PATH_NOT_EXPORTED") return { status: "unsupported", version };
2902
+ if (code === "MODULE_NOT_FOUND") return { status: "not-built", version };
2903
+ throw err;
2904
+ }
2556
2905
  }
2557
- function installedVersion(baseDir, name) {
2906
+ function brandImportPath(entry) {
2907
+ return tokensSpecifier(entry.css);
2908
+ }
2909
+ function isFile2(file) {
2558
2910
  try {
2559
- const parsed = JSON.parse(readFileSync2(join2(baseDir, "node_modules", ...name.split("/"), "package.json"), "utf8"));
2560
- return isPlainObject(parsed) ? exactVersion(parsed.version) : void 0;
2911
+ return statSync2(file).isFile();
2561
2912
  } catch {
2562
- return void 0;
2913
+ return false;
2563
2914
  }
2564
2915
  }
2565
- function axisPackagesBelowTailwind4(pkg, baseDirInput) {
2566
- if (!pkg) return [];
2567
- const baseDir = baseDirInput !== void 0 ? resolve2(baseDirInput) : void 0;
2568
- const out = [];
2569
- const min = lowerOf(TAILWIND4_MIN_AXIS_VERSION);
2570
- for (const name of TAILWIND4_AXIS_PACKAGES) {
2571
- const range = hasDependency(pkg, name);
2572
- if (range === void 0 || range.trim().startsWith("workspace:")) continue;
2573
- const installed = baseDir !== void 0 ? installedVersion(baseDir, name) : void 0;
2574
- const installedLower = installed !== void 0 ? lowerOf(installed) : void 0;
2575
- if (installed !== void 0 && installedLower !== void 0) {
2576
- if (compareLower(installedLower, min) < 0) out.push({ name, range, status: "below", source: "installed", installed });
2577
- continue;
2916
+ function findGlobalCss(baseDir, config) {
2917
+ const root = resolve2(baseDir);
2918
+ const result = {};
2919
+ const configured = config?.globalCss;
2920
+ if (typeof configured === "string" && configured.trim() !== "") {
2921
+ const file = resolve2(root, configured);
2922
+ if (isFile2(file)) return { path: file };
2923
+ result.configuredMissing = configured;
2924
+ }
2925
+ for (const candidate of GLOBAL_CSS_CANDIDATES) {
2926
+ const file = join2(root, candidate);
2927
+ if (isFile2(file)) {
2928
+ result.path = file;
2929
+ break;
2578
2930
  }
2579
- const atLeast = rangeLowerAtLeast(range, TAILWIND4_MIN_AXIS_VERSION);
2580
- if (atLeast === false) out.push({ name, range, status: "below", source: "declared" });
2581
- else if (atLeast === void 0) out.push({ name, range, status: "unknown", source: "declared" });
2582
2931
  }
2583
- return out;
2584
- }
2585
- function axisPackageVersionLabel(issue) {
2586
- return issue.source === "installed" ? `${issue.name} \uC124\uCE58\uBCF8 ${issue.installed}` : `${issue.name} ${issue.range}`;
2587
- }
2588
- var declaredList = (issues) => issues.map(axisPackageVersionLabel).join(" \xB7 ");
2589
- function tailwind4VersionNote(issues) {
2590
- const below = issues.filter((i) => i.status === "below");
2591
- const unknown = issues.filter((i) => i.status === "unknown");
2592
- let note = "";
2593
- if (below.length > 0) note += ` (${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1 \uD544\uC694: ${declaredList(below)})`;
2594
- 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)})`;
2595
- return note;
2596
- }
2597
- var TAILWIND4_COMPONENT_PACKAGES = ["@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2598
- function componentTailwindImportLine(name) {
2599
- return `@import "${name}/tailwind.css";`;
2600
- }
2601
- var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2602
- function hasComponentTailwindImport(css, name) {
2603
- const re = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${escapeRegExp(name)}/tailwind\\.css["']`);
2604
- return re.test(stripCssComments(css));
2932
+ return result;
2605
2933
  }
2606
- function hasComponentSource(css, name) {
2607
- const re = new RegExp(`@source\\s+(["'])(?!@axis-ds/)[^"'\\n]*${escapeRegExp(name)}(?:[/\\\\][^"'\\n]*)?\\1`);
2608
- return re.test(stripCssComments(css));
2934
+ function findBrandImports(css) {
2935
+ const names = [];
2936
+ const pattern = /@import\s+(?:url\(\s*)?["']?@axis-ds\/tokens\/css\/brands\/([a-z][a-z0-9-]*)(?:\.css)?["']?\s*\)?\s*(?=[;\s]|$)/g;
2937
+ for (const m of stripComments(css).matchAll(pattern)) {
2938
+ names.push(m[1]);
2939
+ }
2940
+ return names;
2609
2941
  }
2610
- var CREATE_NEXT_APP_DEFAULTS = [
2611
- {
2612
- prefix: ":root \uC0C9\uAC12",
2613
- pattern: /(?<![\w-])(--(?:background|foreground))\s*:\s*(?:#[0-9a-fA-F]{3,8}\b|(?:rgba?|hsla?|hwb|oklch|oklab|lab|lch|color)\()/g,
2614
- item: (m) => m[1]
2615
- },
2616
- {
2617
- prefix: "@theme",
2618
- pattern: /(?<![\w-])(--color-(?:background|foreground))\s*:\s*var\(\s*(--(?:background|foreground))\s*\)/g,
2619
- item: (m) => `${m[1]}: var(${m[2]})`
2620
- },
2621
- {
2622
- prefix: "@theme",
2623
- pattern: /(?<![\w-])(--font-(?:sans|mono))\s*:\s*var\(\s*(--font-geist-(?:sans|mono))\s*\)/g,
2624
- item: (m) => `${m[1]}: var(${m[2]})`
2625
- },
2626
- {
2627
- prefix: "body",
2628
- pattern: /(?<![\w-])(background|background-color|color)\s*:\s*var\(\s*(--(?:background|foreground))\s*\)/g,
2629
- item: (m) => `${m[1]}: var(${m[2]})`
2942
+ var AXIS_CONFIG_FILE_NAME = "axis.config.json";
2943
+ function readBrandConfig(baseDir) {
2944
+ let parsed;
2945
+ try {
2946
+ parsed = JSON.parse(readFileSync2(join2(resolve2(baseDir), AXIS_CONFIG_FILE_NAME), "utf8"));
2947
+ } catch {
2948
+ return null;
2630
2949
  }
2631
- ];
2632
- function findCreateNextAppDefaults(css) {
2633
- const body = stripCssComments(css);
2634
- const labels = [];
2635
- for (const d of CREATE_NEXT_APP_DEFAULTS) {
2636
- const items = [];
2637
- for (const m of body.matchAll(d.pattern)) {
2638
- const item = d.item(m);
2639
- if (!items.includes(item)) items.push(item);
2640
- }
2641
- if (items.length > 0) labels.push(`${d.prefix} ${items.join(", ")}`);
2950
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
2951
+ return parsed;
2952
+ }
2953
+ function inspectBrandImport(baseDir) {
2954
+ const config = readBrandConfig(baseDir);
2955
+ const brand = config?.brand;
2956
+ if (typeof brand !== "string" || brand.trim() === "") return null;
2957
+ const root = resolve2(baseDir);
2958
+ const location = findGlobalCss(root, config);
2959
+ const base = { brand };
2960
+ if (location.configuredMissing !== void 0) base.configuredMissing = location.configuredMissing;
2961
+ if (!location.path) {
2962
+ return { ...base, state: "css-missing", imports: [] };
2642
2963
  }
2643
- return labels;
2964
+ const cssFile = relative(root, location.path).split(sep).join("/");
2965
+ const imports = [...new Set(findBrandImports(readFileSync2(location.path, "utf8")))];
2966
+ let state;
2967
+ if (imports.includes(brand)) state = "match";
2968
+ else if (imports.length === 0) state = "import-missing";
2969
+ else state = "other-brand";
2970
+ return { ...base, state, cssFile, imports };
2644
2971
  }
2645
- function stripJsComments(src) {
2646
- let out = "";
2647
- let i = 0;
2648
- const n = src.length;
2649
- while (i < n) {
2650
- const c = src[i];
2651
- const d = src[i + 1];
2652
- if (c === '"' || c === "'" || c === "`") {
2653
- let j = i + 1;
2654
- while (j < n && src[j] !== c) {
2655
- if (src[j] === "\\") j++;
2656
- else if (c !== "`" && src[j] === "\n") break;
2657
- j++;
2658
- }
2659
- out += src.slice(i, j + 1);
2660
- i = j + 1;
2661
- continue;
2662
- }
2663
- if (c === "/" && d === "/") {
2664
- const end = src.indexOf("\n", i);
2665
- i = end === -1 ? n : end;
2666
- continue;
2667
- }
2668
- if (c === "/" && d === "*") {
2669
- const end = src.indexOf("*/", i + 2);
2670
- i = end === -1 ? n : end + 2;
2671
- out += " ";
2672
- continue;
2673
- }
2674
- out += c;
2675
- i++;
2972
+ function isMissingBuildFile(err) {
2973
+ const code = errorCode(err);
2974
+ return code === "MODULE_NOT_FOUND" || code === "ENOENT";
2975
+ }
2976
+ function readBrandIndex(indexPath) {
2977
+ const data = readJsonFile(indexPath);
2978
+ if (!Array.isArray(data.brands)) {
2979
+ throw new Error(`${indexPath}: "brands" \uBC30\uC5F4\uC774 \uC5C6\uC2B5\uB2C8\uB2E4`);
2676
2980
  }
2677
- return out;
2981
+ return data.brands;
2678
2982
  }
2679
- var TOKENS_TAILWIND3_PRESET = "@axis-ds/tokens/tailwind";
2680
- var PRESET_REFERENCE = /(?:\brequire\s*\(\s*|\bimport\s*\(\s*|\bfrom\s*|\bimport\s+)(["'])@axis-ds\/tokens\/tailwind\1/;
2681
- function hasTokensPresetReference(source) {
2682
- return PRESET_REFERENCE.test(stripJsComments(source));
2983
+ function readSummary(baseDir, entry) {
2984
+ const metaPath = projectRequire(baseDir).resolve(tokensSpecifier(entry.meta));
2985
+ const meta = readJsonFile(metaPath);
2986
+ return {
2987
+ name: entry.name,
2988
+ description: typeof meta.description === "string" ? meta.description : "",
2989
+ themes: Array.isArray(meta.themes) ? meta.themes.map(String) : [],
2990
+ a11y: typeof meta.a11y === "object" && meta.a11y !== null ? meta.a11y : {},
2991
+ importPath: brandImportPath(entry)
2992
+ };
2683
2993
  }
2684
- var PRESET_PASS_SUFFIX = `\uC5D0 AXIS \uD1A0\uD070\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (${TOKENS_TAILWIND3_PRESET} preset)`;
2685
- function readPackageJson(baseDir) {
2994
+ function listBrands(baseDir) {
2995
+ const tokens = resolveTokens(baseDir);
2996
+ if (tokens.status !== "ok" || !tokens.indexPath) {
2997
+ return { status: tokens.status, version: tokens.version, brands: [] };
2998
+ }
2999
+ const entries = readBrandIndex(tokens.indexPath);
3000
+ let brands;
2686
3001
  try {
2687
- const parsed = JSON.parse(readFileSync2(join2(baseDir, "package.json"), "utf8"));
2688
- return isPlainObject(parsed) ? parsed : void 0;
2689
- } catch {
2690
- return void 0;
3002
+ brands = entries.map((entry) => readSummary(baseDir, entry));
3003
+ } catch (err) {
3004
+ if (isMissingBuildFile(err)) return { status: "not-built", version: tokens.version, brands: [] };
3005
+ throw err;
2691
3006
  }
3007
+ return { status: "ok", version: tokens.version, brands };
2692
3008
  }
2693
- function tailwind4Issues(css, pkg, versions) {
2694
- const issues = [];
2695
- const hasEntry = hasTokensTailwind4Import(css);
2696
- if (!hasEntry) issues.push(`AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${TOKENS_TAILWIND4_IMPORT_LINE}`);
2697
- const missing = TAILWIND4_COMPONENT_PACKAGES.filter(
2698
- (name) => pkg !== void 0 && hasDependency(pkg, name) !== void 0 && !hasComponentTailwindImport(css, name) && !hasComponentSource(css, name)
2699
- );
2700
- if (missing.length > 0) {
2701
- issues.push(`\uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCE94 \uACBD\uB85C(tailwind.css)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${missing.map(componentTailwindImportLine).join(" ")}`);
3009
+ function stripComments(css) {
3010
+ return css.replace(/\/\*[\s\S]*?\*\//g, "");
3011
+ }
3012
+ function parseDeclarations(body) {
3013
+ const out = {};
3014
+ for (const m of body.matchAll(/(--[A-Za-z0-9_-]+)\s*:\s*([^;]+);/g)) {
3015
+ out[m[1]] = m[2].trim();
2702
3016
  }
2703
- if (hasEntry) {
2704
- const defaults = findCreateNextAppDefaults(css);
2705
- 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`);
2706
- if (versions.some((v) => v.name === TAILWIND4_AXIS_PACKAGES[0])) {
2707
- 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`);
3017
+ return out;
3018
+ }
3019
+ function isDarkSelector(selector) {
3020
+ return /\.dark\b/.test(selector) || /\[data-theme=["']?dark["']?\]/.test(selector);
3021
+ }
3022
+ function parseBrandCss(css, name) {
3023
+ const result = { light: {}, dark: {} };
3024
+ let lightFound = false;
3025
+ let darkFound = false;
3026
+ const brandAttr = new RegExp(`\\[data-brand=["']?${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']?\\]`);
3027
+ for (const m of stripComments(css).matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
3028
+ const selectors = m[1].split(",").map((s) => s.trim());
3029
+ if (!selectors.some((s) => brandAttr.test(s))) continue;
3030
+ if (selectors.some(isDarkSelector)) {
3031
+ if (!darkFound) {
3032
+ result.dark = parseDeclarations(m[2]);
3033
+ darkFound = true;
3034
+ }
3035
+ } else if (!lightFound) {
3036
+ result.light = parseDeclarations(m[2]);
3037
+ lightFound = true;
2708
3038
  }
2709
3039
  }
2710
- for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2711
- if (hasComponentTailwindImport(css, name) && versions.some((v) => v.name === name)) {
2712
- issues.push(`${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC ${name} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2713
- }
3040
+ return result;
3041
+ }
3042
+ function pickKeyTokens(decls) {
3043
+ const out = {};
3044
+ for (const key of KEY_TOKENS) {
3045
+ if (Object.prototype.hasOwnProperty.call(decls, key)) out[key] = decls[key];
2714
3046
  }
2715
- return issues;
3047
+ return out;
2716
3048
  }
2717
- function checkTailwindSetup(baseDirInput) {
2718
- const baseDir = resolve2(baseDirInput);
2719
- const pkg = readPackageJson(baseDir);
2720
- const tailwind = detectTailwind(baseDir, pkg);
2721
- if (tailwind.major !== void 0 && tailwind.major >= 4) {
2722
- const label = `Tailwind ${tailwind.major}`;
2723
- for (const candidate of TAILWIND_CSS_CANDIDATES) {
2724
- const file = join2(baseDir, candidate);
2725
- if (!existsSync2(file)) continue;
2726
- const css = readFileSync2(file, "utf8");
2727
- if (!hasTailwindImport(css)) {
2728
- return {
2729
- name: "tailwind.config",
2730
- severity: "warn",
2731
- message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4`
2732
- };
2733
- }
2734
- const versions = axisPackagesBelowTailwind4(pkg, baseDir);
2735
- const issues = tailwind4Issues(css, pkg, versions);
2736
- if (issues.length > 0) {
2737
- return {
2738
- name: "tailwind.config",
2739
- severity: "warn",
2740
- message: `${label}: ${candidate}\uC5D0 ${issues.join(" / ")}${tailwind4VersionNote(versions)}`
2741
- };
3049
+ function providerExample(name) {
3050
+ return [
3051
+ "// 1) Provider \uC5C6\uC774: \uB8E8\uD2B8 \uB808\uC774\uC544\uC6C3(app/layout.tsx)\uC758 <html> \uC5D0 \uBD99\uC785\uB2C8\uB2E4(\uCCAB \uD654\uBA74\uBD80\uD130 \uC801\uC6A9)",
3052
+ `<html lang="ko" data-brand="${name}">`,
3053
+ "",
3054
+ "// 2) ThemeProvider \uB97C \uC4F0\uBA74 \uB9C8\uC6B4\uD2B8 \uB54C \uBE0C\uB79C\uB4DC\uAC00 \uC5C6\uC73C\uBA74 \uC18D\uC131\uC744 \uC9C0\uC6B0\uBBC0\uB85C setBrand \uB85C \uCF2D\uB2C8\uB2E4(\uC800\uC7A5\uB3FC \uC720\uC9C0, setBrand(undefined) \uB85C \uB054)",
3055
+ 'import { useTheme } from "@axis-ds/theme";',
3056
+ "const { setBrand } = useTheme();",
3057
+ `setBrand("${name}");`
3058
+ ].join("\n");
3059
+ }
3060
+ var BRAND_ACTIVATION_FILES = [
3061
+ "app/layout.tsx",
3062
+ "app/layout.jsx",
3063
+ "app/layout.js",
3064
+ "src/app/layout.tsx",
3065
+ "src/app/layout.jsx",
3066
+ "src/app/layout.js",
3067
+ "pages/_app.tsx",
3068
+ "pages/_app.jsx",
3069
+ "pages/_document.tsx",
3070
+ "pages/_document.jsx",
3071
+ "src/pages/_app.tsx",
3072
+ "src/pages/_app.jsx",
3073
+ "src/pages/_document.tsx",
3074
+ "src/pages/_document.jsx"
3075
+ ];
3076
+ var BRAND_ACTIVATION_PATTERN = /data-brand|setBrand\s*\(|defaultBrand\s*=/;
3077
+ function stripJsLikeComments(src) {
3078
+ return src.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:"'`])\/\/[^\n]*/g, "$1");
3079
+ }
3080
+ function findBrandActivation(baseDir) {
3081
+ const root = resolve2(baseDir);
3082
+ const layoutFiles = [];
3083
+ for (const candidate of BRAND_ACTIVATION_FILES) {
3084
+ const file = join2(root, candidate);
3085
+ if (!isFile2(file)) continue;
3086
+ layoutFiles.push(candidate);
3087
+ const text = stripJsLikeComments(readFileSync2(file, "utf8"));
3088
+ if (BRAND_ACTIVATION_PATTERN.test(text)) return { found: candidate, layoutFiles };
3089
+ for (const m of text.matchAll(/from\s+["']\.\/(providers?)["']/g)) {
3090
+ for (const ext of [".tsx", ".jsx", ".ts", ".js"]) {
3091
+ const rel3 = `${dirname(candidate)}/${m[1]}${ext}`;
3092
+ const p = join2(root, rel3);
3093
+ if (isFile2(p) && BRAND_ACTIVATION_PATTERN.test(stripJsLikeComments(readFileSync2(p, "utf8")))) {
3094
+ return { found: rel3, layoutFiles };
3095
+ }
2742
3096
  }
2743
- return {
2744
- name: "tailwind.config",
2745
- severity: "pass",
2746
- message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uC640 AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC2B5\uB2C8\uB2E4`
2747
- };
2748
3097
  }
2749
- return {
2750
- name: "tailwind.config",
2751
- severity: "warn",
2752
- message: `${label}: @import "tailwindcss" \uB97C \uD655\uC778\uD560 CSS \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4`
2753
- };
2754
3098
  }
2755
- const [tsFile, ...otherFiles] = TAILWIND_CONFIG_FILES;
2756
- const tsPath = join2(baseDir, tsFile);
2757
- if (existsSync2(tsPath)) {
2758
- const content = readFileSync2(tsPath, "utf8");
2759
- const hasTokens = content.includes("--background") || content.includes("hsl(var(");
2760
- if (hasTokens) {
2761
- return { name: "tailwind.config", severity: "pass", message: "tailwind.config.ts\uC5D0 AXIS \uD1A0\uD070\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4" };
2762
- }
2763
- if (hasTokensPresetReference(content)) {
2764
- return { name: "tailwind.config", severity: "pass", message: `tailwind.config.ts${PRESET_PASS_SUFFIX}` };
2765
- }
2766
- return { name: "tailwind.config", severity: "warn", message: "tailwind.config.ts\uC5D0 AXIS \uD1A0\uD070 \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" };
3099
+ return { layoutFiles };
3100
+ }
3101
+ function inspectBrandActivation(baseDir) {
3102
+ const config = readBrandConfig(baseDir);
3103
+ const brand = config?.brand;
3104
+ if (typeof brand !== "string" || brand.trim() === "") return null;
3105
+ return { brand, ...findBrandActivation(baseDir) };
3106
+ }
3107
+ function getBrand(baseDir, name) {
3108
+ const tokens = resolveTokens(baseDir);
3109
+ if (tokens.status !== "ok" || !tokens.indexPath) {
3110
+ return { status: tokens.status, version: tokens.version, brand: null, available: [] };
2767
3111
  }
2768
- for (const file of otherFiles) {
2769
- const path18 = join2(baseDir, file);
2770
- if (existsSync2(path18)) {
2771
- if (hasTokensPresetReference(readFileSync2(path18, "utf8"))) {
2772
- return { name: "tailwind.config", severity: "pass", message: `${file}${PRESET_PASS_SUFFIX}` };
2773
- }
2774
- return { name: "tailwind.config", severity: "warn", message: `${file} \uC874\uC7AC (TypeScript \uAD8C\uC7A5)` };
2775
- }
3112
+ const entries = readBrandIndex(tokens.indexPath);
3113
+ const available = entries.map((e) => e.name);
3114
+ const entry = entries.find((e) => e.name === name);
3115
+ if (!entry) {
3116
+ return { status: "ok", version: tokens.version, brand: null, available };
2776
3117
  }
2777
- return { name: "tailwind.config", severity: "fail", message: "tailwind.config \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" };
3118
+ let summary;
3119
+ let parsed;
3120
+ try {
3121
+ summary = readSummary(baseDir, entry);
3122
+ const cssPath = projectRequire(baseDir).resolve(summary.importPath);
3123
+ parsed = parseBrandCss(readFileSync2(cssPath, "utf8"), name);
3124
+ } catch (err) {
3125
+ if (isMissingBuildFile(err)) return { status: "not-built", version: tokens.version, brand: null, available: [] };
3126
+ throw err;
3127
+ }
3128
+ return {
3129
+ status: "ok",
3130
+ version: tokens.version,
3131
+ available,
3132
+ brand: {
3133
+ ...summary,
3134
+ tokens: { light: pickKeyTokens(parsed.light), dark: pickKeyTokens(parsed.dark) },
3135
+ providerExample: providerExample(name),
3136
+ adjustments: { a11y: summary.a11y, reference: BRAND_ADJUSTMENT_REFERENCE }
3137
+ }
3138
+ };
2778
3139
  }
2779
3140
 
2780
3141
  // src/template/template-check.ts
@@ -2784,7 +3145,7 @@ var SEVERITY_ICON = {
2784
3145
  fail: chalk7.red("FAIL")
2785
3146
  };
2786
3147
  async function collectCheckResults(dir) {
2787
- const baseDir = path10.resolve(dir || ".");
3148
+ const baseDir = path11.resolve(dir || ".");
2788
3149
  const results = [];
2789
3150
  results.push(await checkFileExists(baseDir, "axis.config.json", "axis.config.json"));
2790
3151
  results.push(checkTailwindSetup(baseDir));
@@ -2793,6 +3154,12 @@ async function collectCheckResults(dir) {
2793
3154
  results.push(await checkDependencies(baseDir));
2794
3155
  const brand = checkBrand(baseDir);
2795
3156
  if (brand) results.push(brand);
3157
+ const activation = inspectBrandActivation(baseDir);
3158
+ if (activation) results.push(brandActivationResult(activation));
3159
+ const content = checkTailwindContent(baseDir);
3160
+ if (content) results.push(content);
3161
+ const animation = checkAnimationPlugin(baseDir);
3162
+ if (animation) results.push(animation);
2796
3163
  return results;
2797
3164
  }
2798
3165
  async function runCheck(dir) {
@@ -2829,7 +3196,7 @@ async function runCheck(dir) {
2829
3196
  return { pass: passCount, warn: warnCount, fail: failCount };
2830
3197
  }
2831
3198
  async function checkFileExists(baseDir, fileName, label) {
2832
- const exists = await fs10.pathExists(path10.join(baseDir, fileName));
3199
+ const exists = await fs10.pathExists(path11.join(baseDir, fileName));
2833
3200
  return {
2834
3201
  name: label,
2835
3202
  severity: exists ? "pass" : "fail",
@@ -2845,7 +3212,7 @@ async function checkUtils(baseDir) {
2845
3212
  "utils.ts"
2846
3213
  ];
2847
3214
  for (const candidate of candidates) {
2848
- const fullPath = path10.join(baseDir, candidate);
3215
+ const fullPath = path11.join(baseDir, candidate);
2849
3216
  if (await fs10.pathExists(fullPath)) {
2850
3217
  const content = await fs10.readFile(fullPath, "utf-8");
2851
3218
  const hasCn = content.includes("function cn");
@@ -2863,7 +3230,7 @@ async function checkUtils(baseDir) {
2863
3230
  };
2864
3231
  }
2865
3232
  async function checkDependencies(baseDir) {
2866
- const pkgPath = path10.join(baseDir, "package.json");
3233
+ const pkgPath = path11.join(baseDir, "package.json");
2867
3234
  if (!await fs10.pathExists(pkgPath)) {
2868
3235
  return {
2869
3236
  name: "package.json",
@@ -2931,6 +3298,21 @@ function brandCheckResult(check) {
2931
3298
  };
2932
3299
  }
2933
3300
  }
3301
+ function brandActivationResult(check) {
3302
+ if (check.found) {
3303
+ return {
3304
+ name: "brand.activation",
3305
+ severity: "pass",
3306
+ message: `${check.found}\uC5D0 \uBE0C\uB79C\uB4DC\uB97C \uCF1C\uB294 \uCF54\uB4DC(data-brand \xB7 setBrand \uB4F1)\uAC00 \uC788\uC2B5\uB2C8\uB2E4`
3307
+ };
3308
+ }
3309
+ const where = check.layoutFiles.length > 0 ? check.layoutFiles.join(" \xB7 ") : "\uB8E8\uD2B8 \uB808\uC774\uC544\uC6C3(app/layout.tsx \uB4F1)";
3310
+ return {
3311
+ name: "brand.activation",
3312
+ severity: "warn",
3313
+ message: `brand "${check.brand}" \uAC00 \uC124\uC815\uB410\uC9C0\uB9CC ${where}\uC5D0 \uBE0C\uB79C\uB4DC\uB97C \uCF1C\uB294 \uCF54\uB4DC(data-brand \xB7 setBrand \uB4F1)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(ThemeProvider \uB9CC\uC73C\uB85C\uB294 \uB9C8\uC6B4\uD2B8 \uB54C \uAEBC\uC9D0) \u2192 <html data-brand="${check.brand}"> \uB610\uB294 setBrand("${check.brand}")`
3314
+ };
3315
+ }
2934
3316
 
2935
3317
  // src/template/index.ts
2936
3318
  function registerTemplateCommand(program2) {
@@ -3042,32 +3424,10 @@ import path13 from "path";
3042
3424
  // src/theme/theme-add.ts
3043
3425
  import { accessSync, constants, existsSync as existsSync4, readFileSync as readFileSync3, realpathSync, unlinkSync, writeFileSync } from "fs";
3044
3426
  import path12 from "path";
3045
-
3046
- // ../axis-registry-core/src/project.ts
3047
- import { existsSync as existsSync3 } from "fs";
3048
- import path11 from "path";
3049
- function detectInstallCommand(baseDir) {
3050
- if (existsSync3(path11.join(baseDir, "pnpm-lock.yaml"))) return "pnpm install";
3051
- if (existsSync3(path11.join(baseDir, "yarn.lock"))) return "yarn install";
3052
- if (existsSync3(path11.join(baseDir, "package-lock.json"))) return "npm install";
3053
- return "pnpm install";
3054
- }
3055
- function hasDependency2(pkg, name) {
3056
- for (const field of ["dependencies", "devDependencies"]) {
3057
- const deps = pkg[field];
3058
- if (typeof deps === "object" && deps !== null && name in deps) {
3059
- return String(deps[name]);
3060
- }
3061
- }
3062
- return void 0;
3063
- }
3064
- function isPlainObject2(value) {
3065
- return typeof value === "object" && value !== null && !Array.isArray(value);
3066
- }
3067
-
3068
- // src/theme/theme-add.ts
3069
- var TOKENS_DEPENDENCY_RANGE = `^${MIN_BRAND_TOKENS_VERSION}`;
3427
+ var TOKENS_DEPENDENCY_RANGE = `^${TOKENS_DEPENDENCY_MIN_VERSION}`;
3428
+ var TAILWIND3_BASE_IMPORTS = [`${TOKENS_PACKAGE}/css`, `${TOKENS_PACKAGE}/css/shadcn`];
3070
3429
  var THEME_PACKAGE = "@axis-ds/theme";
3430
+ var THEME_MIN_VERSION = "1.3.0";
3071
3431
  function readText(file) {
3072
3432
  return existsSync4(file) ? readFileSync3(file, "utf8") : null;
3073
3433
  }
@@ -3120,6 +3480,31 @@ function insertImportLine(css, line) {
3120
3480
  lines.splice(insertAt, 0, line);
3121
3481
  return bom + lines.join(eol);
3122
3482
  }
3483
+ function insertImportLines(css, lines) {
3484
+ if (lines.length === 0) return css;
3485
+ const body = css.startsWith("\uFEFF") ? css.slice(1) : css;
3486
+ return insertImportLine(css, lines.join(eolOf(body)));
3487
+ }
3488
+ function findLateImports(css) {
3489
+ const body = css.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "));
3490
+ const lines = body.split(/\r?\n/);
3491
+ let rulesStarted = false;
3492
+ const late = [];
3493
+ for (const raw of lines) {
3494
+ const line = raw.trim();
3495
+ if (line === "") continue;
3496
+ if (/^@(charset|import)\b/i.test(line)) {
3497
+ if (rulesStarted && /^@import\b/i.test(line)) late.push(line);
3498
+ continue;
3499
+ }
3500
+ rulesStarted = true;
3501
+ }
3502
+ return late;
3503
+ }
3504
+ function hasCssImport(css, specifier) {
3505
+ const esc = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3506
+ return new RegExp(`@import\\s+(?:url\\(\\s*)?["']${esc}(?:\\.css)?["']`).test(css.replace(/\/\*[\s\S]*?\*\//g, ""));
3507
+ }
3123
3508
  function compareVersions(a, b) {
3124
3509
  const parse = (v) => {
3125
3510
  const m = /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(v.trim());
@@ -3194,6 +3579,28 @@ function planThemeAdd(baseDirInput, brand) {
3194
3579
  } else {
3195
3580
  plan.unverifiedBrand = true;
3196
3581
  }
3582
+ const pkgFile = path12.join(baseDir, "package.json");
3583
+ const pkgText = readText(pkgFile);
3584
+ let pkg;
3585
+ if (pkgText !== null) {
3586
+ try {
3587
+ const parsed = JSON.parse(pkgText);
3588
+ if (!isPlainObject2(parsed)) throw new Error("\uCD5C\uC0C1\uC704\uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
3589
+ pkg = parsed;
3590
+ } catch (err) {
3591
+ plan.errors.push({ kind: "invalid-package-json", message: err.message });
3592
+ plan.writes = [];
3593
+ return plan;
3594
+ }
3595
+ }
3596
+ const tailwind = detectTailwind(baseDir, pkg);
3597
+ const tailwind4 = tailwind.major !== void 0 && tailwind.major >= 4;
3598
+ const requiredBaseImports = tailwind4 ? [
3599
+ TOKENS_TAILWIND4_ENTRY,
3600
+ ...TAILWIND4_COMPONENT_PACKAGES.filter((n) => pkg !== void 0 && hasDependency2(pkg, n) !== void 0).map(
3601
+ (n) => `${n}/tailwind.css`
3602
+ )
3603
+ ] : [...TAILWIND3_BASE_IMPORTS];
3197
3604
  const location = findGlobalCss(baseDir, config);
3198
3605
  if (location.path) {
3199
3606
  const where = realPathInside(baseDir, location.path);
@@ -3210,45 +3617,62 @@ function planThemeAdd(baseDirInput, brand) {
3210
3617
  const before = readFileSync3(location.path, "utf8");
3211
3618
  const imported = findBrandImports(before);
3212
3619
  const otherBrands = [...new Set(imported.filter((n) => n !== brand))];
3213
- if (imported.includes(brand)) {
3214
- plan.css = { file: location.path, configuredMissing: location.configuredMissing, action: "exists", otherBrands };
3215
- } else {
3216
- plan.css = { file: location.path, configuredMissing: location.configuredMissing, action: "add", otherBrands };
3217
- plan.writes.push({
3218
- file: location.path,
3219
- before,
3220
- after: insertImportLine(before, `@import "${plan.importPath}";`)
3221
- });
3620
+ const brandAdded = !imported.includes(brand);
3621
+ const baseImports = requiredBaseImports.filter((spec) => !hasCssImport(before, spec));
3622
+ const lines = [...baseImports.map((spec) => `@import "${spec}";`), ...brandAdded ? [`@import "${plan.importPath}";`] : []];
3623
+ plan.css = {
3624
+ file: location.path,
3625
+ configuredMissing: location.configuredMissing,
3626
+ action: lines.length > 0 ? "add" : "exists",
3627
+ otherBrands,
3628
+ brandAdded,
3629
+ baseImports,
3630
+ requiredBaseImports,
3631
+ lateImports: findLateImports(before),
3632
+ tailwind4
3633
+ };
3634
+ if (lines.length > 0) {
3635
+ plan.writes.push({ file: location.path, before, after: insertImportLines(before, lines) });
3222
3636
  }
3223
3637
  } else {
3224
- plan.css = { configuredMissing: location.configuredMissing, action: "manual", otherBrands: [] };
3638
+ plan.css = {
3639
+ configuredMissing: location.configuredMissing,
3640
+ action: "manual",
3641
+ otherBrands: [],
3642
+ brandAdded: true,
3643
+ baseImports: requiredBaseImports,
3644
+ requiredBaseImports,
3645
+ lateImports: [],
3646
+ tailwind4
3647
+ };
3225
3648
  }
3226
- const pkgFile = path12.join(baseDir, "package.json");
3227
- const pkgText = readText(pkgFile);
3649
+ if (!tailwind4) {
3650
+ const configFile2 = TAILWIND_CONFIG_FILES.find((f) => existsSync4(path12.join(baseDir, f)));
3651
+ const content = inspectTailwindContent(baseDir);
3652
+ plan.tailwindConfig = {
3653
+ configFile: configFile2,
3654
+ presetMissing: configFile2 === void 0 || !hasTokensPresetReference(readFileSync3(path12.join(baseDir, configFile2), "utf8")),
3655
+ contentMissing: content?.targets.filter((t) => !t.covered) ?? [],
3656
+ contentUnknown: content !== null && (content.unreadable || content.dynamic)
3657
+ };
3658
+ }
3659
+ plan.activation = findBrandActivation(baseDir);
3228
3660
  const installed = plan.tokens.version;
3229
3661
  const upgrade = installed !== void 0 && (compareVersions(installed, MIN_BRAND_TOKENS_VERSION) ?? 0) < 0;
3230
- if (pkgText === null) {
3231
- plan.dependency = { action: "no-package-json", upgrade, themeMissing: false };
3662
+ const tokensAddCommand = addCommandFor(plan.installCommand, { [TOKENS_PACKAGE]: TOKENS_DEPENDENCY_RANGE });
3663
+ const themeAddCommand = addCommandFor(plan.installCommand, { [THEME_PACKAGE]: `^${THEME_MIN_VERSION}` });
3664
+ if (pkgText === null || pkg === void 0) {
3665
+ plan.dependency = { action: "no-package-json", upgrade, themeMissing: false, tokensAddCommand, themeAddCommand };
3232
3666
  } else {
3233
- let pkg;
3234
- try {
3235
- const parsed = JSON.parse(pkgText);
3236
- if (!isPlainObject2(parsed)) throw new Error("\uCD5C\uC0C1\uC704\uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
3237
- pkg = parsed;
3238
- } catch (err) {
3239
- plan.errors.push({ kind: "invalid-package-json", message: err.message });
3240
- plan.writes = [];
3241
- return plan;
3242
- }
3243
3667
  const themeMissing = hasDependency2(pkg, THEME_PACKAGE) === void 0;
3244
3668
  const declared = hasDependency2(pkg, TOKENS_PACKAGE);
3245
3669
  if (declared !== void 0) {
3246
- plan.dependency = { action: "present", declaredRange: declared, upgrade, themeMissing };
3670
+ plan.dependency = { action: "present", declaredRange: declared, upgrade, themeMissing, tokensAddCommand, themeAddCommand };
3247
3671
  } else {
3248
3672
  const deps = isPlainObject2(pkg.dependencies) ? { ...pkg.dependencies } : {};
3249
3673
  deps[TOKENS_PACKAGE] = TOKENS_DEPENDENCY_RANGE;
3250
3674
  const next = { ...pkg, dependencies: deps };
3251
- plan.dependency = { action: "add", upgrade, themeMissing };
3675
+ plan.dependency = { action: "add", upgrade, themeMissing, tokensAddCommand, themeAddCommand };
3252
3676
  plan.writes.push({ file: pkgFile, before: pkgText, after: formatJsonLike(pkgText, next) });
3253
3677
  }
3254
3678
  }
@@ -3330,33 +3754,36 @@ function applyThemeAdd(plan) {
3330
3754
  }
3331
3755
 
3332
3756
  // src/theme/index.ts
3333
- var TOKENS_INSTALL_COMMAND = `pnpm add ${TOKENS_PACKAGE}@^${MIN_BRAND_TOKENS_VERSION}`;
3334
- function tokensStatusGuide(status, version) {
3757
+ function tokensInstallCommand(baseDir) {
3758
+ return addCommandFor(detectInstallCommand(baseDir), { [TOKENS_PACKAGE]: TOKENS_DEPENDENCY_RANGE });
3759
+ }
3760
+ var TOKENS_INSTALL_COMMAND = addCommandFor("pnpm install", { [TOKENS_PACKAGE]: TOKENS_DEPENDENCY_RANGE });
3761
+ function tokensStatusGuide(status, version, installCommand = TOKENS_INSTALL_COMMAND) {
3335
3762
  switch (status) {
3336
3763
  case "not-installed":
3337
3764
  return [
3338
3765
  chalk9.yellow(`${TOKENS_PACKAGE} \uAC00 \uC124\uCE58\uB3FC \uC788\uC9C0 \uC54A\uC544 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`),
3339
- ` \uC124\uCE58: ${chalk9.cyan(TOKENS_INSTALL_COMMAND)}`
3766
+ ` \uC124\uCE58: ${chalk9.cyan(installCommand)}`
3340
3767
  ];
3341
3768
  case "unsupported":
3342
3769
  return [
3343
3770
  chalk9.yellow(
3344
3771
  `${TOKENS_PACKAGE} ${version ?? "(\uBC84\uC804 \uBAA8\uB984)"} \uC740 \uBE0C\uB79C\uB4DC\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(${MIN_BRAND_TOKENS_VERSION} \uC774\uC0C1 \uD544\uC694).`
3345
3772
  ),
3346
- ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(TOKENS_INSTALL_COMMAND)}`
3773
+ ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(installCommand)}`
3347
3774
  ];
3348
3775
  case "not-built":
3349
3776
  return [
3350
3777
  chalk9.yellow(
3351
3778
  `${TOKENS_PACKAGE}${version ? ` ${version}` : ""} \uB294 \uC788\uC9C0\uB9CC \uBE4C\uB4DC \uC0B0\uCD9C\uBB3C\uC774 \uC5C6\uC5B4 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
3352
3779
  ),
3353
- ` \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uBA74 ${chalk9.cyan(`pnpm --filter ${TOKENS_PACKAGE} build`)}, \uC544\uB2C8\uBA74 \uC7AC\uC124\uCE58: ${chalk9.cyan(TOKENS_INSTALL_COMMAND)}`
3780
+ ` \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uBA74 ${chalk9.cyan(`pnpm --filter ${TOKENS_PACKAGE} build`)}, \uC544\uB2C8\uBA74 \uC7AC\uC124\uCE58: ${chalk9.cyan(installCommand)}`
3354
3781
  ];
3355
3782
  }
3356
3783
  }
3357
- function renderThemeList(result) {
3784
+ function renderThemeList(result, installCommand) {
3358
3785
  if (result.status !== "ok") {
3359
- return { lines: ["", ...tokensStatusGuide(result.status, result.version), ""], exitCode: 1 };
3786
+ return { lines: ["", ...tokensStatusGuide(result.status, result.version, installCommand), ""], exitCode: 1 };
3360
3787
  }
3361
3788
  if (result.brands.length === 0) {
3362
3789
  return {
@@ -3401,9 +3828,9 @@ function tokenTable(brand) {
3401
3828
  }
3402
3829
  return lines;
3403
3830
  }
3404
- function renderThemeInfo(result, name) {
3831
+ function renderThemeInfo(result, name, installCommand) {
3405
3832
  if (result.status !== "ok") {
3406
- return { lines: ["", ...tokensStatusGuide(result.status, result.version), ""], exitCode: 1 };
3833
+ return { lines: ["", ...tokensStatusGuide(result.status, result.version, installCommand), ""], exitCode: 1 };
3407
3834
  }
3408
3835
  const brand = result.brand;
3409
3836
  if (!brand) {
@@ -3427,7 +3854,7 @@ function renderThemeInfo(result, name) {
3427
3854
  chalk9.bold("Import (globals.css, @charset \xB7 \uB2E4\uB978 @import \uB2E4\uC74C)"),
3428
3855
  ` @import "${brand.importPath}";`,
3429
3856
  "",
3430
- chalk9.bold("Provider (@axis-ds/theme 1.3.0 \uC774\uC0C1)"),
3857
+ chalk9.bold("\uBE0C\uB79C\uB4DC \uCF1C\uAE30 (ThemeProvider \uB294 @axis-ds/theme 1.3.0 \uC774\uC0C1)"),
3431
3858
  ...brand.providerExample.split("\n").map((l) => l ? ` ${l}` : ""),
3432
3859
  chalk9.gray(
3433
3860
  ` ThemeProvider \uB098 <html data-brand="${brand.name}"> \uAC00 \uC5C6\uC73C\uBA74 import \uB9CC\uC73C\uB85C\uB294 \uD654\uBA74\uC774 \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`
@@ -3512,14 +3939,31 @@ function renderThemeAdd(plan, written) {
3512
3939
  )
3513
3940
  );
3514
3941
  }
3942
+ const brandLine = `@import "${plan.importPath}";`;
3515
3943
  if (css?.action === "add" && css.file) {
3516
- lines.push(` ${chalk9.green("\u2713")} ${rel(plan, css.file)}: @import "${plan.importPath}"; \uCD94\uAC00`);
3944
+ for (const spec of css.baseImports) {
3945
+ lines.push(` ${chalk9.green("\u2713")} ${rel(plan, css.file)}: @import "${spec}"; \uCD94\uAC00(\uAE30\uBCF8 \uD1A0\uD070 CSS, \uBE0C\uB79C\uB4DC \uC55E)`);
3946
+ }
3947
+ if (css.brandAdded) lines.push(` ${chalk9.green("\u2713")} ${rel(plan, css.file)}: ${brandLine} \uCD94\uAC00`);
3948
+ else lines.push(` ${chalk9.gray("-")} ${rel(plan, css.file)}: \uBE0C\uB79C\uB4DC import \uAC00 \uC774\uBBF8 \uC788\uC5B4 \uAC74\uB108\uB700`);
3517
3949
  } else if (css?.action === "exists" && css.file) {
3518
3950
  lines.push(` ${chalk9.gray("-")} ${rel(plan, css.file)}: import \uAC00 \uC774\uBBF8 \uC788\uC5B4 \uAC74\uB108\uB700`);
3519
3951
  } else {
3520
3952
  lines.push(
3521
- chalk9.yellow(" ! CSS \uC9C4\uC785\uC810(globalCss \xB7 app/globals.css \xB7 src/app/globals.css \xB7 globals.css)\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."),
3522
- ` \uC804\uC5ED CSS \uB9E8 \uC55E(@charset \xB7 \uB2E4\uB978 @import \uB2E4\uC74C)\uC5D0 \uC9C1\uC811 \uB123\uC73C\uC138\uC694: @import "${plan.importPath}";`
3953
+ chalk9.yellow(
3954
+ " ! CSS \uC9C4\uC785\uC810(globalCss \xB7 app/globals.css \xB7 src/app/globals.css \xB7 styles/globals.css \xB7 src/styles/globals.css \xB7 globals.css)\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."
3955
+ ),
3956
+ ` \uC804\uC5ED CSS \uB9E8 \uC55E(@charset \xB7 \uB2E4\uB978 @import \uB2E4\uC74C)\uC5D0 \uC774 \uC21C\uC11C\uB85C \uC9C1\uC811 \uB123\uC73C\uC138\uC694: ${[
3957
+ ...(css?.requiredBaseImports ?? []).map((spec) => `@import "${spec}";`),
3958
+ brandLine
3959
+ ].join(" ")}`
3960
+ );
3961
+ }
3962
+ if (css && css.lateImports.length > 0) {
3963
+ lines.push(
3964
+ chalk9.yellow(
3965
+ ` ! \uADDC\uCE59(@tailwind \uB4F1) \uB4A4\uC5D0 \uC788\uB294 @import ${css.lateImports.length}\uC904\uC740 CSS \uADDC\uCE59\uC0C1 \uBB34\uC2DC\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4(Next.js \uB294 \uC55E\uC73C\uB85C \uB04C\uC5B4\uC62C\uB824 \uC801\uC6A9). \uB9E8 \uC55E\uC73C\uB85C \uC62E\uAE30\uC138\uC694: ${css.lateImports.join(" ")}`
3966
+ )
3523
3967
  );
3524
3968
  }
3525
3969
  if (css && css.otherBrands.length > 0) {
@@ -3538,16 +3982,14 @@ function renderThemeAdd(plan, written) {
3538
3982
  } else if (dep?.action === "present") {
3539
3983
  lines.push(` ${chalk9.gray("-")} package.json: ${TOKENS_PACKAGE} ${dep.declaredRange} \uAC00 \uC774\uBBF8 \uC788\uC5B4 \uBC94\uC704\uB97C \uBC14\uAFB8\uC9C0 \uC54A\uC74C`);
3540
3984
  } else if (dep?.action === "no-package-json") {
3541
- lines.push(
3542
- chalk9.yellow(` ! package.json \uC774 \uC5C6\uC5B4 \uC758\uC874\uC131\uC744 \uCD94\uAC00\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58: ${TOKENS_INSTALL_COMMAND}`)
3543
- );
3985
+ lines.push(chalk9.yellow(` ! package.json \uC774 \uC5C6\uC5B4 \uC758\uC874\uC131\uC744 \uCD94\uAC00\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58: ${dep.tokensAddCommand}`));
3544
3986
  }
3545
3987
  if (dep?.upgrade) {
3546
3988
  lines.push(
3547
3989
  chalk9.yellow(
3548
3990
  ` ! \uC124\uCE58\uB41C ${TOKENS_PACKAGE} ${plan.tokens.version} \uC740 \uBE0C\uB79C\uB4DC\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(${MIN_BRAND_TOKENS_VERSION} \uC774\uC0C1 \uD544\uC694).`
3549
3991
  ),
3550
- ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(TOKENS_INSTALL_COMMAND)}`
3992
+ ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(dep.tokensAddCommand)}`
3551
3993
  );
3552
3994
  }
3553
3995
  if (plan.configChanged) {
@@ -3555,16 +3997,36 @@ function renderThemeAdd(plan, written) {
3555
3997
  } else {
3556
3998
  lines.push(` ${chalk9.gray("-")} axis.config.json: brand \uAC00 \uC774\uBBF8 "${plan.brand}"`);
3557
3999
  }
3558
- lines.push(
3559
- "",
3560
- chalk9.bold(`Provider (${THEME_PACKAGE} 1.3.0 \uC774\uC0C1)`),
3561
- ...providerExample(plan.brand).split("\n").map((l) => l ? ` ${l}` : ""),
3562
- chalk9.gray(
3563
- ` ThemeProvider \uB098 <html data-brand="${plan.brand}"> \uAC00 \uC5C6\uC73C\uBA74 import \uB9CC\uC73C\uB85C\uB294 \uD654\uBA74\uC774 \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`
3564
- )
3565
- );
4000
+ const tw = plan.tailwindConfig;
4001
+ if (tw && (tw.presetMissing || tw.contentMissing.length > 0)) {
4002
+ lines.push("", chalk9.bold(`Tailwind \uC124\uC815 (${tw.configFile ?? "tailwind.config"}, \uD30C\uC77C\uC740 \uACE0\uCE58\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4)`));
4003
+ if (tw.presetMissing) {
4004
+ lines.push(
4005
+ chalk9.yellow(` ! AXIS preset \uC774 \uC5C6\uC2B5\uB2C8\uB2E4. presets \uC5D0 \uB123\uC73C\uC138\uC694: presets: [require("${TOKENS_TAILWIND3_PRESET}")]`) + chalk9.gray(` (ESM \uC774\uBA74 import axisPreset from "${TOKENS_TAILWIND3_PRESET}")`)
4006
+ );
4007
+ }
4008
+ for (const t of tw.contentMissing) {
4009
+ lines.push(
4010
+ chalk9.yellow(
4011
+ ` ! 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}"`
4012
+ )
4013
+ );
4014
+ }
4015
+ }
4016
+ const act = plan.activation;
4017
+ lines.push("", chalk9.bold(`\uBE0C\uB79C\uB4DC \uCF1C\uAE30 (ThemeProvider \uB294 ${THEME_PACKAGE} ${THEME_MIN_VERSION} \uC774\uC0C1)`));
4018
+ if (act?.found) {
4019
+ lines.push(` ${chalk9.gray("-")} ${act.found} \uC5D0 \uBE0C\uB79C\uB4DC\uB97C \uCF1C\uB294 \uCF54\uB4DC(data-brand \xB7 setBrand \uB4F1)\uAC00 \uC788\uC2B5\uB2C8\uB2E4. "${plan.brand}" \uB85C \uCF1C\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.`);
4020
+ } else {
4021
+ lines.push(
4022
+ ...providerExample(plan.brand).split("\n").map((l) => l ? ` ${l}` : ""),
4023
+ chalk9.yellow(
4024
+ ` ! ${act && act.layoutFiles.length > 0 ? act.layoutFiles.join(" \xB7 ") : "\uB8E8\uD2B8 \uB808\uC774\uC544\uC6C3"} \uC5D0\uC11C \uBE0C\uB79C\uB4DC\uB97C \uCF1C\uB294 \uCF54\uB4DC(data-brand \xB7 setBrand \uB4F1)\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uB458 \uB2E4 \uC5C6\uC73C\uBA74 import \uB9CC\uC73C\uB85C\uB294 \uD654\uBA74\uC774 \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`
4025
+ )
4026
+ );
4027
+ }
3566
4028
  if (dep?.themeMissing) {
3567
- lines.push(` ${THEME_PACKAGE} \uC124\uCE58: ${chalk9.cyan(`pnpm add ${THEME_PACKAGE}@^1.3.0`)}`);
4029
+ lines.push(` ThemeProvider \uB97C \uC4F0\uBA74 ${THEME_PACKAGE} \uC124\uCE58: ${chalk9.cyan(dep.themeAddCommand)}`);
3568
4030
  }
3569
4031
  lines.push("", chalk9.gray(written.length > 0 ? `\uD30C\uC77C ${written.length}\uAC1C\uB97C \uBC14\uAFE8\uC2B5\uB2C8\uB2E4.` : "\uBC14\uAFBC \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."), "");
3570
4032
  return { lines, exitCode: 0 };
@@ -3598,11 +4060,11 @@ function registerThemeCommand(program2) {
3598
4060
  const themeCmd = program2.command("theme").description("\uBE0C\uB79C\uB4DC \uD14C\uB9C8 (@axis-ds/tokens \uBE0C\uB79C\uB4DC)");
3599
4061
  themeCmd.command("list").alias("ls").description("\uC124\uCE58\uB41C @axis-ds/tokens \uC758 \uBE0C\uB79C\uB4DC \uBAA9\uB85D").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action((options) => {
3600
4062
  const baseDir = path13.resolve(options.dir || ".");
3601
- print(renderThemeList(listBrands(baseDir)));
4063
+ print(renderThemeList(listBrands(baseDir), tokensInstallCommand(baseDir)));
3602
4064
  });
3603
4065
  themeCmd.command("info <brand>").description("\uBE0C\uB79C\uB4DC \uC0C1\uC138: import \uACBD\uB85C \xB7 Provider \uC608\uC2DC \xB7 \uC8FC\uC694 \uD1A0\uD070 \xB7 \uBCF4\uC815 \uB0B4\uC5ED").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action((name, options) => {
3604
4066
  const baseDir = path13.resolve(options.dir || ".");
3605
- print(renderThemeInfo(getBrand(baseDir, name), name));
4067
+ print(renderThemeInfo(getBrand(baseDir, name), name, tokensInstallCommand(baseDir)));
3606
4068
  });
3607
4069
  themeCmd.command("add <brand>").description("\uBE0C\uB79C\uB4DC \uC801\uC6A9: CSS import \xB7 package.json \uC758\uC874\uC131 \xB7 axis.config.json brand (\uC124\uCE58\uB294 \uC548\uB0B4\uB9CC)").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action((name, options) => {
3608
4070
  const baseDir = path13.resolve(options.dir || ".");
@@ -3925,13 +4387,13 @@ async function fetchRegistryJson(url, options = {}) {
3925
4387
  }
3926
4388
 
3927
4389
  // src/init/init.ts
3928
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync6, lstatSync as lstatSync2, readFileSync as readFileSync4, realpathSync as realpathSync3, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
4390
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync6, lstatSync as lstatSync3, readFileSync as readFileSync4, realpathSync as realpathSync3, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
3929
4391
  import path15 from "path";
3930
4392
  import chalk10 from "chalk";
3931
4393
  import fs12 from "fs-extra";
3932
4394
 
3933
4395
  // ../axis-registry-core/src/add/safe-write.ts
3934
- import { constants as fsConstants, existsSync as existsSync5, lstatSync, realpathSync as realpathSync2, statSync as statSync2 } from "fs";
4396
+ import { constants as fsConstants, existsSync as existsSync5, lstatSync as lstatSync2, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
3935
4397
  import path14 from "path";
3936
4398
  import fs11 from "fs-extra";
3937
4399
  var NEW_FILE_FLAGS = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | (fsConstants.O_NOFOLLOW ?? 0);
@@ -3969,7 +4431,7 @@ function escapeReason(root, target, options = {}) {
3969
4431
  }
3970
4432
  let st = null;
3971
4433
  try {
3972
- st = lstatSync(target);
4434
+ st = lstatSync2(target);
3973
4435
  } catch {
3974
4436
  st = null;
3975
4437
  }
@@ -3987,7 +4449,7 @@ function escapeReason(root, target, options = {}) {
3987
4449
  }
3988
4450
  let followed;
3989
4451
  try {
3990
- followed = statSync2(realTarget);
4452
+ followed = statSync3(realTarget);
3991
4453
  } catch {
3992
4454
  return `\uAE30\uC874 \uD30C\uC77C \uC815\uBCF4\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${target})`;
3993
4455
  }
@@ -4003,7 +4465,7 @@ function escapeReason(root, target, options = {}) {
4003
4465
  }
4004
4466
  function isSymlink(p) {
4005
4467
  try {
4006
- return lstatSync(p).isSymbolicLink();
4468
+ return lstatSync2(p).isSymbolicLink();
4007
4469
  } catch {
4008
4470
  return false;
4009
4471
  }
@@ -4019,7 +4481,7 @@ function outsideLinkTarget(root, target) {
4019
4481
  }
4020
4482
  function hardLinkCount(target) {
4021
4483
  try {
4022
- const st = lstatSync(target);
4484
+ const st = lstatSync2(target);
4023
4485
  return st.isFile() && st.nlink >= 2 ? st.nlink : null;
4024
4486
  } catch {
4025
4487
  return null;
@@ -4150,11 +4612,45 @@ export function cn(...inputs: ClassValue[]) {
4150
4612
  return twMerge(clsx(inputs));
4151
4613
  }
4152
4614
  `;
4153
- var INIT_DEFAULTS = {
4154
- componentsDir: "./src/components/ui",
4155
- tailwindConfig: "./tailwind.config.ts",
4156
- globalCss: "./src/app/globals.css"
4157
- };
4615
+ async function resolveInitAnswers(baseDirInput, tailwind4) {
4616
+ const baseDir = path15.resolve(baseDirInput);
4617
+ const tw4 = tailwind4 ?? isTailwind4(planUtilsDependencies(baseDir).tailwind);
4618
+ const current = await existingConfig(baseDir);
4619
+ const found = detectProjectPaths(baseDir);
4620
+ const notes = [];
4621
+ const currentComponents = nonEmptyString(current?.componentsDir);
4622
+ const componentsDir = currentComponents ?? found.componentsDir;
4623
+ const pickFile = (key, value, source, label) => {
4624
+ const existing = nonEmptyString(current?.[key]);
4625
+ if (existing !== void 0 && projectFileExists(baseDir, existing)) return existing;
4626
+ if (existing !== void 0) {
4627
+ if (source === "found") {
4628
+ notes.push(`! \uAE30\uC874 ${key}(${existing})\uAC00 \uC5C6\uC5B4 \uCC3E\uC740 \uD30C\uC77C ${value} \uB85C \uBC14\uAFC9\uB2C8\uB2E4.`);
4629
+ return value;
4630
+ }
4631
+ notes.push(`! \uAE30\uC874 ${key}(${existing}) \uD30C\uC77C\uC774 \uC5C6\uACE0 \uB2E4\uB978 ${label} \uD30C\uC77C\uB3C4 \uCC3E\uC9C0 \uBABB\uD574 \uADF8\uB300\uB85C \uB461\uB2C8\uB2E4. \uD30C\uC77C \uC704\uCE58\uC5D0 \uB9DE\uAC8C axis.config.json \uC744 \uACE0\uCE58\uC138\uC694.`);
4632
+ return existing;
4633
+ } else if (source === "default") {
4634
+ notes.push(`! ${label} \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD574 \uAE30\uBCF8\uAC12 ${value} \uB97C \uAE30\uB85D\uD569\uB2C8\uB2E4. \uD30C\uC77C \uC704\uCE58\uC5D0 \uB9DE\uAC8C axis.config.json \uC744 \uACE0\uCE58\uC138\uC694.`);
4635
+ }
4636
+ return value;
4637
+ };
4638
+ const globalCss = pickFile("globalCss", found.globalCss, found.globalCssSource, "\uC804\uC5ED CSS");
4639
+ const answers = { componentsDir, globalCss };
4640
+ if (!tw4) {
4641
+ answers.tailwindConfig = pickFile("tailwindConfig", found.tailwindConfig, found.tailwindConfigSource, "Tailwind \uC124\uC815");
4642
+ }
4643
+ return { answers, notes };
4644
+ }
4645
+ function animationGuideLine(pkg, tailwind4, installCommand) {
4646
+ const name = tailwind4 ? ANIMATION_PLUGIN_TAILWIND4 : ANIMATION_PLUGIN_TAILWIND3;
4647
+ if (pkg !== void 0 && (hasDependency2(pkg, ANIMATION_PLUGIN_TAILWIND3) !== void 0 || hasDependency2(pkg, ANIMATION_PLUGIN_TAILWIND4) !== void 0)) {
4648
+ return void 0;
4649
+ }
4650
+ const add = `${addCommandFor(installCommand, {}).trimEnd()} ${name}`;
4651
+ const where = tailwind4 ? `\uC804\uC5ED CSS \uC5D0 @import "${name}"; \uB97C \uB123\uC73C\uC138\uC694` : `tailwind.config \uC758 plugins \uC5D0 require("${name}") \uB97C \uB123\uC73C\uC138\uC694`;
4652
+ return `\uC560\uB2C8\uBA54\uC774\uC158: Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158(animate-in \uB958)\uC740 \uD50C\uB7EC\uADF8\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. ${add} \uB4A4 ${where}.`;
4653
+ }
4158
4654
  var INIT_NEXT_STEP_COMMAND = "npx @axis-ds/cli add button";
4159
4655
  function isTailwind4(tailwind) {
4160
4656
  return tailwind.major !== void 0 && tailwind.major >= 4;
@@ -4221,12 +4717,6 @@ function tailwind4GuideLines(pkg, installCommand, globalCss, baseDir) {
4221
4717
  return lines;
4222
4718
  }
4223
4719
  var BOM = "\uFEFF";
4224
- function addCommandFor(installCommand, deps) {
4225
- const specs = Object.entries(deps).map(([name, range]) => `"${name}@${range}"`).join(" ");
4226
- if (installCommand.startsWith("yarn")) return `yarn add ${specs}`;
4227
- if (installCommand.startsWith("npm")) return `npm install ${specs}`;
4228
- return `pnpm add ${specs}`;
4229
- }
4230
4720
  function planUtilsDependencies(baseDirInput) {
4231
4721
  const baseDir = path15.resolve(baseDirInput);
4232
4722
  const installCommand = detectInstallCommand(baseDir);
@@ -4349,11 +4839,36 @@ function reportDependencies(plan, log) {
4349
4839
  }
4350
4840
  async function existingConfig(baseDir) {
4351
4841
  try {
4352
- return await readAxisConfig(baseDir);
4842
+ const parsed = await readAxisConfig(baseDir);
4843
+ return isPlainObject2(parsed) ? parsed : null;
4353
4844
  } catch {
4354
4845
  return null;
4355
4846
  }
4356
4847
  }
4848
+ var INIT_MANAGED_KEYS = ["$schema", "componentsDir", "tailwindConfig", "globalCss", "registry"];
4849
+ function mergeInitConfig(current, next) {
4850
+ const removed = [];
4851
+ const entries = [];
4852
+ for (const [k, v] of Object.entries(current)) {
4853
+ if (k in next) entries.push([k, next[k]]);
4854
+ else if (INIT_MANAGED_KEYS.includes(k)) removed.push(k);
4855
+ else entries.push([k, v]);
4856
+ }
4857
+ const nextKeys = Object.keys(next);
4858
+ nextKeys.forEach((k, i) => {
4859
+ if (k in current) return;
4860
+ let at = 0;
4861
+ for (let j = i - 1; j >= 0; j--) {
4862
+ const idx = entries.findIndex(([key]) => key === nextKeys[j]);
4863
+ if (idx !== -1) {
4864
+ at = idx + 1;
4865
+ break;
4866
+ }
4867
+ }
4868
+ entries.splice(at, 0, [k, next[k]]);
4869
+ });
4870
+ return { merged: Object.fromEntries(entries), removed };
4871
+ }
4357
4872
  function configStateNote(state) {
4358
4873
  if (state === "created") return "axis.config.json \uC740 \uC774\uBBF8 \uB9CC\uB4E4\uC5C8\uC2B5\uB2C8\uB2E4.";
4359
4874
  if (state === "updated") return "axis.config.json \uC740 \uC774\uBBF8 \uAC31\uC2E0\uD588\uC2B5\uB2C8\uB2E4.";
@@ -4370,7 +4885,7 @@ function planUtilsWrite(baseDirInput, utilsPath) {
4370
4885
  const shown = path15.relative(baseDir, utilsPath).split(path15.sep).join("/") || utilsPath;
4371
4886
  let st;
4372
4887
  try {
4373
- st = lstatSync2(utilsPath);
4888
+ st = lstatSync3(utilsPath);
4374
4889
  } catch (e) {
4375
4890
  if (e.code === "ENOENT") return { action: "create" };
4376
4891
  return { action: "reject", reason: `${shown} \uC758 \uC815\uBCF4\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${errCode(e)})` };
@@ -4390,7 +4905,7 @@ function planUtilsWrite(baseDirInput, utilsPath) {
4390
4905
  }
4391
4906
  let target;
4392
4907
  try {
4393
- target = statSync3(real);
4908
+ target = statSync4(real);
4394
4909
  } catch (e) {
4395
4910
  return { action: "reject", reason: `${shown} \uB9C1\uD06C \uB300\uC0C1(${real})\uC758 \uC815\uBCF4\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${errCode(e)})` };
4396
4911
  }
@@ -4446,8 +4961,6 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4446
4961
  registry
4447
4962
  );
4448
4963
  const configPath = path15.join(baseDir, "axis.config.json");
4449
- const nextText = `${JSON.stringify(axisConfig, null, 2)}
4450
- `;
4451
4964
  const rejects = [];
4452
4965
  let previousText = null;
4453
4966
  try {
@@ -4457,9 +4970,18 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4457
4970
  rejects.push(`\uAE30\uC874 axis.config.json \uC744 \uC77D\uC9C0 \uBABB\uD574 \uBE44\uAD50\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${errCode(e)})`);
4458
4971
  }
4459
4972
  }
4973
+ let removedKeys = [];
4974
+ let nextText = `${JSON.stringify(axisConfig, null, 2)}
4975
+ `;
4976
+ if (previousText !== null && current !== null) {
4977
+ const { merged, removed } = mergeInitConfig(current, axisConfig);
4978
+ removedKeys = removed;
4979
+ const body = previousText.startsWith(BOM) ? previousText.slice(1) : previousText;
4980
+ nextText = (previousText.startsWith(BOM) ? BOM : "") + formatJsonLike(body, merged);
4981
+ }
4460
4982
  if (previousText !== null && previousText !== nextText) {
4461
4983
  try {
4462
- const st = statSync3(configPath);
4984
+ const st = statSync4(configPath);
4463
4985
  if (st.isFile() && st.nlink >= 2) rejects.push(hardLinkMessage("axis.config.json", st.nlink));
4464
4986
  } catch {
4465
4987
  }
@@ -4500,7 +5022,8 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4500
5022
  configState = "updated";
4501
5023
  const registryNote = migratedRegistry ? "registry \uB97C \uC0C8 \uAE30\uBCF8 \uC8FC\uC18C\uB85C \uAC31\uC2E0" : existingRegistry !== void 0 ? "registry \uB294 \uAE30\uC874 \uAC12 \uC720\uC9C0" : "registry \uB294 \uAE30\uBCF8\uAC12";
4502
5024
  const agenticNote = existingAgenticDir !== void 0 ? ", agenticDir \uB294 \uAE30\uC874 \uAC12 \uC720\uC9C0" : "";
4503
- log(chalk10.green(`\u2713 axis.config.json \uAC31\uC2E0(\uAE30\uC874 \uD30C\uC77C\uC744 init \uAC12\uC73C\uB85C \uB2E4\uC2DC \uC500, ${registryNote}${agenticNote})`));
5025
+ const mergeNote = current !== null ? "init \uC774 \uB2E4\uB8E8\uB294 \uD0A4\uB9CC \uBC14\uAFB8\uACE0 \uB098\uBA38\uC9C0 \uD0A4\uB294 \uADF8\uB300\uB85C \uB460" : "\uC77D\uC9C0 \uBABB\uD55C \uAE30\uC874 \uD30C\uC77C\uC744 init \uAC12\uC73C\uB85C \uB2E4\uC2DC \uC500";
5026
+ log(chalk10.green(`\u2713 axis.config.json \uAC31\uC2E0(${mergeNote}, ${registryNote}${agenticNote})`));
4504
5027
  if (migratedRegistry && existingRegistry !== void 0) {
4505
5028
  log(
4506
5029
  chalk10.yellow(
@@ -4508,9 +5031,8 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4508
5031
  )
4509
5032
  );
4510
5033
  }
4511
- const dropped = current ? Object.keys(current).filter((k) => !(k in axisConfig) && current[k] !== void 0) : [];
4512
- if (dropped.length > 0) {
4513
- log(chalk10.yellow(` \uAE30\uC874 \uC124\uC815\uC5D0\uC11C \uBE60\uC9C4 \uD0A4: ${dropped.join(", ")} (\uD544\uC694\uD558\uBA74 axis.config.json \uC5D0 \uB2E4\uC2DC \uC801\uC5B4 \uC8FC\uC138\uC694)`));
5034
+ if (removedKeys.length > 0) {
5035
+ log(chalk10.yellow(` \uC9C0\uC6B4 \uD0A4: ${removedKeys.join(", ")} (Tailwind 4 \uC774\uC0C1\uC740 \uC124\uC815 \uD30C\uC77C\uC744 \uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4)`));
4514
5036
  }
4515
5037
  }
4516
5038
  let utilsStateNote = "utils.ts \uB294 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4(\uC774\uBBF8 \uAC19\uC740 \uB0B4\uC6A9).";
@@ -4552,6 +5074,8 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4552
5074
  return;
4553
5075
  }
4554
5076
  reportDependencies(plan, log);
5077
+ const animation = animationGuideLine(readPackageJsonSafe(baseDir), tailwind4, plan.installCommand);
5078
+ if (animation !== void 0) log(chalk10.yellow(` ${animation}`));
4555
5079
  if (tailwind4) {
4556
5080
  const [head, ...rest] = tailwind4GuideLines(
4557
5081
  readPackageJsonSafe(baseDir),
@@ -4572,28 +5096,31 @@ ${head}`));
4572
5096
  function registerInitCommand(program2, registryUrl = DEFAULT_REGISTRY_URL) {
4573
5097
  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) => {
4574
5098
  console.log(chalk11.blue("\n\u{1F3A8} AXIS Design System \uCD08\uAE30\uD654\n"));
4575
- const tailwind4 = isTailwind4(planUtilsDependencies(process.cwd()).tailwind);
4576
- const config = options.yes ? { ...INIT_DEFAULTS } : await prompts2([
5099
+ const baseDir = process.cwd();
5100
+ const tailwind4 = isTailwind4(planUtilsDependencies(baseDir).tailwind);
5101
+ const resolved = await resolveInitAnswers(baseDir, tailwind4);
5102
+ for (const note of resolved.notes) console.log(note.startsWith("!") ? chalk11.yellow(note) : chalk11.gray(note));
5103
+ const config = options.yes ? { ...resolved.answers } : await prompts2([
4577
5104
  {
4578
5105
  type: "text",
4579
5106
  name: "componentsDir",
4580
5107
  message: "\uCEF4\uD3EC\uB10C\uD2B8 \uB514\uB809\uD1A0\uB9AC:",
4581
- initial: INIT_DEFAULTS.componentsDir
5108
+ initial: resolved.answers.componentsDir
4582
5109
  },
4583
5110
  {
4584
5111
  type: tailwind4 ? null : "text",
4585
5112
  name: "tailwindConfig",
4586
5113
  message: "Tailwind \uC124\uC815 \uD30C\uC77C:",
4587
- initial: INIT_DEFAULTS.tailwindConfig
5114
+ initial: resolved.answers.tailwindConfig
4588
5115
  },
4589
5116
  {
4590
5117
  type: "text",
4591
5118
  name: "globalCss",
4592
5119
  message: "\uAE00\uB85C\uBC8C CSS \uD30C\uC77C:",
4593
- initial: INIT_DEFAULTS.globalCss
5120
+ initial: resolved.answers.globalCss
4594
5121
  }
4595
5122
  ]);
4596
- await runInit(process.cwd(), config, registryUrl);
5123
+ await runInit(baseDir, config, registryUrl);
4597
5124
  });
4598
5125
  }
4599
5126
 
@@ -4602,7 +5129,7 @@ import chalk12 from "chalk";
4602
5129
  import prompts3 from "prompts";
4603
5130
 
4604
5131
  // ../axis-registry-core/src/add/add.ts
4605
- import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
5132
+ import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync5 } from "fs";
4606
5133
  import path17 from "path";
4607
5134
  import fs13 from "fs-extra";
4608
5135
 
@@ -5261,7 +5788,7 @@ function relativeSpecifierResolves(fromFile, spec) {
5261
5788
  const base = path17.resolve(path17.dirname(fromFile), spec);
5262
5789
  return RESOLVE_EXTS.some((ext) => {
5263
5790
  try {
5264
- return statSync4(base + ext).isFile();
5791
+ return statSync5(base + ext).isFile();
5265
5792
  } catch {
5266
5793
  return false;
5267
5794
  }
@@ -5336,7 +5863,7 @@ function missingDependencies(root, items) {
5336
5863
  const installCommand = detectInstallCommand(root);
5337
5864
  let pkg;
5338
5865
  try {
5339
- const text = readFileSync6(path17.join(root, "package.json"), "utf8").replace(/^/, "");
5866
+ const text = readFileSync6(path17.join(root, "package.json"), "utf8").replace(/^\uFEFF/, "");
5340
5867
  const parsed = JSON.parse(text);
5341
5868
  pkg = isPlainObject2(parsed) ? parsed : void 0;
5342
5869
  } catch {