@axis-ds/cli 1.5.4 → 1.6.1

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) {
@@ -1744,7 +1745,7 @@ import ora5 from "ora";
1744
1745
 
1745
1746
  // src/template/template-apply.ts
1746
1747
  import fs8 from "fs-extra";
1747
- import path8 from "path";
1748
+ import path9 from "path";
1748
1749
  import chalk5 from "chalk";
1749
1750
  import ora3 from "ora";
1750
1751
  import prompts from "prompts";
@@ -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,940 +1842,1585 @@ 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 as existsSync2, lstatSync, readdirSync, readFileSync as readFileSync2 } from "fs";
1847
+ import { extname, join, resolve } from "path";
1848
+
1849
+ // ../axis-registry-core/src/project.ts
1850
+ import { existsSync, readFileSync, statSync } from "fs";
1851
+ import path8 from "path";
1852
+ function detectInstallCommand(baseDir) {
1853
+ if (existsSync(path8.join(baseDir, "pnpm-lock.yaml"))) return "pnpm install";
1854
+ if (existsSync(path8.join(baseDir, "yarn.lock"))) return "yarn install";
1855
+ if (existsSync(path8.join(baseDir, "package-lock.json"))) return "npm install";
1856
+ return "pnpm install";
1857
+ }
1858
+ function hasDependency(pkg, name) {
1859
+ for (const field of ["dependencies", "devDependencies"]) {
1860
+ const deps = pkg[field];
1861
+ if (typeof deps === "object" && deps !== null && name in deps) {
1862
+ return String(deps[name]);
1861
1863
  }
1862
1864
  }
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;
1878
- }
1865
+ return void 0;
1866
+ }
1867
+ function isPlainObject(value) {
1868
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1869
+ }
1870
+ function addCommandFor(installCommand, deps) {
1871
+ const specs = Object.entries(deps).map(([name, range]) => `"${name}@${range}"`).join(" ");
1872
+ if (installCommand.startsWith("yarn")) return `yarn add ${specs}`;
1873
+ if (installCommand.startsWith("npm")) return `npm install ${specs}`;
1874
+ return `pnpm add ${specs}`;
1875
+ }
1876
+ var GLOBAL_CSS_CANDIDATES = [
1877
+ "app/globals.css",
1878
+ "src/app/globals.css",
1879
+ "styles/globals.css",
1880
+ "src/styles/globals.css",
1881
+ "globals.css"
1882
+ ];
1883
+ var TAILWIND_CONFIG_CANDIDATES = [
1884
+ "tailwind.config.ts",
1885
+ "tailwind.config.js",
1886
+ "tailwind.config.mjs",
1887
+ "tailwind.config.cjs"
1888
+ ];
1889
+ function isFile(file) {
1890
+ try {
1891
+ return statSync(file).isFile();
1892
+ } catch {
1893
+ return false;
1879
1894
  }
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}`));
1895
+ }
1896
+ function isDirectory(dir) {
1897
+ try {
1898
+ return statSync(dir).isDirectory();
1899
+ } catch {
1900
+ return false;
1901
+ }
1902
+ }
1903
+ function findFirstFile(baseDir, candidates) {
1904
+ return candidates.find((c) => isFile(path8.join(baseDir, c)));
1905
+ }
1906
+ function detectProjectPaths(baseDirInput) {
1907
+ const baseDir = path8.resolve(baseDirInput);
1908
+ const hasSrc = isDirectory(path8.join(baseDir, "src"));
1909
+ const css = findFirstFile(baseDir, GLOBAL_CSS_CANDIDATES);
1910
+ const tw = findFirstFile(baseDir, TAILWIND_CONFIG_CANDIDATES);
1911
+ return {
1912
+ componentsDir: hasSrc ? "./src/components/ui" : "./components/ui",
1913
+ globalCss: css !== void 0 ? `./${css}` : hasSrc ? "./src/app/globals.css" : "./app/globals.css",
1914
+ globalCssSource: css !== void 0 ? "found" : "default",
1915
+ tailwindConfig: tw !== void 0 ? `./${tw}` : "./tailwind.config.ts",
1916
+ tailwindConfigSource: tw !== void 0 ? "found" : "default"
1917
+ };
1918
+ }
1919
+ function projectFileExists(baseDir, relPath) {
1920
+ return isFile(path8.resolve(baseDir, relPath));
1921
+ }
1922
+ var ROOT_LAYOUT_FILES = [
1923
+ "app/layout.tsx",
1924
+ "app/layout.jsx",
1925
+ "app/layout.js",
1926
+ "src/app/layout.tsx",
1927
+ "src/app/layout.jsx",
1928
+ "src/app/layout.js",
1929
+ "pages/_app.tsx",
1930
+ "pages/_app.jsx",
1931
+ "pages/_document.tsx",
1932
+ "pages/_document.jsx",
1933
+ "src/pages/_app.tsx",
1934
+ "src/pages/_app.jsx",
1935
+ "src/pages/_document.tsx",
1936
+ "src/pages/_document.jsx"
1937
+ ];
1938
+ function exactVersionOf(version) {
1939
+ if (typeof version !== "string") return void 0;
1940
+ const v = version.trim();
1941
+ if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(v) || /^v?0\.0\.0-/.test(v)) return void 0;
1942
+ return v;
1943
+ }
1944
+ function findInstalledPackage(baseDirInput, name) {
1945
+ let dir = path8.resolve(baseDirInput);
1946
+ for (; ; ) {
1947
+ const pkgDir = path8.join(dir, "node_modules", ...name.split("/"));
1948
+ const file = path8.join(pkgDir, "package.json");
1949
+ if (isFile(file)) {
1950
+ let version;
1951
+ try {
1952
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
1953
+ version = isPlainObject(parsed) ? exactVersionOf(parsed.version) : void 0;
1954
+ } catch {
1955
+ version = void 0;
1892
1956
  }
1957
+ return { dir: pkgDir, version };
1893
1958
  }
1894
- return;
1895
- }
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);
1959
+ const parent = path8.dirname(dir);
1960
+ if (parent === dir) return void 0;
1961
+ dir = parent;
1901
1962
  }
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);
1963
+ }
1964
+ function isPnpProject(baseDirInput) {
1965
+ let dir = path8.resolve(baseDirInput);
1966
+ for (; ; ) {
1967
+ if (isFile(path8.join(dir, ".pnp.cjs")) || isFile(path8.join(dir, ".pnp.js"))) return true;
1968
+ const parent = path8.dirname(dir);
1969
+ if (parent === dir) return false;
1970
+ dir = parent;
1906
1971
  }
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}`));
1919
- }
1972
+ }
1973
+ function compareExactVersions(a, b) {
1974
+ const parse = (v) => {
1975
+ const m = /^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?/.exec(v.trim());
1976
+ return m ? { core: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] !== void 0 } : void 0;
1977
+ };
1978
+ const x = parse(a);
1979
+ const y = parse(b);
1980
+ if (!x || !y) return void 0;
1981
+ for (let i = 0; i < 3; i++) if (x.core[i] !== y.core[i]) return x.core[i] - y.core[i];
1982
+ return Number(y.pre) - Number(x.pre);
1983
+ }
1984
+
1985
+ // ../axis-registry-core/src/template/tailwind-core.ts
1986
+ function hasDependency2(pkg, name) {
1987
+ for (const field of ["dependencies", "devDependencies"]) {
1988
+ const deps = pkg[field];
1989
+ if (typeof deps === "object" && deps !== null && name in deps) {
1990
+ return String(deps[name]);
1920
1991
  }
1921
1992
  }
1922
- console.log(chalk5.green(`
1923
- '${detail.name}' \uD15C\uD50C\uB9BF \uC801\uC6A9 \uC644\uB8CC!`));
1993
+ return void 0;
1924
1994
  }
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;
1995
+ function isPlainObject2(value) {
1996
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1997
+ }
1998
+ var VERSION = String.raw`v?(\d+)(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?`;
1999
+ function versionParts(token) {
2000
+ const m = new RegExp(`^${VERSION}$`).exec(token);
2001
+ if (!m) return void 0;
2002
+ if (/^v?0\.0\.0-/.test(token)) return void 0;
2003
+ return { major: Number(m[1]), minor: m[2], patch: m[3] };
2004
+ }
2005
+ var isWild = (part) => part === void 0 || /^[xX*]$/.test(part);
2006
+ function comparatorBounds(op, v) {
2007
+ switch (op) {
2008
+ case ">=":
2009
+ return { lower: v.major };
2010
+ case ">":
2011
+ return { lower: isWild(v.minor) ? v.major + 1 : v.major };
2012
+ case "<=":
2013
+ return { upper: v.major };
2014
+ case "<": {
2015
+ const zeroOrWild = (part) => isWild(part) || Number(part) === 0;
2016
+ return { upper: zeroOrWild(v.minor) && zeroOrWild(v.patch) ? v.major - 1 : v.major };
1943
2017
  }
1944
- selectedSlug = choice;
2018
+ default:
2019
+ return { lower: v.major, upper: v.major };
1945
2020
  }
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;
1957
- }
1958
- targetDir = dir;
2021
+ }
2022
+ function tailwindMajorBounds(range) {
2023
+ const r = range.trim();
2024
+ if (r === "" || r.includes("||")) return void 0;
2025
+ const simple = new RegExp(`^(?:\\^|~|=)?\\s*(${VERSION})$`).exec(r);
2026
+ if (simple) {
2027
+ const v = versionParts(simple[1]);
2028
+ return v ? { lower: v.major, upper: v.major } : void 0;
1959
2029
  }
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;
1971
- }
1972
- } else {
1973
- await fs8.ensureDir(resolved);
2030
+ const hyphen = new RegExp(`^(${VERSION})\\s+-\\s+(${VERSION})$`).exec(r);
2031
+ if (hyphen) {
2032
+ const tokens2 = r.split(/\s+-\s+/);
2033
+ const a = versionParts(tokens2[0]);
2034
+ const b = versionParts(tokens2[1]);
2035
+ return a && b && a.major <= b.major ? { lower: a.major, upper: b.major } : void 0;
1974
2036
  }
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 });
1980
- console.log(chalk5.green("axis.config.json \uC0DD\uC131 \uC644\uB8CC"));
2037
+ const tokens = r.replace(/(>=|<=|>|<|=)\s+/g, "$1").split(/\s+/);
2038
+ const lowers = [];
2039
+ const uppers = [];
2040
+ for (const t of tokens) {
2041
+ const c = /^(>=|<=|>|<|=)(.+)$/.exec(t);
2042
+ if (!c) return void 0;
2043
+ const v = versionParts(c[2]);
2044
+ if (v === void 0) return void 0;
2045
+ const b = comparatorBounds(c[1], v);
2046
+ if (b.lower !== void 0) lowers.push(b.lower);
2047
+ if (b.upper !== void 0) uppers.push(b.upper);
1981
2048
  }
1982
- console.log(chalk5.blue(`
1983
- \uB2E4\uC74C \uB2E8\uACC4:`));
1984
- console.log(chalk5.cyan(` cd ${targetDir}`));
1985
- console.log(chalk5.cyan(` pnpm install`));
1986
- console.log(chalk5.cyan(` pnpm dev`));
2049
+ if (lowers.length === 0) return void 0;
2050
+ const lower = Math.max(...lowers);
2051
+ const upper = uppers.length > 0 ? Math.min(...uppers) : void 0;
2052
+ if (upper !== void 0 && upper < lower) return void 0;
2053
+ return upper !== void 0 ? { lower, upper } : { lower };
1987
2054
  }
1988
-
1989
- // src/template/template-diff.ts
1990
- import fs9 from "fs-extra";
1991
- import path9 from "path";
1992
- import chalk6 from "chalk";
1993
- import ora4 from "ora";
1994
- async function diffTemplate(slug, options) {
1995
- const spinner = ora4(`'${slug}' \uD15C\uD50C\uB9BF \uAC00\uC838\uC624\uB294 \uC911...`).start();
1996
- let detail;
2055
+ function tailwindMajorFromRange(range) {
2056
+ return tailwindMajorBounds(range)?.lower;
2057
+ }
2058
+ function majorWithin(major, bounds) {
2059
+ return major >= bounds.lower && (bounds.upper === void 0 || major <= bounds.upper);
2060
+ }
2061
+ function majorFromVersion(version) {
2062
+ if (typeof version !== "string") return void 0;
2063
+ const v = version.trim();
2064
+ const m = /^v?(\d+)\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]*)?$/.exec(v);
2065
+ if (!m || /^v?0\.0\.0-/.test(v)) return void 0;
2066
+ return Number(m[1]);
2067
+ }
2068
+ function detectTailwind(baseDirInput, pkg) {
2069
+ const baseDir = resolve(baseDirInput);
2070
+ const installedFile = join(baseDir, "node_modules", "tailwindcss", "package.json");
2071
+ const declared = pkg ? hasDependency2(pkg, "tailwindcss") : void 0;
2072
+ const bounds = declared !== void 0 ? tailwindMajorBounds(declared) : void 0;
2073
+ const declaredMajor = bounds?.lower;
1997
2074
  try {
1998
- detail = await fetchTemplateDetail(slug);
1999
- spinner.succeed(`'${detail.name}' \uB85C\uB4DC \uC644\uB8CC`);
2000
- } catch (err) {
2001
- spinner.fail(err.message);
2002
- process.exit(1);
2003
- }
2004
- const baseDir = path9.resolve(options.dir || ".");
2005
- const entries = [];
2006
- for (const file of detail.files) {
2007
- const localPath = path9.join(baseDir, file.path);
2008
- if (!await fs9.pathExists(localPath)) {
2009
- entries.push({ path: file.path, status: "added" });
2010
- continue;
2011
- }
2012
- const localContent = await fs9.readFile(localPath, "utf-8");
2013
- if (localContent === file.content) {
2014
- entries.push({ path: file.path, status: "unchanged" });
2015
- } else {
2016
- entries.push({
2017
- path: file.path,
2018
- status: "modified",
2019
- localLines: localContent.split("\n").length,
2020
- remoteLines: file.content.split("\n").length
2021
- });
2022
- }
2023
- }
2024
- if (options.verbose) {
2025
- const templatePaths = new Set(detail.files.map((f) => f.path));
2026
- const localFiles = await collectLocalFiles(baseDir);
2027
- for (const lf of localFiles) {
2028
- if (!templatePaths.has(lf)) {
2029
- entries.push({ path: lf, status: "extra" });
2075
+ const installed = JSON.parse(readFileSync2(installedFile, "utf8"));
2076
+ const version = isPlainObject2(installed) ? installed.version : void 0;
2077
+ const major = majorFromVersion(version);
2078
+ if (major !== void 0) {
2079
+ const found = { major, source: "installed", value: String(version) };
2080
+ if (declared !== void 0 && bounds !== void 0 && !majorWithin(major, bounds)) {
2081
+ found.mismatch = { declared, declaredMajor: bounds.lower };
2030
2082
  }
2083
+ return found;
2031
2084
  }
2032
- }
2033
- console.log(chalk6.blue(`
2034
- '${detail.name}' \uD15C\uD50C\uB9BF diff \uACB0\uACFC:
2035
- `));
2036
- const statusIcon = {
2037
- added: chalk6.green("+ \uCD94\uAC00 \uD544\uC694"),
2038
- modified: chalk6.yellow("~ \uBCC0\uACBD\uB428"),
2039
- unchanged: chalk6.gray("= \uB3D9\uC77C"),
2040
- extra: chalk6.cyan("? \uB85C\uCEEC \uC804\uC6A9")
2041
- };
2042
- let added = 0;
2043
- let modified = 0;
2044
- let unchanged = 0;
2045
- let extra = 0;
2046
- for (const entry of entries) {
2047
- const icon = statusIcon[entry.status];
2048
- console.log(` ${icon.padEnd(28)} ${entry.path}`);
2049
- if (entry.status === "modified" && options.verbose) {
2050
- console.log(
2051
- chalk6.gray(
2052
- ` \uB85C\uCEEC ${entry.localLines}\uC904 / \uC6D0\uBCF8 ${entry.remoteLines}\uC904`
2053
- )
2054
- );
2055
- }
2056
- switch (entry.status) {
2057
- case "added":
2058
- added++;
2059
- break;
2060
- case "modified":
2061
- modified++;
2062
- break;
2063
- case "unchanged":
2064
- 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
2085
  } catch {
2084
- return results;
2085
2086
  }
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
- }
2087
+ if (declared !== void 0) {
2088
+ if (declaredMajor !== void 0) return { major: declaredMajor, source: "declared", value: declared };
2089
+ return { source: "none", value: declared };
2095
2090
  }
2096
- return results;
2091
+ return { source: "none" };
2097
2092
  }
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"
2093
+ var TAILWIND_CONFIG_FILES = [
2094
+ "tailwind.config.ts",
2095
+ "tailwind.config.js",
2096
+ "tailwind.config.mjs",
2097
+ "tailwind.config.cjs"
2124
2098
  ];
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"));
2099
+ var TAILWIND_CSS_CANDIDATES = [
2100
+ "globals.css",
2101
+ "app/globals.css",
2102
+ "src/app/globals.css",
2103
+ "src/globals.css",
2104
+ "styles/globals.css"
2105
+ ];
2106
+ var TAILWIND_IMPORT = /@import\s+(?:url\(\s*)?["']tailwindcss["']/;
2107
+ function stripCssComments(css) {
2108
+ return css.replace(/\/\*[\s\S]*?\*\//g, "");
2128
2109
  }
2129
- function errorCode(err) {
2130
- return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
2110
+ function hasTailwindImport(css) {
2111
+ return TAILWIND_IMPORT.test(stripCssComments(css));
2131
2112
  }
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
- }
2113
+ var TOKENS_TAILWIND4_ENTRY = "@axis-ds/tokens/css/tailwind4";
2114
+ var TOKENS_TAILWIND4_IMPORT_LINE = `@import "${TOKENS_TAILWIND4_ENTRY}";`;
2115
+ var TOKENS_TAILWIND4_IMPORT = /@import\s+(?:url\(\s*)?["']@axis-ds\/tokens\/css\/tailwind4["']/;
2116
+ function hasTokensTailwind4Import(css) {
2117
+ return TOKENS_TAILWIND4_IMPORT.test(stripCssComments(css));
2141
2118
  }
2142
- function projectRequire(baseDir) {
2143
- return createRequire(join(resolve(baseDir), "package.json"));
2119
+ var TOKENS_CSS_IMPORT = /@import\s+(?:url\(\s*)?["'](@axis-ds\/tokens\/css(?:\/shadcn|\/tailwind4)?)["']/;
2120
+ function findTokensCssImport(css) {
2121
+ return TOKENS_CSS_IMPORT.exec(stripCssComments(css))?.[1];
2144
2122
  }
2145
- function tokensSpecifier(subpath) {
2146
- return `${TOKENS_PACKAGE}/${subpath.replace(/^\.\//, "")}`;
2123
+ var TOKENS_CSS_JS_IMPORT = /(?:\bimport\s+|\bfrom\s+|\brequire\s*\(\s*)(["'])(@axis-ds\/tokens\/css(?:\/shadcn|\/tailwind4)?)\1/;
2124
+ var AXIS_VARIABLE_DEFINITION = /(?<![\w-])--axis-[\w-]+\s*:/;
2125
+ function findLayoutTokensCssImport(baseDirInput) {
2126
+ const baseDir = resolve(baseDirInput);
2127
+ for (const candidate of ROOT_LAYOUT_FILES) {
2128
+ const file = join(baseDir, candidate);
2129
+ if (!existsSync2(file)) continue;
2130
+ let text;
2131
+ try {
2132
+ text = readFileSync2(file, "utf8");
2133
+ } catch {
2134
+ continue;
2135
+ }
2136
+ const m = TOKENS_CSS_JS_IMPORT.exec(stripJsComments(text));
2137
+ if (m) return { file: candidate, spec: m[2] };
2138
+ }
2139
+ return void 0;
2147
2140
  }
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;
2141
+ function checkGlobalsCss(baseDirInput) {
2142
+ const baseDir = resolve(baseDirInput);
2143
+ for (const candidate of TAILWIND_CSS_CANDIDATES) {
2144
+ const file = join(baseDir, candidate);
2145
+ if (!existsSync2(file)) continue;
2146
+ const content = readFileSync2(file, "utf8");
2147
+ const css = stripCssComments(content);
2148
+ const imported = findTokensCssImport(content);
2149
+ let pass;
2150
+ if (imported !== void 0) pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${imported} import)`;
2151
+ else if (AXIS_VARIABLE_DEFINITION.test(css)) pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218(--axis-*)\uAC00 \uC815\uC758\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4`;
2152
+ else {
2153
+ const layout = findLayoutTokensCssImport(baseDir);
2154
+ if (layout) pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${layout.file}\uC758 ${layout.spec} import)`;
2155
+ }
2156
+ if (pass !== void 0) {
2157
+ const tailwind = detectTailwind(baseDir, readPackageJson(baseDir));
2158
+ const tailwind4 = tailwind.major !== void 0 && tailwind.major >= 4;
2159
+ if (!tailwind4 && findCreateNextAppDefaults(content).some((d) => d.includes("font-family: Arial"))) {
2160
+ return {
2161
+ name: "globals.css",
2162
+ severity: "warn",
2163
+ message: `${pass}. \uB2E4\uB9CC create-next-app \uAE30\uBCF8 body font-family: Arial \uC774 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4 \u2192 \uC9C0\uC6B0\uC138\uC694(AXIS \uAE00\uAF34 \uB300\uC2E0 Arial \uC774 \uC801\uC6A9\uB428)`
2164
+ };
2165
+ }
2166
+ return { name: "globals.css", severity: "pass", message: pass };
2167
+ }
2168
+ if (css.includes("--background") && css.includes("--primary")) {
2169
+ return {
2170
+ name: "globals.css",
2171
+ severity: "warn",
2172
+ message: `${candidate}\uC5D0 \uC790\uCCB4 CSS \uBCC0\uC218(--background \xB7 --primary)\uB9CC \uC788\uACE0 AXIS \uD1A0\uD070\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 @axis-ds/tokens CSS \uB97C import \uD558\uC138\uC694`
2173
+ };
2174
+ }
2175
+ return { name: "globals.css", severity: "warn", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4` };
2161
2176
  }
2177
+ return { name: "globals.css", severity: "fail", message: "globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" };
2162
2178
  }
2163
- function brandImportPath(entry) {
2164
- return tokensSpecifier(entry.css);
2179
+ var TAILWIND4_MIN_AXIS_VERSION = "1.4.0";
2180
+ var TAILWIND4_AXIS_PACKAGES = ["@axis-ds/tokens", "@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2181
+ function lowerOf(token) {
2182
+ const v = versionParts(token);
2183
+ if (!v) return void 0;
2184
+ const num = (part) => isWild(part) ? 0 : Number(part);
2185
+ const pre = /^v?\d+(?:\.(?:\d+|[xX*]))?(?:\.(?:\d+|[xX*]))?-/.test(token);
2186
+ return { core: [v.major, num(v.minor), num(v.patch)], pre };
2165
2187
  }
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;
2188
+ function compareLower(a, b) {
2189
+ for (let i = 0; i < 3; i++) {
2190
+ if (a.core[i] !== b.core[i]) return a.core[i] - b.core[i];
2172
2191
  }
2192
+ return Number(b.pre) - Number(a.pre);
2173
2193
  }
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;
2194
+ function rangeLowerVersion(range) {
2195
+ const r = range.trim();
2196
+ if (r === "" || r.includes("||")) return void 0;
2197
+ const simple = new RegExp(`^(?:\\^|~|=)?\\s*(${VERSION})$`).exec(r);
2198
+ if (simple) return lowerOf(simple[1]);
2199
+ if (new RegExp(`^(${VERSION})\\s+-\\s+(${VERSION})$`).test(r)) {
2200
+ const [a, b] = r.split(/\s+-\s+/);
2201
+ return lowerOf(b) ? lowerOf(a) : void 0;
2182
2202
  }
2183
- for (const candidate of GLOBAL_CSS_CANDIDATES) {
2184
- const file = join(root, candidate);
2185
- if (isFile(file)) {
2186
- result.path = file;
2187
- break;
2203
+ const tokens = r.replace(/(>=|<=|>|<|=)\s+/g, "$1").split(/\s+/);
2204
+ let lower;
2205
+ for (const t of tokens) {
2206
+ const c = /^(>=|<=|>|<|=)(.+)$/.exec(t);
2207
+ if (!c) return void 0;
2208
+ const v = versionParts(c[2]);
2209
+ const low = lowerOf(c[2]);
2210
+ if (!v || !low) return void 0;
2211
+ let candidate;
2212
+ if (c[1] === ">=" || c[1] === "=") candidate = low;
2213
+ else if (c[1] === ">") {
2214
+ if (isWild(v.minor)) candidate = { core: [v.major + 1, 0, 0], pre: false };
2215
+ else if (isWild(v.patch)) candidate = { core: [v.major, Number(v.minor) + 1, 0], pre: false };
2216
+ else candidate = { core: [v.major, Number(v.minor), Number(v.patch) + 1], pre: false };
2188
2217
  }
2218
+ if (candidate && (!lower || compareLower(candidate, lower) > 0)) lower = candidate;
2189
2219
  }
2190
- return result;
2220
+ return lower;
2191
2221
  }
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;
2222
+ function rangeLowerAtLeast(range, min) {
2223
+ const lower = rangeLowerVersion(range);
2224
+ const target = lowerOf(min);
2225
+ if (!lower || !target) return void 0;
2226
+ return compareLower(lower, target) >= 0;
2199
2227
  }
2200
- var AXIS_CONFIG_FILE_NAME = "axis.config.json";
2201
- function readBrandConfig(baseDir) {
2202
- let parsed;
2228
+ function exactVersion(version) {
2229
+ if (typeof version !== "string") return void 0;
2230
+ const v = version.trim();
2231
+ if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(v) || /^v?0\.0\.0-/.test(v)) return void 0;
2232
+ return v;
2233
+ }
2234
+ function installedVersion(baseDir, name) {
2203
2235
  try {
2204
- parsed = JSON.parse(readFileSync(join(resolve(baseDir), AXIS_CONFIG_FILE_NAME), "utf8"));
2236
+ const parsed = JSON.parse(readFileSync2(join(baseDir, "node_modules", ...name.split("/"), "package.json"), "utf8"));
2237
+ return isPlainObject2(parsed) ? exactVersion(parsed.version) : void 0;
2205
2238
  } catch {
2206
- return null;
2239
+ return void 0;
2207
2240
  }
2208
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
2209
- return parsed;
2210
2241
  }
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: [] };
2242
+ function axisPackagesBelowTailwind4(pkg, baseDirInput) {
2243
+ if (!pkg) return [];
2244
+ const baseDir = baseDirInput !== void 0 ? resolve(baseDirInput) : void 0;
2245
+ const out = [];
2246
+ const min = lowerOf(TAILWIND4_MIN_AXIS_VERSION);
2247
+ for (const name of TAILWIND4_AXIS_PACKAGES) {
2248
+ const range = hasDependency2(pkg, name);
2249
+ if (range === void 0 || range.trim().startsWith("workspace:")) continue;
2250
+ const installed = baseDir !== void 0 ? installedVersion(baseDir, name) : void 0;
2251
+ const installedLower = installed !== void 0 ? lowerOf(installed) : void 0;
2252
+ if (installed !== void 0 && installedLower !== void 0) {
2253
+ if (compareLower(installedLower, min) < 0) out.push({ name, range, status: "below", source: "installed", installed });
2254
+ continue;
2255
+ }
2256
+ const atLeast = rangeLowerAtLeast(range, TAILWIND4_MIN_AXIS_VERSION);
2257
+ if (atLeast === false) out.push({ name, range, status: "below", source: "declared" });
2258
+ else if (atLeast === void 0) out.push({ name, range, status: "unknown", source: "declared" });
2221
2259
  }
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 };
2260
+ return out;
2229
2261
  }
2230
- function isMissingBuildFile(err) {
2231
- const code = errorCode(err);
2232
- return code === "MODULE_NOT_FOUND" || code === "ENOENT";
2262
+ function axisPackageVersionLabel(issue) {
2263
+ return issue.source === "installed" ? `${issue.name} \uC124\uCE58\uBCF8 ${issue.installed}` : `${issue.name} ${issue.range}`;
2233
2264
  }
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;
2265
+ var declaredList = (issues) => issues.map(axisPackageVersionLabel).join(" \xB7 ");
2266
+ function tailwind4VersionNote(issues) {
2267
+ const below = issues.filter((i) => i.status === "below");
2268
+ const unknown = issues.filter((i) => i.status === "unknown");
2269
+ let note = "";
2270
+ if (below.length > 0) note += ` (${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1 \uD544\uC694: ${declaredList(below)})`;
2271
+ 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)})`;
2272
+ return note;
2240
2273
  }
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
- };
2274
+ var TAILWIND4_COMPONENT_PACKAGES = ["@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2275
+ function componentTailwindImportLine(name) {
2276
+ return `@import "${name}/tailwind.css";`;
2251
2277
  }
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 };
2278
+ var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2279
+ function hasComponentTailwindImport(css, name) {
2280
+ const re = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${escapeRegExp(name)}/tailwind\\.css["']`);
2281
+ return re.test(stripCssComments(css));
2266
2282
  }
2267
- function stripComments(css) {
2268
- return css.replace(/\/\*[\s\S]*?\*\//g, "");
2283
+ function hasComponentSource(css, name) {
2284
+ const re = new RegExp(`@source\\s+(["'])(?!@axis-ds/)[^"'\\n]*${escapeRegExp(name)}(?:[/\\\\][^"'\\n]*)?\\1`);
2285
+ return re.test(stripCssComments(css));
2269
2286
  }
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();
2287
+ var CREATE_NEXT_APP_DEFAULTS = [
2288
+ {
2289
+ prefix: ":root \uC0C9\uAC12",
2290
+ pattern: /(?<![\w-])(--(?:background|foreground))\s*:\s*(?:#[0-9a-fA-F]{3,8}\b|(?:rgba?|hsla?|hwb|oklch|oklab|lab|lch|color)\()/g,
2291
+ item: (m) => m[1]
2292
+ },
2293
+ {
2294
+ prefix: "@theme",
2295
+ pattern: /(?<![\w-])(--color-(?:background|foreground))\s*:\s*var\(\s*(--(?:background|foreground))\s*\)/g,
2296
+ item: (m) => `${m[1]}: var(${m[2]})`
2297
+ },
2298
+ {
2299
+ prefix: "@theme",
2300
+ pattern: /(?<![\w-])(--font-(?:sans|mono))\s*:\s*var\(\s*(--font-geist-(?:sans|mono))\s*\)/g,
2301
+ item: (m) => `${m[1]}: var(${m[2]})`
2302
+ },
2303
+ {
2304
+ prefix: "body",
2305
+ // WI-0070: create-next-app 의 body `font-family: Arial, Helvetica, sans-serif` 도 같은 특징으로 봅니다. 레이어 밖 규칙이라
2306
+ // AXIS 글꼴(Pretendard, html 에 적용)을 이기고 Button 등이 `font-family: inherit` 로 Arial 을 물려받습니다.
2307
+ pattern: /(?<![\w-])(?:(background|background-color|color)\s*:\s*var\(\s*(--(?:background|foreground))\s*\)|font-family\s*:\s*Arial\b)/g,
2308
+ item: (m) => m[1] !== void 0 ? `${m[1]}: var(${m[2]})` : "font-family: Arial"
2274
2309
  }
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;
2310
+ ];
2311
+ function findCreateNextAppDefaults(css) {
2312
+ const body = stripCssComments(css);
2313
+ const labels = [];
2314
+ for (const d of CREATE_NEXT_APP_DEFAULTS) {
2315
+ const items = [];
2316
+ for (const m of body.matchAll(d.pattern)) {
2317
+ const item = d.item(m);
2318
+ if (!items.includes(item)) items.push(item);
2296
2319
  }
2320
+ if (items.length > 0) labels.push(`${d.prefix} ${items.join(", ")}`);
2297
2321
  }
2298
- return result;
2322
+ return labels;
2299
2323
  }
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];
2324
+ function stripJsComments(src) {
2325
+ let out = "";
2326
+ let i = 0;
2327
+ const n = src.length;
2328
+ while (i < n) {
2329
+ const c = src[i];
2330
+ const d = src[i + 1];
2331
+ if (c === '"' || c === "'" || c === "`") {
2332
+ let j = i + 1;
2333
+ while (j < n && src[j] !== c) {
2334
+ if (src[j] === "\\") j++;
2335
+ else if (c !== "`" && src[j] === "\n") break;
2336
+ j++;
2337
+ }
2338
+ out += src.slice(i, j + 1);
2339
+ i = j + 1;
2340
+ continue;
2341
+ }
2342
+ if (c === "/" && d === "/") {
2343
+ const end = src.indexOf("\n", i);
2344
+ i = end === -1 ? n : end;
2345
+ continue;
2346
+ }
2347
+ if (c === "/" && d === "*") {
2348
+ const end = src.indexOf("*/", i + 2);
2349
+ i = end === -1 ? n : end + 2;
2350
+ out += " ";
2351
+ continue;
2352
+ }
2353
+ out += c;
2354
+ i++;
2304
2355
  }
2305
2356
  return out;
2306
2357
  }
2307
- function providerExample(name) {
2308
- return [
2309
- 'import { ThemeProvider } from "@axis-ds/theme";',
2310
- "",
2311
- `<ThemeProvider defaultBrand="${name}">{children}</ThemeProvider>`
2312
- ].join("\n");
2358
+ var TOKENS_TAILWIND3_PRESET = "@axis-ds/tokens/tailwind";
2359
+ var PRESET_REFERENCE = /(?:\brequire\s*\(\s*|\bimport\s*\(\s*|\bfrom\s*|\bimport\s+)(["'])@axis-ds\/tokens\/tailwind\1/;
2360
+ function hasTokensPresetReference(source) {
2361
+ return PRESET_REFERENCE.test(stripJsComments(source));
2313
2362
  }
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 };
2363
+ var PRESET_PASS_SUFFIX = `\uC5D0 AXIS \uD1A0\uD070\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (${TOKENS_TAILWIND3_PRESET} preset)`;
2364
+ var TOKENS_CJS_PRESET_FIXED_VERSION = "1.7.0";
2365
+ var TOKENS_TAILWIND3_CJS_REQUIRE = `require("${TOKENS_TAILWIND3_PRESET}").default`;
2366
+ var PRESET_REQUIRE_CALL = /\brequire\s*\(\s*(["'])@axis-ds\/tokens\/tailwind\1\s*\)/g;
2367
+ var DEFAULT_ACCESS_AHEAD = /^\s*(?:\.\s*default\b|\[\s*["']default["']\s*\])/;
2368
+ function hasBareCjsPresetRequire(code) {
2369
+ for (const m of code.matchAll(PRESET_REQUIRE_CALL)) {
2370
+ const start = m.index ?? 0;
2371
+ const end = start + m[0].length;
2372
+ if (DEFAULT_ACCESS_AHEAD.test(code.slice(end))) continue;
2373
+ const before = code.slice(0, start);
2374
+ if (/\b(?:const|let|var)\s*\{[^{}]*\}\s*=\s*$/.test(before)) continue;
2375
+ const bound = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*$/.exec(before);
2376
+ if (!bound) return true;
2377
+ const ident = escapeRegExp(bound[1]);
2378
+ const rest = code.slice(0, bound.index) + code.slice(end);
2379
+ const bareUse = new RegExp(`(?<![\\w$.])${ident}(?![\\w$])(?!\\s*(?:\\.\\s*default\\b|\\[\\s*["']default["']\\s*\\]))`);
2380
+ if (bareUse.test(rest)) return true;
2381
+ }
2382
+ return false;
2383
+ }
2384
+ function isCommonJsConfig(file, source) {
2385
+ return extname(file) === ".cjs" || /\bmodule\.exports\b/.test(source);
2386
+ }
2387
+ function packageVersionGate(baseDirInput, pkg, name, min) {
2388
+ const baseDir = resolve(baseDirInput);
2389
+ if (!isPnpProject(baseDir)) {
2390
+ const installed = findInstalledPackage(baseDir, name)?.version;
2391
+ if (installed !== void 0) {
2392
+ const cmp = compareExactVersions(installed, min);
2393
+ if (cmp !== void 0) return { status: cmp >= 0 ? "ok" : "below", source: "installed", value: installed };
2394
+ }
2395
+ }
2396
+ const declared = pkg ? hasDependency2(pkg, name) : void 0;
2397
+ if (declared !== void 0) {
2398
+ const atLeast = rangeLowerAtLeast(declared, min);
2399
+ if (atLeast !== void 0) return { status: atLeast ? "ok" : "below", source: "declared", value: declared };
2400
+ return { status: "unknown", source: "declared", value: declared };
2324
2401
  }
2325
- let summary;
2326
- let parsed;
2402
+ return { status: "unknown", source: "none" };
2403
+ }
2404
+ function versionGateLabel(gate) {
2405
+ if (gate.source === "installed") return `\uC124\uCE58\uBCF8 ${gate.value}`;
2406
+ if (gate.source === "declared") return `\uC120\uC5B8 ${gate.value}`;
2407
+ return "\uC124\uCE58\uBCF8 \xB7 \uC120\uC5B8 \uC5C6\uC74C";
2408
+ }
2409
+ function readPackageJson(baseDir) {
2327
2410
  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;
2411
+ const parsed = JSON.parse(readFileSync2(join(baseDir, "package.json"), "utf8"));
2412
+ return isPlainObject2(parsed) ? parsed : void 0;
2413
+ } catch {
2414
+ return void 0;
2334
2415
  }
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 }
2416
+ }
2417
+ function tailwind4Issues(css, pkg, versions) {
2418
+ const issues = [];
2419
+ const hasEntry = hasTokensTailwind4Import(css);
2420
+ if (!hasEntry) issues.push(`AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${TOKENS_TAILWIND4_IMPORT_LINE}`);
2421
+ const missing = TAILWIND4_COMPONENT_PACKAGES.filter(
2422
+ (name) => pkg !== void 0 && hasDependency2(pkg, name) !== void 0 && !hasComponentTailwindImport(css, name) && !hasComponentSource(css, name)
2423
+ );
2424
+ if (missing.length > 0) {
2425
+ issues.push(`\uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCE94 \uACBD\uB85C(tailwind.css)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${missing.map(componentTailwindImportLine).join(" ")}`);
2426
+ }
2427
+ if (hasEntry) {
2428
+ const defaults = findCreateNextAppDefaults(css);
2429
+ 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`);
2430
+ if (versions.some((v) => v.name === TAILWIND4_AXIS_PACKAGES[0])) {
2431
+ 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`);
2344
2432
  }
2345
- };
2433
+ }
2434
+ for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2435
+ if (hasComponentTailwindImport(css, name) && versions.some((v) => v.name === name)) {
2436
+ issues.push(`${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC ${name} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2437
+ }
2438
+ }
2439
+ return issues;
2346
2440
  }
2347
-
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) {
2352
- for (const field of ["dependencies", "devDependencies"]) {
2353
- const deps = pkg[field];
2354
- if (typeof deps === "object" && deps !== null && name in deps) {
2355
- return String(deps[name]);
2441
+ function checkTailwindSetup(baseDirInput) {
2442
+ const baseDir = resolve(baseDirInput);
2443
+ const pkg = readPackageJson(baseDir);
2444
+ const tailwind = detectTailwind(baseDir, pkg);
2445
+ if (tailwind.major !== void 0 && tailwind.major >= 4) {
2446
+ const label = `Tailwind ${tailwind.major}`;
2447
+ for (const candidate of TAILWIND_CSS_CANDIDATES) {
2448
+ const file = join(baseDir, candidate);
2449
+ if (!existsSync2(file)) continue;
2450
+ const css = readFileSync2(file, "utf8");
2451
+ if (!hasTailwindImport(css)) {
2452
+ return {
2453
+ name: "tailwind.config",
2454
+ severity: "warn",
2455
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4`
2456
+ };
2457
+ }
2458
+ const versions = axisPackagesBelowTailwind4(pkg, baseDir);
2459
+ const issues = tailwind4Issues(css, pkg, versions);
2460
+ if (issues.length > 0) {
2461
+ return {
2462
+ name: "tailwind.config",
2463
+ severity: "warn",
2464
+ message: `${label}: ${candidate}\uC5D0 ${issues.join(" / ")}${tailwind4VersionNote(versions)}`
2465
+ };
2466
+ }
2467
+ return {
2468
+ name: "tailwind.config",
2469
+ severity: "pass",
2470
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uC640 AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC2B5\uB2C8\uB2E4`
2471
+ };
2356
2472
  }
2473
+ return {
2474
+ name: "tailwind.config",
2475
+ severity: "warn",
2476
+ message: `${label}: @import "tailwindcss" \uB97C \uD655\uC778\uD560 CSS \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4`
2477
+ };
2357
2478
  }
2358
- return void 0;
2479
+ for (const file of TAILWIND_CONFIG_FILES) {
2480
+ const path18 = join(baseDir, file);
2481
+ if (!existsSync2(path18)) continue;
2482
+ const content = readFileSync2(path18, "utf8");
2483
+ if (hasTokensPresetReference(content)) {
2484
+ const code = stripJsComments(content);
2485
+ if (isCommonJsConfig(file, code) && hasBareCjsPresetRequire(code)) {
2486
+ const gate = packageVersionGate(baseDir, pkg, "@axis-ds/tokens", TOKENS_CJS_PRESET_FIXED_VERSION);
2487
+ if (gate.status !== "ok") {
2488
+ const why = gate.status === "below" ? `@axis-ds/tokens ${versionGateLabel(gate)} \uC5D0\uC11C\uB294 CommonJS \uC758 require("${TOKENS_TAILWIND3_PRESET}") \uAC00 preset \uC774 \uC544\uB2C8\uB77C { default } \uB97C \uB3CC\uB824\uC918 \uC0C9 \xB7 keyframes \uAC00 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4` : `@axis-ds/tokens \uBC84\uC804\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${versionGateLabel(gate)}). ${TOKENS_CJS_PRESET_FIXED_VERSION} \uBBF8\uB9CC\uC774\uBA74 CommonJS \uC758 require("${TOKENS_TAILWIND3_PRESET}") \uB85C\uB294 preset \uC774 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4`;
2489
+ return {
2490
+ name: "tailwind.config",
2491
+ severity: "warn",
2492
+ message: `${file}\uC5D0 AXIS preset \uCC38\uC870\uAC00 \uC788\uC9C0\uB9CC ${why} \u2192 ${TOKENS_TAILWIND3_CJS_REQUIRE} \uB85C \uBC14\uAFB8\uAC70\uB098 @axis-ds/tokens ${TOKENS_CJS_PRESET_FIXED_VERSION} \uC774\uC0C1`
2493
+ };
2494
+ }
2495
+ }
2496
+ return { name: "tailwind.config", severity: "pass", message: `${file}${PRESET_PASS_SUFFIX}` };
2497
+ }
2498
+ const ownMapping = content.includes("--background") || content.includes("hsl(var(");
2499
+ return {
2500
+ name: "tailwind.config",
2501
+ severity: "warn",
2502
+ 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)`
2503
+ };
2504
+ }
2505
+ return { name: "tailwind.config", severity: "fail", message: "tailwind.config \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" };
2359
2506
  }
2360
- function isPlainObject(value) {
2361
- return typeof value === "object" && value !== null && !Array.isArray(value);
2507
+ function readAxisConfigLike(baseDir) {
2508
+ try {
2509
+ const parsed = JSON.parse(readFileSync2(join(baseDir, "axis.config.json"), "utf8"));
2510
+ return isPlainObject2(parsed) ? parsed : void 0;
2511
+ } catch {
2512
+ return void 0;
2513
+ }
2362
2514
  }
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] };
2515
+ function contentGlobToRegExp(glob) {
2516
+ const g = glob.trim().replace(/^\.\//, "");
2517
+ let re = "";
2518
+ for (let i = 0; i < g.length; i++) {
2519
+ const c = g[i];
2520
+ if (c === "*") {
2521
+ if (g[i + 1] === "*") {
2522
+ const slash = g[i + 2] === "/";
2523
+ re += slash ? "(?:.*/)?" : ".*";
2524
+ i += slash ? 2 : 1;
2525
+ } else re += "[^/]*";
2526
+ } else if (c === "?") re += "[^/]";
2527
+ else if (c === "{") {
2528
+ const end = g.indexOf("}", i);
2529
+ if (end === -1) {
2530
+ re += "\\{";
2531
+ continue;
2532
+ }
2533
+ re += `(?:${g.slice(i + 1, end).split(",").map((part) => part.replace(/[.+^$()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*")).join("|")})`;
2534
+ i = end;
2535
+ } else if (c === "[") {
2536
+ const end = g.indexOf("]", i);
2537
+ if (end === -1) re += "\\[";
2538
+ else {
2539
+ re += g.slice(i, end + 1);
2540
+ i = end;
2541
+ }
2542
+ } else re += c.replace(/[.+^$()|\\]/g, "\\$&");
2543
+ }
2544
+ return new RegExp(`^${re}$`);
2369
2545
  }
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 };
2546
+ function balancedSlice(src, start) {
2547
+ const open = src[start];
2548
+ const close = open === "[" ? "]" : "}";
2549
+ let depth = 0;
2550
+ for (let i = start; i < src.length; i++) {
2551
+ const c = src[i];
2552
+ if (c === '"' || c === "'" || c === "`") {
2553
+ let j = i + 1;
2554
+ while (j < src.length && src[j] !== c) j += src[j] === "\\" ? 2 : 1;
2555
+ i = j;
2556
+ continue;
2382
2557
  }
2383
- default:
2384
- return { lower: v.major, upper: v.major };
2558
+ if (c === open) depth++;
2559
+ else if (c === close && --depth === 0) return src.slice(start, i + 1);
2385
2560
  }
2561
+ return void 0;
2386
2562
  }
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;
2563
+ function stringLiterals(src) {
2564
+ return [...src.matchAll(/(["'`])((?:\\.|(?!\1)[^\\])*)\1/g)].map((m) => m[2]).filter((s) => !s.includes("${"));
2565
+ }
2566
+ function readTailwindContent(source) {
2567
+ const src = stripJsComments(source);
2568
+ const key = /\bcontent\s*:\s*/g;
2569
+ for (const m of src.matchAll(key)) {
2570
+ let at = (m.index ?? 0) + m[0].length;
2571
+ let block = src[at] === "[" || src[at] === "{" ? balancedSlice(src, at) : void 0;
2572
+ if (block === void 0) continue;
2573
+ if (block.startsWith("{")) {
2574
+ const files = /\bfiles\s*:\s*\[/.exec(block);
2575
+ if (!files) return { globs: [], dynamic: true };
2576
+ at = files.index + files[0].length - 1;
2577
+ block = balancedSlice(block, at);
2578
+ if (block === void 0) return { globs: [], dynamic: true };
2579
+ }
2580
+ const inner = block.slice(1, -1);
2581
+ const globs = stringLiterals(inner);
2582
+ const rest = inner.replace(/(["'`])((?:\\.|(?!\1)[^\\])*)\1/g, "").replace(/[\s,]/g, "");
2583
+ return { globs, dynamic: rest !== "" };
2394
2584
  }
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;
2585
+ return void 0;
2586
+ }
2587
+ function normalizeRel(p) {
2588
+ const s = p.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
2589
+ if (s === "" || s.startsWith("../") || s.startsWith("/") || /^[A-Za-z]:/.test(s)) return void 0;
2590
+ return s;
2591
+ }
2592
+ function inspectTailwindContent(baseDirInput) {
2593
+ const baseDir = resolve(baseDirInput);
2594
+ const pkg = readPackageJson(baseDir);
2595
+ const tailwind = detectTailwind(baseDir, pkg);
2596
+ if (tailwind.major !== void 0 && tailwind.major >= 4) return null;
2597
+ const configFile = TAILWIND_CONFIG_FILES.find((f) => existsSync2(join(baseDir, f)));
2598
+ if (configFile === void 0) return null;
2599
+ const targets = [];
2600
+ const componentsDir = readAxisConfigLike(baseDir)?.componentsDir;
2601
+ const rel3 = typeof componentsDir === "string" ? normalizeRel(componentsDir) : void 0;
2602
+ if (rel3 !== void 0) {
2603
+ targets.push({ label: `componentsDir(${componentsDir})`, sample: `${rel3}/button.tsx`, suggest: `./${rel3}/**/*.{ts,tsx}` });
2401
2604
  }
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);
2605
+ for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2606
+ if (pkg !== void 0 && hasDependency2(pkg, name) !== void 0) {
2607
+ targets.push({ label: name, sample: `node_modules/${name}/dist/index.js`, suggest: `./node_modules/${name}/dist/**/*.{js,mjs}` });
2608
+ }
2413
2609
  }
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);
2610
+ if (targets.length === 0) return null;
2611
+ const read = readTailwindContent(readFileSync2(join(baseDir, configFile), "utf8"));
2612
+ const patterns = (read?.globs ?? []).filter((g) => !g.trim().startsWith("!")).map(contentGlobToRegExp);
2613
+ return {
2614
+ configFile,
2615
+ unreadable: read === void 0,
2616
+ dynamic: read?.dynamic ?? false,
2617
+ targets: targets.map((t) => ({ ...t, covered: patterns.some((re) => re.test(t.sample)) }))
2618
+ };
2422
2619
  }
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]);
2620
+ function checkTailwindContent(baseDir) {
2621
+ const r = inspectTailwindContent(baseDir);
2622
+ if (r === null) return null;
2623
+ const missing = r.targets.filter((t) => !t.covered);
2624
+ if (missing.length === 0) {
2625
+ return {
2626
+ name: "tailwind.content",
2627
+ severity: "pass",
2628
+ message: `${r.configFile}\uC758 content \uAC00 ${r.targets.map((t) => t.label).join(" \xB7 ")} \uB97C \uB36E\uC2B5\uB2C8\uB2E4`
2629
+ };
2630
+ }
2631
+ const list = missing.map((t) => `${t.label} \u2192 "${t.suggest}"`).join(" \xB7 ");
2632
+ if (r.unreadable || r.dynamic) {
2633
+ return {
2634
+ name: "tailwind.content",
2635
+ severity: "warn",
2636
+ 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}`
2637
+ };
2638
+ }
2639
+ return {
2640
+ name: "tailwind.content",
2641
+ severity: "fail",
2642
+ 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}`
2643
+ };
2429
2644
  }
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 };
2645
+ var ANIMATION_PLUGIN_TAILWIND3 = "tailwindcss-animate";
2646
+ var ANIMATION_PLUGIN_TAILWIND4 = "tw-animate-css";
2647
+ var ANIMATION_SCAN_ENTRY_LIMIT = 2e3;
2648
+ var ANIMATION_SCAN_DEPTH_LIMIT = 8;
2649
+ var ANIMATION_SCAN_FILE_BYTES = 512 * 1024;
2650
+ function componentsUseAnimation(baseDir, componentsDir) {
2651
+ const rel3 = normalizeRel(componentsDir);
2652
+ if (rel3 === void 0) return false;
2653
+ const stack = [[join(baseDir, rel3), 0]];
2654
+ let visited = 0;
2655
+ while (stack.length > 0) {
2656
+ const [dir, depth] = stack.pop();
2657
+ let names;
2658
+ try {
2659
+ names = readdirSync(dir);
2660
+ } catch {
2661
+ continue;
2662
+ }
2663
+ for (const name of names) {
2664
+ if (++visited > ANIMATION_SCAN_ENTRY_LIMIT) return false;
2665
+ const p = join(dir, name);
2666
+ let st;
2667
+ try {
2668
+ st = lstatSync(p);
2669
+ } catch {
2670
+ continue;
2671
+ }
2672
+ if (st.isSymbolicLink()) continue;
2673
+ if (st.isDirectory()) {
2674
+ if (name !== "node_modules" && depth < ANIMATION_SCAN_DEPTH_LIMIT) stack.push([p, depth + 1]);
2675
+ } else if (st.isFile() && st.size <= ANIMATION_SCAN_FILE_BYTES && /\.(tsx|ts|jsx|js)$/.test(name)) {
2676
+ if (/\banimate-(?:in|out)\b/.test(readFileSync2(p, "utf8"))) return true;
2444
2677
  }
2445
- return found;
2446
2678
  }
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
2679
  }
2453
- return { source: "none" };
2454
- }
2455
- var TAILWIND_CONFIG_FILES = [
2456
- "tailwind.config.ts",
2457
- "tailwind.config.js",
2458
- "tailwind.config.mjs",
2459
- "tailwind.config.cjs"
2460
- ];
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, "");
2471
- }
2472
- function hasTailwindImport(css) {
2473
- return TAILWIND_IMPORT.test(stripCssComments(css));
2474
- }
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));
2680
+ return false;
2480
2681
  }
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];
2682
+ function checkAnimationPlugin(baseDirInput) {
2683
+ const baseDir = resolve(baseDirInput);
2684
+ const pkg = readPackageJson(baseDir);
2685
+ const config = readAxisConfigLike(baseDir);
2686
+ const declared = pkg !== void 0 && hasDependency2(pkg, "@axis-ds/ui-react") !== void 0;
2687
+ const copied = typeof config?.componentsDir === "string" && componentsUseAnimation(baseDir, config.componentsDir);
2688
+ if (!declared && !copied) return null;
2689
+ const by = declared ? "@axis-ds/ui-react \uC120\uC5B8" : "componentsDir \uC758 animate-in \uB958 \uD074\uB798\uC2A4";
2690
+ const tailwind = detectTailwind(baseDir, pkg);
2691
+ if (tailwind.major !== void 0 && tailwind.major >= 4) {
2692
+ const file = TAILWIND_CSS_CANDIDATES.find((c) => existsSync2(join(baseDir, c)));
2693
+ const css = file !== void 0 ? stripCssComments(readFileSync2(join(baseDir, file), "utf8")) : "";
2694
+ const ok = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${ANIMATION_PLUGIN_TAILWIND4}["']`).test(css) || new RegExp(`@plugin\\s+["']${ANIMATION_PLUGIN_TAILWIND3}["']`).test(css);
2695
+ return ok ? { name: "animation", severity: "pass", message: `${file}\uC5D0 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4` } : {
2696
+ name: "animation",
2697
+ severity: "warn",
2698
+ 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}";`
2699
+ };
2700
+ }
2701
+ const configFile = TAILWIND_CONFIG_FILES.find((f) => existsSync2(join(baseDir, f)));
2702
+ const source = configFile !== void 0 ? stripJsComments(readFileSync2(join(baseDir, configFile), "utf8")) : "";
2703
+ const plugins = configFile !== void 0 ? readAnimationPluginUse(source) : "absent";
2704
+ const pluginDeclared = pkg !== void 0 && hasDependency2(pkg, ANIMATION_PLUGIN_TAILWIND3) !== void 0;
2705
+ const pluginInstalled = pluginDeclared && (isPnpProject(baseDir) || findInstalledPackage(baseDir, ANIMATION_PLUGIN_TAILWIND3) !== void 0);
2706
+ if (plugins === "found" && pluginDeclared && pluginInstalled) {
2707
+ return { name: "animation", severity: "pass", message: `${configFile}\uC758 plugins \uC5D0 ${ANIMATION_PLUGIN_TAILWIND3} \uAC00 \uC788\uC2B5\uB2C8\uB2E4` };
2708
+ }
2709
+ const cfg = configFile ?? "tailwind.config";
2710
+ const reasons = [];
2711
+ if (!pluginDeclared) reasons.push(`${ANIMATION_PLUGIN_TAILWIND3} \uC120\uC5B8 \uC5C6\uC74C`);
2712
+ else if (!pluginInstalled) reasons.push(`${ANIMATION_PLUGIN_TAILWIND3} \uBBF8\uC124\uCE58(\uC120\uC5B8\uB9CC \uC788\uC74C)`);
2713
+ if (plugins === "dynamic") reasons.push(`${cfg} plugins \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD568`);
2714
+ else if (plugins !== "found") reasons.push(`${cfg} plugins \uC5D0 \uC5C6\uC74C`);
2715
+ return {
2716
+ name: "animation",
2717
+ severity: "warn",
2718
+ message: `\uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(${by}, ${reasons.join(" \xB7 ")}): 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}")`
2719
+ };
2484
2720
  }
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` };
2721
+ function readAnimationPluginUse(source) {
2722
+ const name = escapeRegExp(ANIMATION_PLUGIN_TAILWIND3);
2723
+ const idents = /* @__PURE__ */ new Set();
2724
+ for (const m of source.matchAll(new RegExp(`\\bimport\\s+([A-Za-z_$][\\w$]*)\\s+from\\s*(["'])${name}\\2`, "g"))) idents.add(m[1]);
2725
+ for (const m of source.matchAll(
2726
+ new RegExp(`\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*require\\s*\\(\\s*(["'])${name}\\2\\s*\\)`, "g")
2727
+ )) {
2728
+ idents.add(m[1]);
2729
+ }
2730
+ const requireCall = new RegExp(`^require\\s*\\(\\s*(["'])${name}\\1\\s*\\)(?:\\s*\\.\\s*default)?$`);
2731
+ let sawDynamic = false;
2732
+ for (const m of source.matchAll(/\bplugins\b\s*(:)?\s*/g)) {
2733
+ if (m[1] === void 0) {
2734
+ const before = source.slice(Math.max(0, (m.index ?? 0) - 1), m.index ?? 0);
2735
+ if (/[{,\s]/.test(before) || before === "") sawDynamic = true;
2736
+ continue;
2493
2737
  }
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)` };
2738
+ const at = (m.index ?? 0) + m[0].length;
2739
+ if (source[at] !== "[") {
2740
+ sawDynamic = true;
2741
+ continue;
2742
+ }
2743
+ const block = balancedSlice(source, at);
2744
+ if (block === void 0) {
2745
+ sawDynamic = true;
2746
+ continue;
2747
+ }
2748
+ const elements = splitTopLevel(block.slice(1, -1));
2749
+ for (const el of elements) {
2750
+ const e = el.trim();
2751
+ if (e === "") continue;
2752
+ if (requireCall.test(e) || idents.has(e) || idents.has(e.replace(/\(\s*\)$/, ""))) return "found";
2753
+ if (/^require\s*\(\s*(["'])[^"']+\1\s*\)(?:\s*\.\s*default)?$/.test(e)) continue;
2754
+ sawDynamic = true;
2497
2755
  }
2498
- return { name: "globals.css", severity: "warn", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4` };
2499
2756
  }
2500
- return { name: "globals.css", severity: "fail", message: "globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" };
2757
+ return sawDynamic ? "dynamic" : "absent";
2501
2758
  }
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 };
2759
+ function splitTopLevel(body) {
2760
+ const out = [];
2761
+ let depth = 0;
2762
+ let start = 0;
2763
+ for (let i = 0; i < body.length; i++) {
2764
+ const c = body[i];
2765
+ if (c === '"' || c === "'" || c === "`") {
2766
+ let j = i + 1;
2767
+ while (j < body.length && body[j] !== c) j += body[j] === "\\" ? 2 : 1;
2768
+ i = j;
2769
+ continue;
2770
+ }
2771
+ if (c === "(" || c === "[" || c === "{") depth++;
2772
+ else if (c === ")" || c === "]" || c === "}") depth--;
2773
+ else if (c === "," && depth === 0) {
2774
+ out.push(body.slice(start, i));
2775
+ start = i + 1;
2776
+ }
2777
+ }
2778
+ out.push(body.slice(start));
2779
+ return out;
2510
2780
  }
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];
2781
+
2782
+ // src/template/template-apply.ts
2783
+ async function templateApplyWarnings(baseDir, detail) {
2784
+ const warnings = [];
2785
+ const pkgPath = path9.join(baseDir, "package.json");
2786
+ const templateRange = detail.devDependencies?.tailwindcss ?? detail.dependencies?.tailwindcss;
2787
+ const templateMajor = templateRange !== void 0 ? tailwindMajorFromRange(templateRange) : void 0;
2788
+ if (templateMajor !== void 0 && await fs8.pathExists(pkgPath)) {
2789
+ let pkg;
2790
+ try {
2791
+ pkg = await fs8.readJSON(pkgPath);
2792
+ } catch {
2793
+ pkg = void 0;
2794
+ }
2795
+ const project = detectTailwind(baseDir, pkg);
2796
+ if (project.major !== void 0 && project.major !== templateMajor) {
2797
+ warnings.push(
2798
+ `\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.`
2799
+ );
2800
+ }
2514
2801
  }
2515
- return Number(b.pre) - Number(a.pre);
2802
+ const usesRootApp = detail.files.some((f) => f.path.startsWith("app/"));
2803
+ if (usesRootApp && await fs8.pathExists(path9.join(baseDir, "src", "app"))) {
2804
+ warnings.push(
2805
+ "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."
2806
+ );
2807
+ }
2808
+ return warnings;
2516
2809
  }
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;
2810
+ async function applyTemplate(slug, options) {
2811
+ const spinner = ora3(`'${slug}' \uD15C\uD50C\uB9BF \uAC00\uC838\uC624\uB294 \uC911...`).start();
2812
+ let detail;
2813
+ try {
2814
+ detail = await fetchTemplateDetail(slug);
2815
+ spinner.succeed(`'${detail.name}' \uD15C\uD50C\uB9BF \uB85C\uB4DC \uC644\uB8CC (${detail.files.length}\uAC1C \uD30C\uC77C)`);
2816
+ } catch (err) {
2817
+ spinner.fail(err.message);
2818
+ process.exit(1);
2525
2819
  }
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 };
2820
+ const baseDir = path9.resolve(options.dir || ".");
2821
+ for (const w of await templateApplyWarnings(baseDir, detail)) {
2822
+ console.log(chalk5.yellow(`! ${w}`));
2823
+ }
2824
+ const conflicts = [];
2825
+ for (const file of detail.files) {
2826
+ const dest = path9.join(baseDir, file.path);
2827
+ if (await fs8.pathExists(dest)) {
2828
+ conflicts.push(file.path);
2540
2829
  }
2541
- if (candidate && (!lower || compareLower(candidate, lower) > 0)) lower = candidate;
2542
2830
  }
2543
- return lower;
2544
- }
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;
2831
+ if (conflicts.length > 0 && !options.yes && !options.dryRun) {
2832
+ console.log(chalk5.yellow(`
2833
+ \uAE30\uC874 \uD30C\uC77C\uACFC \uCDA9\uB3CC\uD558\uB294 ${conflicts.length}\uAC1C \uD30C\uC77C:`));
2834
+ for (const c of conflicts) {
2835
+ console.log(chalk5.gray(` - ${c}`));
2836
+ }
2837
+ const { proceed } = await prompts({
2838
+ type: "confirm",
2839
+ name: "proceed",
2840
+ message: "\uCDA9\uB3CC \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
2841
+ initial: false
2842
+ });
2843
+ if (!proceed) {
2844
+ console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2845
+ return;
2846
+ }
2847
+ }
2848
+ if (options.dryRun) {
2849
+ console.log(chalk5.blue("\n[\uBBF8\uB9AC\uBCF4\uAE30] \uC0DD\uC131\uB420 \uD30C\uC77C:"));
2850
+ for (const file of detail.files) {
2851
+ const dest = path9.join(baseDir, file.path);
2852
+ const exists = conflicts.includes(file.path);
2853
+ const label = exists ? chalk5.yellow("\uB36E\uC5B4\uC4F0\uAE30") : chalk5.green("\uC0DD\uC131");
2854
+ console.log(` ${label} ${dest}`);
2855
+ }
2856
+ if (detail.postInstall?.length) {
2857
+ console.log(chalk5.blue("\n[\uBBF8\uB9AC\uBCF4\uAE30] postInstall \uD328\uCE58:"));
2858
+ for (const p of detail.postInstall) {
2859
+ console.log(chalk5.gray(` ${p.type}: ${p.file}`));
2860
+ }
2861
+ }
2862
+ return;
2863
+ }
2864
+ const writeSpinner = ora3("\uD30C\uC77C \uC0DD\uC131 \uC911...").start();
2865
+ for (const file of detail.files) {
2866
+ const dest = path9.join(baseDir, file.path);
2867
+ await fs8.ensureDir(path9.dirname(dest));
2868
+ await fs8.writeFile(dest, file.content);
2869
+ }
2870
+ writeSpinner.succeed(`${detail.files.length}\uAC1C \uD30C\uC77C \uC0DD\uC131 \uC644\uB8CC`);
2871
+ if (detail.postInstall?.length) {
2872
+ console.log(chalk5.blue("\npostInstall \uD328\uCE58 \uC2E4\uD589 \uC911..."));
2873
+ await runPostInstall(detail.postInstall, baseDir);
2874
+ }
2875
+ if (!options.skipDeps) {
2876
+ const allDeps = { ...detail.dependencies, ...detail.devDependencies };
2877
+ const depList = Object.entries(allDeps);
2878
+ if (depList.length > 0) {
2879
+ console.log(chalk5.yellow("\n\uD544\uC694\uD55C \uC758\uC874\uC131:"));
2880
+ const deps = Object.entries(detail.dependencies || {}).map(([k, v]) => `${k}@${v}`).join(" ");
2881
+ const devDeps = Object.entries(detail.devDependencies || {}).map(([k, v]) => `${k}@${v}`).join(" ");
2882
+ if (deps) {
2883
+ console.log(chalk5.cyan(` pnpm add ${deps}`));
2884
+ }
2885
+ if (devDeps) {
2886
+ console.log(chalk5.cyan(` pnpm add -D ${devDeps}`));
2887
+ }
2888
+ }
2889
+ }
2890
+ console.log(chalk5.green(`
2891
+ '${detail.name}' \uD15C\uD50C\uB9BF \uC801\uC6A9 \uC644\uB8CC!`));
2892
+ }
2893
+ async function initTemplate(slug, options) {
2894
+ let selectedSlug = slug;
2895
+ if (!selectedSlug) {
2896
+ const { fetchTemplateIndex: fetchTemplateIndex2 } = await import("./registry-client-6WSGKPF2.js");
2897
+ const index = await fetchTemplateIndex2();
2898
+ const { choice } = await prompts({
2899
+ type: "select",
2900
+ name: "choice",
2901
+ message: "\uC0AC\uC6A9\uD560 \uD15C\uD50C\uB9BF\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
2902
+ choices: index.templates.map((t) => ({
2903
+ title: `${t.name} (${t.category})`,
2904
+ description: t.description,
2905
+ value: t.slug
2906
+ }))
2907
+ });
2908
+ if (!choice) {
2909
+ console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2910
+ return;
2911
+ }
2912
+ selectedSlug = choice;
2913
+ }
2914
+ let targetDir = options.dir;
2915
+ if (!targetDir) {
2916
+ const { dir } = await prompts({
2917
+ type: "text",
2918
+ name: "dir",
2919
+ message: "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC:",
2920
+ initial: selectedSlug
2921
+ });
2922
+ if (!dir) {
2923
+ console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2924
+ return;
2925
+ }
2926
+ targetDir = dir;
2927
+ }
2928
+ const resolved = path9.resolve(targetDir);
2929
+ if (await fs8.pathExists(resolved)) {
2930
+ const entries = await fs8.readdir(resolved);
2931
+ if (entries.length > 0) {
2932
+ const { proceed } = await prompts({
2933
+ type: "confirm",
2934
+ name: "proceed",
2935
+ message: `'${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?`,
2936
+ initial: false
2937
+ });
2938
+ if (!proceed) return;
2939
+ }
2940
+ } else {
2941
+ await fs8.ensureDir(resolved);
2942
+ }
2943
+ await applyTemplate(selectedSlug, { dir: targetDir, yes: true });
2944
+ const configPath = path9.join(resolved, "axis.config.json");
2945
+ if (!await fs8.pathExists(configPath)) {
2946
+ const axisConfig = createTemplateConfig(selectedSlug);
2947
+ await fs8.writeJSON(configPath, axisConfig, { spaces: 2 });
2948
+ console.log(chalk5.green("axis.config.json \uC0DD\uC131 \uC644\uB8CC"));
2949
+ }
2950
+ console.log(chalk5.blue(`
2951
+ \uB2E4\uC74C \uB2E8\uACC4:`));
2952
+ console.log(chalk5.cyan(` cd ${targetDir}`));
2953
+ console.log(chalk5.cyan(` pnpm install`));
2954
+ console.log(chalk5.cyan(` pnpm dev`));
2955
+ }
2956
+
2957
+ // src/template/template-diff.ts
2958
+ import fs9 from "fs-extra";
2959
+ import path10 from "path";
2960
+ import chalk6 from "chalk";
2961
+ import ora4 from "ora";
2962
+ async function diffTemplate(slug, options) {
2963
+ const spinner = ora4(`'${slug}' \uD15C\uD50C\uB9BF \uAC00\uC838\uC624\uB294 \uC911...`).start();
2964
+ let detail;
2965
+ try {
2966
+ detail = await fetchTemplateDetail(slug);
2967
+ spinner.succeed(`'${detail.name}' \uB85C\uB4DC \uC644\uB8CC`);
2968
+ } catch (err) {
2969
+ spinner.fail(err.message);
2970
+ process.exit(1);
2971
+ }
2972
+ const baseDir = path10.resolve(options.dir || ".");
2973
+ const entries = [];
2974
+ for (const file of detail.files) {
2975
+ const localPath = path10.join(baseDir, file.path);
2976
+ if (!await fs9.pathExists(localPath)) {
2977
+ entries.push({ path: file.path, status: "added" });
2978
+ continue;
2979
+ }
2980
+ const localContent = await fs9.readFile(localPath, "utf-8");
2981
+ if (localContent === file.content) {
2982
+ entries.push({ path: file.path, status: "unchanged" });
2983
+ } else {
2984
+ entries.push({
2985
+ path: file.path,
2986
+ status: "modified",
2987
+ localLines: localContent.split("\n").length,
2988
+ remoteLines: file.content.split("\n").length
2989
+ });
2990
+ }
2991
+ }
2992
+ if (options.verbose) {
2993
+ const templatePaths = new Set(detail.files.map((f) => f.path));
2994
+ const localFiles = await collectLocalFiles(baseDir);
2995
+ for (const lf of localFiles) {
2996
+ if (!templatePaths.has(lf)) {
2997
+ entries.push({ path: lf, status: "extra" });
2998
+ }
2999
+ }
3000
+ }
3001
+ console.log(chalk6.blue(`
3002
+ '${detail.name}' \uD15C\uD50C\uB9BF diff \uACB0\uACFC:
3003
+ `));
3004
+ const statusIcon = {
3005
+ added: chalk6.green("+ \uCD94\uAC00 \uD544\uC694"),
3006
+ modified: chalk6.yellow("~ \uBCC0\uACBD\uB428"),
3007
+ unchanged: chalk6.gray("= \uB3D9\uC77C"),
3008
+ extra: chalk6.cyan("? \uB85C\uCEEC \uC804\uC6A9")
3009
+ };
3010
+ let added = 0;
3011
+ let modified = 0;
3012
+ let unchanged = 0;
3013
+ let extra = 0;
3014
+ for (const entry of entries) {
3015
+ const icon = statusIcon[entry.status];
3016
+ console.log(` ${icon.padEnd(28)} ${entry.path}`);
3017
+ if (entry.status === "modified" && options.verbose) {
3018
+ console.log(
3019
+ chalk6.gray(
3020
+ ` \uB85C\uCEEC ${entry.localLines}\uC904 / \uC6D0\uBCF8 ${entry.remoteLines}\uC904`
3021
+ )
3022
+ );
3023
+ }
3024
+ switch (entry.status) {
3025
+ case "added":
3026
+ added++;
3027
+ break;
3028
+ case "modified":
3029
+ modified++;
3030
+ break;
3031
+ case "unchanged":
3032
+ unchanged++;
3033
+ break;
3034
+ case "extra":
3035
+ extra++;
3036
+ break;
3037
+ }
3038
+ }
3039
+ console.log(chalk6.blue("\n\uC694\uC57D:"));
3040
+ console.log(
3041
+ ` \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))}` : ""}`
3042
+ );
3043
+ }
3044
+ async function collectLocalFiles(dir, base) {
3045
+ const root = base || dir;
3046
+ const ignore = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", "dist"]);
3047
+ const results = [];
3048
+ let entries;
3049
+ try {
3050
+ entries = await fs9.readdir(dir, { withFileTypes: true });
3051
+ } catch {
3052
+ return results;
3053
+ }
3054
+ for (const entry of entries) {
3055
+ if (ignore.has(entry.name)) continue;
3056
+ const fullPath = path10.join(dir, entry.name);
3057
+ if (entry.isDirectory()) {
3058
+ const nested = await collectLocalFiles(fullPath, root);
3059
+ results.push(...nested);
3060
+ } else {
3061
+ results.push(path10.relative(root, fullPath).replace(/\\/g, "/"));
3062
+ }
3063
+ }
3064
+ return results;
3065
+ }
3066
+
3067
+ // src/template/template-check.ts
3068
+ import fs10 from "fs-extra";
3069
+ import path11 from "path";
3070
+ import chalk7 from "chalk";
3071
+
3072
+ // src/theme/brand-core.ts
3073
+ import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
3074
+ import { createRequire } from "module";
3075
+ import { dirname, join as join2, relative, resolve as resolve2, sep } from "path";
3076
+ var TOKENS_PACKAGE = "@axis-ds/tokens";
3077
+ var BRAND_NAME_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
3078
+ var MIN_BRAND_TOKENS_VERSION = "1.3.0";
3079
+ var TOKENS_DEPENDENCY_MIN_VERSION = "1.6.0";
3080
+ var KEY_TOKENS = [
3081
+ "--primary",
3082
+ "--primary-foreground",
3083
+ "--background",
3084
+ "--foreground",
3085
+ "--ring",
3086
+ "--radius",
3087
+ "--axis-surface-default",
3088
+ "--axis-text-primary",
3089
+ "--axis-text-brand",
3090
+ "--axis-button-bg-default",
3091
+ "--axis-border-focus",
3092
+ "--axis-font-family-sans"
3093
+ ];
3094
+ var BRAND_ADJUSTMENT_REFERENCE = "WI-0025 PRD \uBD80\uB85D B (docs/workitems/WI-0025-brand-theme/prd.md)";
3095
+ function readJsonFile(file) {
3096
+ return JSON.parse(readFileSync3(file, "utf8"));
3097
+ }
3098
+ function errorCode(err) {
3099
+ return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
3100
+ }
3101
+ function findTokensPackageJson(baseDir) {
3102
+ let dir = resolve2(baseDir);
3103
+ for (; ; ) {
3104
+ const candidate = join2(dir, "node_modules", TOKENS_PACKAGE, "package.json");
3105
+ if (existsSync3(candidate)) return candidate;
3106
+ const parent = dirname(dir);
3107
+ if (parent === dir) return void 0;
3108
+ dir = parent;
3109
+ }
3110
+ }
3111
+ function projectRequire(baseDir) {
3112
+ return createRequire(join2(resolve2(baseDir), "package.json"));
3113
+ }
3114
+ function tokensSpecifier(subpath) {
3115
+ return `${TOKENS_PACKAGE}/${subpath.replace(/^\.\//, "")}`;
3116
+ }
3117
+ function resolveTokens(baseDir) {
3118
+ const pkgJson = findTokensPackageJson(baseDir);
3119
+ if (!pkgJson) return { status: "not-installed" };
3120
+ const pkg = readJsonFile(pkgJson);
3121
+ const version = typeof pkg.version === "string" ? pkg.version : void 0;
3122
+ try {
3123
+ const indexPath = projectRequire(baseDir).resolve(tokensSpecifier("./brands"));
3124
+ return { status: "ok", version, indexPath };
3125
+ } catch (err) {
3126
+ const code = errorCode(err);
3127
+ if (code === "ERR_PACKAGE_PATH_NOT_EXPORTED") return { status: "unsupported", version };
3128
+ if (code === "MODULE_NOT_FOUND") return { status: "not-built", version };
3129
+ throw err;
3130
+ }
2550
3131
  }
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;
3132
+ function brandImportPath(entry) {
3133
+ return tokensSpecifier(entry.css);
2556
3134
  }
2557
- function installedVersion(baseDir, name) {
3135
+ function isFile2(file) {
2558
3136
  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;
3137
+ return statSync2(file).isFile();
2561
3138
  } catch {
2562
- return void 0;
3139
+ return false;
2563
3140
  }
2564
3141
  }
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;
3142
+ function findGlobalCss(baseDir, config) {
3143
+ const root = resolve2(baseDir);
3144
+ const result = {};
3145
+ const configured = config?.globalCss;
3146
+ if (typeof configured === "string" && configured.trim() !== "") {
3147
+ const file = resolve2(root, configured);
3148
+ if (isFile2(file)) return { path: file };
3149
+ result.configuredMissing = configured;
3150
+ }
3151
+ for (const candidate of GLOBAL_CSS_CANDIDATES) {
3152
+ const file = join2(root, candidate);
3153
+ if (isFile2(file)) {
3154
+ result.path = file;
3155
+ break;
2578
3156
  }
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
3157
  }
2583
- return out;
3158
+ return result;
2584
3159
  }
2585
- function axisPackageVersionLabel(issue) {
2586
- return issue.source === "installed" ? `${issue.name} \uC124\uCE58\uBCF8 ${issue.installed}` : `${issue.name} ${issue.range}`;
3160
+ function findBrandImports(css) {
3161
+ const names = [];
3162
+ const pattern = /@import\s+(?:url\(\s*)?["']?@axis-ds\/tokens\/css\/brands\/([a-z][a-z0-9-]*)(?:\.css)?["']?\s*\)?\s*(?=[;\s]|$)/g;
3163
+ for (const m of stripComments(css).matchAll(pattern)) {
3164
+ names.push(m[1]);
3165
+ }
3166
+ return names;
2587
3167
  }
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;
3168
+ var AXIS_CONFIG_FILE_NAME = "axis.config.json";
3169
+ function readBrandConfig(baseDir) {
3170
+ let parsed;
3171
+ try {
3172
+ parsed = JSON.parse(readFileSync3(join2(resolve2(baseDir), AXIS_CONFIG_FILE_NAME), "utf8"));
3173
+ } catch {
3174
+ return null;
3175
+ }
3176
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
3177
+ return parsed;
2596
3178
  }
2597
- var TAILWIND4_COMPONENT_PACKAGES = ["@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2598
- function componentTailwindImportLine(name) {
2599
- return `@import "${name}/tailwind.css";`;
3179
+ function inspectBrandImport(baseDir) {
3180
+ const config = readBrandConfig(baseDir);
3181
+ const brand = config?.brand;
3182
+ if (typeof brand !== "string" || brand.trim() === "") return null;
3183
+ const root = resolve2(baseDir);
3184
+ const location = findGlobalCss(root, config);
3185
+ const base = { brand };
3186
+ if (location.configuredMissing !== void 0) base.configuredMissing = location.configuredMissing;
3187
+ if (!location.path) {
3188
+ return { ...base, state: "css-missing", imports: [] };
3189
+ }
3190
+ const cssFile = relative(root, location.path).split(sep).join("/");
3191
+ const imports = [...new Set(findBrandImports(readFileSync3(location.path, "utf8")))];
3192
+ let state;
3193
+ if (imports.includes(brand)) state = "match";
3194
+ else if (imports.length === 0) state = "import-missing";
3195
+ else state = "other-brand";
3196
+ return { ...base, state, cssFile, imports };
2600
3197
  }
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));
3198
+ function isMissingBuildFile(err) {
3199
+ const code = errorCode(err);
3200
+ return code === "MODULE_NOT_FOUND" || code === "ENOENT";
2605
3201
  }
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));
3202
+ function readBrandIndex(indexPath) {
3203
+ const data = readJsonFile(indexPath);
3204
+ if (!Array.isArray(data.brands)) {
3205
+ throw new Error(`${indexPath}: "brands" \uBC30\uC5F4\uC774 \uC5C6\uC2B5\uB2C8\uB2E4`);
3206
+ }
3207
+ return data.brands;
2609
3208
  }
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]})`
3209
+ function readSummary(baseDir, entry) {
3210
+ const metaPath = projectRequire(baseDir).resolve(tokensSpecifier(entry.meta));
3211
+ const meta = readJsonFile(metaPath);
3212
+ return {
3213
+ name: entry.name,
3214
+ description: typeof meta.description === "string" ? meta.description : "",
3215
+ themes: Array.isArray(meta.themes) ? meta.themes.map(String) : [],
3216
+ a11y: typeof meta.a11y === "object" && meta.a11y !== null ? meta.a11y : {},
3217
+ importPath: brandImportPath(entry)
3218
+ };
3219
+ }
3220
+ function listBrands(baseDir) {
3221
+ const tokens = resolveTokens(baseDir);
3222
+ if (tokens.status !== "ok" || !tokens.indexPath) {
3223
+ return { status: tokens.status, version: tokens.version, brands: [] };
2630
3224
  }
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(", ")}`);
3225
+ const entries = readBrandIndex(tokens.indexPath);
3226
+ let brands;
3227
+ try {
3228
+ brands = entries.map((entry) => readSummary(baseDir, entry));
3229
+ } catch (err) {
3230
+ if (isMissingBuildFile(err)) return { status: "not-built", version: tokens.version, brands: [] };
3231
+ throw err;
2642
3232
  }
2643
- return labels;
3233
+ return { status: "ok", version: tokens.version, brands };
2644
3234
  }
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++;
3235
+ function stripComments(css) {
3236
+ return css.replace(/\/\*[\s\S]*?\*\//g, "");
3237
+ }
3238
+ function parseDeclarations(body) {
3239
+ const out = {};
3240
+ for (const m of body.matchAll(/(--[A-Za-z0-9_-]+)\s*:\s*([^;]+);/g)) {
3241
+ out[m[1]] = m[2].trim();
2676
3242
  }
2677
3243
  return out;
2678
3244
  }
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));
2683
- }
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) {
2686
- 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;
2691
- }
3245
+ function isDarkSelector(selector) {
3246
+ return /\.dark\b/.test(selector) || /\[data-theme=["']?dark["']?\]/.test(selector);
2692
3247
  }
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(" ")}`);
2702
- }
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`);
3248
+ function parseBrandCss(css, name) {
3249
+ const result = { light: {}, dark: {} };
3250
+ let lightFound = false;
3251
+ let darkFound = false;
3252
+ const brandAttr = new RegExp(`\\[data-brand=["']?${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']?\\]`);
3253
+ for (const m of stripComments(css).matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
3254
+ const selectors = m[1].split(",").map((s) => s.trim());
3255
+ if (!selectors.some((s) => brandAttr.test(s))) continue;
3256
+ if (selectors.some(isDarkSelector)) {
3257
+ if (!darkFound) {
3258
+ result.dark = parseDeclarations(m[2]);
3259
+ darkFound = true;
3260
+ }
3261
+ } else if (!lightFound) {
3262
+ result.light = parseDeclarations(m[2]);
3263
+ lightFound = true;
2708
3264
  }
2709
3265
  }
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
- }
3266
+ return result;
3267
+ }
3268
+ function pickKeyTokens(decls) {
3269
+ const out = {};
3270
+ for (const key of KEY_TOKENS) {
3271
+ if (Object.prototype.hasOwnProperty.call(decls, key)) out[key] = decls[key];
2714
3272
  }
2715
- return issues;
3273
+ return out;
3274
+ }
3275
+ var THEME_MARKUP_BRAND_MIN_VERSION = "1.7.0";
3276
+ function providerExample(name) {
3277
+ return [
3278
+ "// 1) \uB8E8\uD2B8 \uB808\uC774\uC544\uC6C3(app/layout.tsx)\uC758 <html> \uC5D0 \uBD99\uC785\uB2C8\uB2E4(\uCCAB \uD654\uBA74\uBD80\uD130 \uC801\uC6A9)",
3279
+ `// ThemeProvider \uB97C \uD568\uAED8 \uC4F0\uBA74 @axis-ds/theme ${THEME_MARKUP_BRAND_MIN_VERSION} \uC774\uC0C1\uC774 \uD544\uC694\uD569\uB2C8\uB2E4(\uADF8 \uC544\uB798\uB294 \uB9C8\uC6B4\uD2B8 \uB54C \uC18D\uC131\uC744 \uC9C0\uC6C0)`,
3280
+ `<html lang="ko" data-brand="${name}">`,
3281
+ "",
3282
+ `// 2) \uC0AC\uC6A9\uC790\uAC00 \uACE0\uB974\uAC8C \uD558\uB824\uBA74 setBrand \uB85C \uCF1C\uACE0 \uB055\uB2C8\uB2E4(\uC800\uC7A5\uB3FC \uC720\uC9C0, setBrand(undefined) \uB85C \uB048 \uC0C1\uD0DC \uC720\uC9C0\uB294 ${THEME_MARKUP_BRAND_MIN_VERSION} \uC774\uC0C1)`,
3283
+ 'import { useTheme } from "@axis-ds/theme";',
3284
+ "const { setBrand } = useTheme();",
3285
+ `setBrand("${name}");`
3286
+ ].join("\n");
2716
3287
  }
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
- };
3288
+ var BRAND_ACTIVATION_FILES = ROOT_LAYOUT_FILES;
3289
+ var BRAND_ACTIVATION_PATTERN = /data-brand|setBrand\s*\(|defaultBrand\s*=/;
3290
+ var PROVIDER_ACTIVATION_PATTERN = /setBrand\s*\(|defaultBrand\s*=/;
3291
+ var THEME_PROVIDER_PATTERN = /<ThemeProvider\b/;
3292
+ function stripJsLikeComments(src) {
3293
+ return src.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:"'`])\/\/[^\n]*/g, "$1");
3294
+ }
3295
+ function findBrandActivation(baseDir) {
3296
+ const root = resolve2(baseDir);
3297
+ const layoutFiles = [];
3298
+ for (const candidate of BRAND_ACTIVATION_FILES) {
3299
+ const file = join2(root, candidate);
3300
+ if (!isFile2(file)) continue;
3301
+ layoutFiles.push(candidate);
3302
+ const text = stripJsLikeComments(readFileSync3(file, "utf8"));
3303
+ const providerTexts = [];
3304
+ for (const m of text.matchAll(/from\s+["']\.\/(providers?)["']/g)) {
3305
+ for (const ext of [".tsx", ".jsx", ".ts", ".js"]) {
3306
+ const rel3 = `${dirname(candidate)}/${m[1]}${ext}`;
3307
+ const p = join2(root, rel3);
3308
+ if (isFile2(p)) providerTexts.push({ rel: rel3, text: stripJsLikeComments(readFileSync3(p, "utf8")) });
2733
3309
  }
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
- };
3310
+ }
3311
+ const all = [text, ...providerTexts.map((x) => x.text)];
3312
+ const markupWithProvider = !all.some((t) => PROVIDER_ACTIVATION_PATTERN.test(t)) && all.some((t) => THEME_PROVIDER_PATTERN.test(t));
3313
+ if (BRAND_ACTIVATION_PATTERN.test(text)) {
3314
+ return markupWithProvider ? { found: candidate, layoutFiles, markupWithProvider } : { found: candidate, layoutFiles };
3315
+ }
3316
+ for (const x of providerTexts) {
3317
+ if (BRAND_ACTIVATION_PATTERN.test(x.text)) {
3318
+ return markupWithProvider ? { found: x.rel, layoutFiles, markupWithProvider } : { found: x.rel, layoutFiles };
2742
3319
  }
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
3320
  }
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
3321
  }
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}` };
3322
+ return { layoutFiles };
3323
+ }
3324
+ function inspectBrandActivation(baseDir) {
3325
+ const config = readBrandConfig(baseDir);
3326
+ const brand = config?.brand;
3327
+ if (typeof brand !== "string" || brand.trim() === "") return null;
3328
+ const location = findBrandActivation(baseDir);
3329
+ if (!location.markupWithProvider) return { brand, ...location };
3330
+ return { brand, ...location, theme: packageVersionGate(baseDir, readProjectPackageJson(baseDir), THEME_PACKAGE, THEME_MARKUP_BRAND_MIN_VERSION) };
3331
+ }
3332
+ var THEME_PACKAGE = "@axis-ds/theme";
3333
+ function readProjectPackageJson(baseDir) {
3334
+ try {
3335
+ const parsed = JSON.parse(readFileSync3(join2(resolve2(baseDir), "package.json"), "utf8"));
3336
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : void 0;
3337
+ } catch {
3338
+ return void 0;
3339
+ }
3340
+ }
3341
+ function getBrand(baseDir, name) {
3342
+ const tokens = resolveTokens(baseDir);
3343
+ if (tokens.status !== "ok" || !tokens.indexPath) {
3344
+ return { status: tokens.status, version: tokens.version, brand: null, available: [] };
3345
+ }
3346
+ const entries = readBrandIndex(tokens.indexPath);
3347
+ const available = entries.map((e) => e.name);
3348
+ const entry = entries.find((e) => e.name === name);
3349
+ if (!entry) {
3350
+ return { status: "ok", version: tokens.version, brand: null, available };
3351
+ }
3352
+ let summary;
3353
+ let parsed;
3354
+ try {
3355
+ summary = readSummary(baseDir, entry);
3356
+ const cssPath = projectRequire(baseDir).resolve(summary.importPath);
3357
+ parsed = parseBrandCss(readFileSync3(cssPath, "utf8"), name);
3358
+ } catch (err) {
3359
+ if (isMissingBuildFile(err)) return { status: "not-built", version: tokens.version, brand: null, available: [] };
3360
+ throw err;
3361
+ }
3362
+ return {
3363
+ status: "ok",
3364
+ version: tokens.version,
3365
+ available,
3366
+ brand: {
3367
+ ...summary,
3368
+ tokens: { light: pickKeyTokens(parsed.light), dark: pickKeyTokens(parsed.dark) },
3369
+ providerExample: providerExample(name),
3370
+ adjustments: { a11y: summary.a11y, reference: BRAND_ADJUSTMENT_REFERENCE }
2765
3371
  }
2766
- return { name: "tailwind.config", severity: "warn", message: "tailwind.config.ts\uC5D0 AXIS \uD1A0\uD070 \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" };
3372
+ };
3373
+ }
3374
+
3375
+ // ../axis-registry-core/src/template/dependencies-core.ts
3376
+ import { readFileSync as readFileSync4 } from "fs";
3377
+ import { join as join3, resolve as resolve3 } from "path";
3378
+ var REQUIRED_DEPENDENCIES = ["tailwindcss"];
3379
+ var RECOMMENDED_DEPENDENCIES = ["clsx", "tailwind-merge"];
3380
+ function declaredAxisPackages(pkg) {
3381
+ const out = [];
3382
+ for (const field of ["dependencies", "devDependencies"]) {
3383
+ const deps = pkg[field];
3384
+ if (!isPlainObject(deps)) continue;
3385
+ for (const name of Object.keys(deps)) if (name.startsWith("@axis-ds/") && !out.includes(name)) out.push(name);
2767
3386
  }
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)` };
3387
+ return out;
3388
+ }
3389
+ function checkDependencies(baseDirInput) {
3390
+ const baseDir = resolve3(baseDirInput);
3391
+ let pkg;
3392
+ try {
3393
+ const parsed = JSON.parse(readFileSync4(join3(baseDir, "package.json"), "utf8"));
3394
+ if (!isPlainObject(parsed)) throw new Error("not object");
3395
+ pkg = parsed;
3396
+ } catch (err) {
3397
+ if (err?.code === "ENOENT") {
3398
+ return { name: "dependencies", severity: "fail", message: "package.json \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" };
2775
3399
  }
3400
+ return { name: "dependencies", severity: "fail", message: "package.json \uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(JSON \uD655\uC778)" };
2776
3401
  }
2777
- return { name: "tailwind.config", severity: "fail", message: "tailwind.config \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" };
3402
+ const missingRequired = REQUIRED_DEPENDENCIES.filter((d) => !hasDependency(pkg, d));
3403
+ if (missingRequired.length > 0) {
3404
+ return { name: "dependencies", severity: "fail", message: `\uD544\uC218 \uC758\uC874\uC131 \uB204\uB77D: ${missingRequired.join(", ")}` };
3405
+ }
3406
+ const missingRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => !hasDependency(pkg, d));
3407
+ const targets = [
3408
+ ...[...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => hasDependency(pkg, d) !== void 0),
3409
+ ...declaredAxisPackages(pkg)
3410
+ ];
3411
+ const pnp = isPnpProject(baseDir);
3412
+ const notInstalled = pnp ? [] : targets.filter((d) => findInstalledPackage(baseDir, d) === void 0);
3413
+ const problems = [];
3414
+ if (missingRecommended.length > 0) problems.push(`\uAD8C\uC7A5 \uC758\uC874\uC131 \uB204\uB77D: ${missingRecommended.join(", ")}`);
3415
+ if (notInstalled.length > 0) {
3416
+ problems.push(`\uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC740 \uC758\uC874\uC131: ${notInstalled.join(", ")} \u2192 ${detectInstallCommand(baseDir)}`);
3417
+ }
3418
+ if (problems.length > 0) return { name: "dependencies", severity: "warn", message: problems.join(" / ") };
3419
+ return {
3420
+ name: "dependencies",
3421
+ severity: "pass",
3422
+ message: pnp ? `\uD544\uC218 \uBC0F \uAD8C\uC7A5 \uC758\uC874\uC131\uC774 \uC120\uC5B8\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (Plug'n'Play \uB77C \uC124\uCE58\uBCF8\uC740 \uD655\uC778\uD558\uC9C0 \uC54A\uC74C, ${targets.length}\uAC1C)` : `\uD544\uC218 \uBC0F \uAD8C\uC7A5 \uC758\uC874\uC131\uC774 \uC120\uC5B8 \xB7 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (${targets.length}\uAC1C \uD655\uC778)`
3423
+ };
2778
3424
  }
2779
3425
 
2780
3426
  // src/template/template-check.ts
@@ -2784,15 +3430,21 @@ var SEVERITY_ICON = {
2784
3430
  fail: chalk7.red("FAIL")
2785
3431
  };
2786
3432
  async function collectCheckResults(dir) {
2787
- const baseDir = path10.resolve(dir || ".");
3433
+ const baseDir = path11.resolve(dir || ".");
2788
3434
  const results = [];
2789
3435
  results.push(await checkFileExists(baseDir, "axis.config.json", "axis.config.json"));
2790
3436
  results.push(checkTailwindSetup(baseDir));
2791
3437
  results.push(checkGlobalsCss(baseDir));
2792
3438
  results.push(await checkUtils(baseDir));
2793
- results.push(await checkDependencies(baseDir));
3439
+ results.push(checkDependencies(baseDir));
2794
3440
  const brand = checkBrand(baseDir);
2795
3441
  if (brand) results.push(brand);
3442
+ const activation = inspectBrandActivation(baseDir);
3443
+ if (activation) results.push(brandActivationResult(activation));
3444
+ const content = checkTailwindContent(baseDir);
3445
+ if (content) results.push(content);
3446
+ const animation = checkAnimationPlugin(baseDir);
3447
+ if (animation) results.push(animation);
2796
3448
  return results;
2797
3449
  }
2798
3450
  async function runCheck(dir) {
@@ -2829,7 +3481,7 @@ async function runCheck(dir) {
2829
3481
  return { pass: passCount, warn: warnCount, fail: failCount };
2830
3482
  }
2831
3483
  async function checkFileExists(baseDir, fileName, label) {
2832
- const exists = await fs10.pathExists(path10.join(baseDir, fileName));
3484
+ const exists = await fs10.pathExists(path11.join(baseDir, fileName));
2833
3485
  return {
2834
3486
  name: label,
2835
3487
  severity: exists ? "pass" : "fail",
@@ -2845,7 +3497,7 @@ async function checkUtils(baseDir) {
2845
3497
  "utils.ts"
2846
3498
  ];
2847
3499
  for (const candidate of candidates) {
2848
- const fullPath = path10.join(baseDir, candidate);
3500
+ const fullPath = path11.join(baseDir, candidate);
2849
3501
  if (await fs10.pathExists(fullPath)) {
2850
3502
  const content = await fs10.readFile(fullPath, "utf-8");
2851
3503
  const hasCn = content.includes("function cn");
@@ -2862,41 +3514,6 @@ async function checkUtils(baseDir) {
2862
3514
  message: "utils.ts\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC2DC \uD544\uC694)"
2863
3515
  };
2864
3516
  }
2865
- async function checkDependencies(baseDir) {
2866
- const pkgPath = path10.join(baseDir, "package.json");
2867
- if (!await fs10.pathExists(pkgPath)) {
2868
- return {
2869
- name: "package.json",
2870
- severity: "fail",
2871
- message: "package.json \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4"
2872
- };
2873
- }
2874
- const pkg = await fs10.readJSON(pkgPath);
2875
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
2876
- const required = ["tailwindcss"];
2877
- const recommended = ["clsx", "tailwind-merge"];
2878
- const missingRequired = required.filter((d) => !allDeps[d]);
2879
- const missingRecommended = recommended.filter((d) => !allDeps[d]);
2880
- if (missingRequired.length > 0) {
2881
- return {
2882
- name: "dependencies",
2883
- severity: "fail",
2884
- message: `\uD544\uC218 \uC758\uC874\uC131 \uB204\uB77D: ${missingRequired.join(", ")}`
2885
- };
2886
- }
2887
- if (missingRecommended.length > 0) {
2888
- return {
2889
- name: "dependencies",
2890
- severity: "warn",
2891
- message: `\uAD8C\uC7A5 \uC758\uC874\uC131 \uB204\uB77D: ${missingRecommended.join(", ")}`
2892
- };
2893
- }
2894
- return {
2895
- name: "dependencies",
2896
- severity: "pass",
2897
- message: "\uD544\uC218 \uBC0F \uAD8C\uC7A5 \uC758\uC874\uC131\uC774 \uBAA8\uB450 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4"
2898
- };
2899
- }
2900
3517
  function checkBrand(baseDir) {
2901
3518
  const check = inspectBrandImport(baseDir);
2902
3519
  return check ? brandCheckResult(check) : null;
@@ -2931,6 +3548,30 @@ function brandCheckResult(check) {
2931
3548
  };
2932
3549
  }
2933
3550
  }
3551
+ function brandActivationResult(check) {
3552
+ if (check.found && check.theme && check.theme.status !== "ok") {
3553
+ const label = versionGateLabel(check.theme);
3554
+ const why = check.theme.status === "below" ? `@axis-ds/theme ${label} \uC758 ThemeProvider \uAC00 \uB9C8\uC6B4\uD2B8 \uB54C \uC9C0\uC6C1\uB2C8\uB2E4` : `@axis-ds/theme \uBC84\uC804\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${label}). ${THEME_MARKUP_BRAND_MIN_VERSION} \uBBF8\uB9CC\uC774\uBA74 ThemeProvider \uAC00 \uB9C8\uC6B4\uD2B8 \uB54C \uC9C0\uC6C1\uB2C8\uB2E4`;
3555
+ return {
3556
+ name: "brand.activation",
3557
+ severity: "warn",
3558
+ message: `${check.found}\uC758 <html data-brand> \uB97C ${why} \u2192 @axis-ds/theme ${THEME_MARKUP_BRAND_MIN_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9AC\uAC70\uB098 setBrand("${check.brand}")`
3559
+ };
3560
+ }
3561
+ if (check.found) {
3562
+ return {
3563
+ name: "brand.activation",
3564
+ severity: "pass",
3565
+ message: `${check.found}\uC5D0 \uBE0C\uB79C\uB4DC\uB97C \uCF1C\uB294 \uCF54\uB4DC(data-brand \xB7 setBrand \uB4F1)\uAC00 \uC788\uC2B5\uB2C8\uB2E4`
3566
+ };
3567
+ }
3568
+ const where = check.layoutFiles.length > 0 ? check.layoutFiles.join(" \xB7 ") : "\uB8E8\uD2B8 \uB808\uC774\uC544\uC6C3(app/layout.tsx \uB4F1)";
3569
+ return {
3570
+ name: "brand.activation",
3571
+ severity: "warn",
3572
+ 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}")`
3573
+ };
3574
+ }
2934
3575
 
2935
3576
  // src/template/index.ts
2936
3577
  function registerTemplateCommand(program2) {
@@ -3040,36 +3681,13 @@ import chalk9 from "chalk";
3040
3681
  import path13 from "path";
3041
3682
 
3042
3683
  // src/theme/theme-add.ts
3043
- import { accessSync, constants, existsSync as existsSync4, readFileSync as readFileSync3, realpathSync, unlinkSync, writeFileSync } from "fs";
3684
+ import { accessSync, constants, existsSync as existsSync4, readFileSync as readFileSync5, realpathSync, unlinkSync, writeFileSync } from "fs";
3044
3685
  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}`;
3070
- var THEME_PACKAGE = "@axis-ds/theme";
3686
+ var TOKENS_DEPENDENCY_RANGE = `^${TOKENS_DEPENDENCY_MIN_VERSION}`;
3687
+ var TAILWIND3_BASE_IMPORTS = [`${TOKENS_PACKAGE}/css`, `${TOKENS_PACKAGE}/css/shadcn`];
3688
+ var THEME_MIN_VERSION = "1.3.0";
3071
3689
  function readText(file) {
3072
- return existsSync4(file) ? readFileSync3(file, "utf8") : null;
3690
+ return existsSync4(file) ? readFileSync5(file, "utf8") : null;
3073
3691
  }
3074
3692
  function eolOf(text) {
3075
3693
  return text.includes("\r\n") ? "\r\n" : "\n";
@@ -3120,6 +3738,31 @@ function insertImportLine(css, line) {
3120
3738
  lines.splice(insertAt, 0, line);
3121
3739
  return bom + lines.join(eol);
3122
3740
  }
3741
+ function insertImportLines(css, lines) {
3742
+ if (lines.length === 0) return css;
3743
+ const body = css.startsWith("\uFEFF") ? css.slice(1) : css;
3744
+ return insertImportLine(css, lines.join(eolOf(body)));
3745
+ }
3746
+ function findLateImports(css) {
3747
+ const body = css.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "));
3748
+ const lines = body.split(/\r?\n/);
3749
+ let rulesStarted = false;
3750
+ const late = [];
3751
+ for (const raw of lines) {
3752
+ const line = raw.trim();
3753
+ if (line === "") continue;
3754
+ if (/^@(charset|import)\b/i.test(line)) {
3755
+ if (rulesStarted && /^@import\b/i.test(line)) late.push(line);
3756
+ continue;
3757
+ }
3758
+ rulesStarted = true;
3759
+ }
3760
+ return late;
3761
+ }
3762
+ function hasCssImport(css, specifier) {
3763
+ const esc = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3764
+ return new RegExp(`@import\\s+(?:url\\(\\s*)?["']${esc}(?:\\.css)?["']`).test(css.replace(/\/\*[\s\S]*?\*\//g, ""));
3765
+ }
3123
3766
  function compareVersions(a, b) {
3124
3767
  const parse = (v) => {
3125
3768
  const m = /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(v.trim());
@@ -3166,7 +3809,7 @@ function planThemeAdd(baseDirInput, brand) {
3166
3809
  let config;
3167
3810
  try {
3168
3811
  const parsed = JSON.parse(configText);
3169
- if (!isPlainObject2(parsed)) throw new Error("\uCD5C\uC0C1\uC704\uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
3812
+ if (!isPlainObject(parsed)) throw new Error("\uCD5C\uC0C1\uC704\uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
3170
3813
  config = parsed;
3171
3814
  } catch (err) {
3172
3815
  plan.errors.push({ kind: "invalid-config", message: err.message });
@@ -3178,7 +3821,7 @@ function planThemeAdd(baseDirInput, brand) {
3178
3821
  }
3179
3822
  plan.tokens = resolveTokens(baseDir);
3180
3823
  if (plan.tokens.status === "ok" && plan.tokens.indexPath) {
3181
- const data = JSON.parse(readFileSync3(plan.tokens.indexPath, "utf8"));
3824
+ const data = JSON.parse(readFileSync5(plan.tokens.indexPath, "utf8"));
3182
3825
  const entries = Array.isArray(data.brands) ? data.brands : [];
3183
3826
  const entry = entries.find((e) => e.name === brand);
3184
3827
  if (!entry) {
@@ -3194,6 +3837,28 @@ function planThemeAdd(baseDirInput, brand) {
3194
3837
  } else {
3195
3838
  plan.unverifiedBrand = true;
3196
3839
  }
3840
+ const pkgFile = path12.join(baseDir, "package.json");
3841
+ const pkgText = readText(pkgFile);
3842
+ let pkg;
3843
+ if (pkgText !== null) {
3844
+ try {
3845
+ const parsed = JSON.parse(pkgText);
3846
+ if (!isPlainObject(parsed)) throw new Error("\uCD5C\uC0C1\uC704\uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
3847
+ pkg = parsed;
3848
+ } catch (err) {
3849
+ plan.errors.push({ kind: "invalid-package-json", message: err.message });
3850
+ plan.writes = [];
3851
+ return plan;
3852
+ }
3853
+ }
3854
+ const tailwind = detectTailwind(baseDir, pkg);
3855
+ const tailwind4 = tailwind.major !== void 0 && tailwind.major >= 4;
3856
+ const requiredBaseImports = tailwind4 ? [
3857
+ TOKENS_TAILWIND4_ENTRY,
3858
+ ...TAILWIND4_COMPONENT_PACKAGES.filter((n) => pkg !== void 0 && hasDependency(pkg, n) !== void 0).map(
3859
+ (n) => `${n}/tailwind.css`
3860
+ )
3861
+ ] : [...TAILWIND3_BASE_IMPORTS];
3197
3862
  const location = findGlobalCss(baseDir, config);
3198
3863
  if (location.path) {
3199
3864
  const where = realPathInside(baseDir, location.path);
@@ -3207,48 +3872,65 @@ function planThemeAdd(baseDirInput, brand) {
3207
3872
  plan.writes = [];
3208
3873
  return plan;
3209
3874
  }
3210
- const before = readFileSync3(location.path, "utf8");
3875
+ const before = readFileSync5(location.path, "utf8");
3211
3876
  const imported = findBrandImports(before);
3212
3877
  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
- });
3878
+ const brandAdded = !imported.includes(brand);
3879
+ const baseImports = requiredBaseImports.filter((spec) => !hasCssImport(before, spec));
3880
+ const lines = [...baseImports.map((spec) => `@import "${spec}";`), ...brandAdded ? [`@import "${plan.importPath}";`] : []];
3881
+ plan.css = {
3882
+ file: location.path,
3883
+ configuredMissing: location.configuredMissing,
3884
+ action: lines.length > 0 ? "add" : "exists",
3885
+ otherBrands,
3886
+ brandAdded,
3887
+ baseImports,
3888
+ requiredBaseImports,
3889
+ lateImports: findLateImports(before),
3890
+ tailwind4
3891
+ };
3892
+ if (lines.length > 0) {
3893
+ plan.writes.push({ file: location.path, before, after: insertImportLines(before, lines) });
3222
3894
  }
3223
3895
  } else {
3224
- plan.css = { configuredMissing: location.configuredMissing, action: "manual", otherBrands: [] };
3896
+ plan.css = {
3897
+ configuredMissing: location.configuredMissing,
3898
+ action: "manual",
3899
+ otherBrands: [],
3900
+ brandAdded: true,
3901
+ baseImports: requiredBaseImports,
3902
+ requiredBaseImports,
3903
+ lateImports: [],
3904
+ tailwind4
3905
+ };
3225
3906
  }
3226
- const pkgFile = path12.join(baseDir, "package.json");
3227
- const pkgText = readText(pkgFile);
3907
+ if (!tailwind4) {
3908
+ const configFile2 = TAILWIND_CONFIG_FILES.find((f) => existsSync4(path12.join(baseDir, f)));
3909
+ const content = inspectTailwindContent(baseDir);
3910
+ plan.tailwindConfig = {
3911
+ configFile: configFile2,
3912
+ presetMissing: configFile2 === void 0 || !hasTokensPresetReference(readFileSync5(path12.join(baseDir, configFile2), "utf8")),
3913
+ contentMissing: content?.targets.filter((t) => !t.covered) ?? [],
3914
+ contentUnknown: content !== null && (content.unreadable || content.dynamic)
3915
+ };
3916
+ }
3917
+ plan.activation = findBrandActivation(baseDir);
3228
3918
  const installed = plan.tokens.version;
3229
3919
  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 };
3920
+ const tokensAddCommand = addCommandFor(plan.installCommand, { [TOKENS_PACKAGE]: TOKENS_DEPENDENCY_RANGE });
3921
+ const themeAddCommand = addCommandFor(plan.installCommand, { [THEME_PACKAGE]: `^${THEME_MIN_VERSION}` });
3922
+ if (pkgText === null || pkg === void 0) {
3923
+ plan.dependency = { action: "no-package-json", upgrade, themeMissing: false, tokensAddCommand, themeAddCommand };
3232
3924
  } 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
- const themeMissing = hasDependency2(pkg, THEME_PACKAGE) === void 0;
3244
- const declared = hasDependency2(pkg, TOKENS_PACKAGE);
3925
+ const themeMissing = hasDependency(pkg, THEME_PACKAGE) === void 0;
3926
+ const declared = hasDependency(pkg, TOKENS_PACKAGE);
3245
3927
  if (declared !== void 0) {
3246
- plan.dependency = { action: "present", declaredRange: declared, upgrade, themeMissing };
3928
+ plan.dependency = { action: "present", declaredRange: declared, upgrade, themeMissing, tokensAddCommand, themeAddCommand };
3247
3929
  } else {
3248
- const deps = isPlainObject2(pkg.dependencies) ? { ...pkg.dependencies } : {};
3930
+ const deps = isPlainObject(pkg.dependencies) ? { ...pkg.dependencies } : {};
3249
3931
  deps[TOKENS_PACKAGE] = TOKENS_DEPENDENCY_RANGE;
3250
3932
  const next = { ...pkg, dependencies: deps };
3251
- plan.dependency = { action: "add", upgrade, themeMissing };
3933
+ plan.dependency = { action: "add", upgrade, themeMissing, tokensAddCommand, themeAddCommand };
3252
3934
  plan.writes.push({ file: pkgFile, before: pkgText, after: formatJsonLike(pkgText, next) });
3253
3935
  }
3254
3936
  }
@@ -3330,33 +4012,55 @@ function applyThemeAdd(plan) {
3330
4012
  }
3331
4013
 
3332
4014
  // src/theme/index.ts
3333
- var TOKENS_INSTALL_COMMAND = `pnpm add ${TOKENS_PACKAGE}@^${MIN_BRAND_TOKENS_VERSION}`;
3334
- function tokensStatusGuide(status, version) {
4015
+ function importOffset(css, spec) {
4016
+ const escaped = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4017
+ const m = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${escaped}["']`).exec(css);
4018
+ return m ? m.index : -1;
4019
+ }
4020
+ function baseImportLines(after, spec, brandImportPath2, file) {
4021
+ if (spec.endsWith("/tailwind.css")) {
4022
+ return [` ${chalk9.green("\u2713")} ${file}: @import "${spec}"; \uCD94\uAC00(\uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCE94 \uACBD\uB85C, \uC704\uCE58 \uBB34\uAD00)`];
4023
+ }
4024
+ const at = importOffset(after, spec);
4025
+ const brandAt = importOffset(after, brandImportPath2);
4026
+ if (brandAt === -1 || at !== -1 && at < brandAt) {
4027
+ return [` ${chalk9.green("\u2713")} ${file}: @import "${spec}"; \uCD94\uAC00(\uAE30\uBCF8 \uD1A0\uD070 CSS, \uBE0C\uB79C\uB4DC \uC55E)`];
4028
+ }
4029
+ return [
4030
+ ` ${chalk9.green("\u2713")} ${file}: @import "${spec}"; \uCD94\uAC00(\uAE30\uBCF8 \uD1A0\uD070 CSS)`,
4031
+ chalk9.yellow(` ! \uC774 \uC904\uC774 \uBE0C\uB79C\uB4DC import \uB4A4\uC5D0 \uB4E4\uC5B4\uAC14\uC2B5\uB2C8\uB2E4. \uBE0C\uB79C\uB4DC \uAC12\uC774 \uAE30\uBCF8 \uD1A0\uD070\uC5D0 \uB36E\uC774\uBBC0\uB85C @import "${brandImportPath2}"; \uC55E\uC73C\uB85C \uC62E\uAE30\uC138\uC694`)
4032
+ ];
4033
+ }
4034
+ function tokensInstallCommand(baseDir) {
4035
+ return addCommandFor(detectInstallCommand(baseDir), { [TOKENS_PACKAGE]: TOKENS_DEPENDENCY_RANGE });
4036
+ }
4037
+ var TOKENS_INSTALL_COMMAND = addCommandFor("pnpm install", { [TOKENS_PACKAGE]: TOKENS_DEPENDENCY_RANGE });
4038
+ function tokensStatusGuide(status, version, installCommand = TOKENS_INSTALL_COMMAND) {
3335
4039
  switch (status) {
3336
4040
  case "not-installed":
3337
4041
  return [
3338
4042
  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)}`
4043
+ ` \uC124\uCE58: ${chalk9.cyan(installCommand)}`
3340
4044
  ];
3341
4045
  case "unsupported":
3342
4046
  return [
3343
4047
  chalk9.yellow(
3344
4048
  `${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
4049
  ),
3346
- ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(TOKENS_INSTALL_COMMAND)}`
4050
+ ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(installCommand)}`
3347
4051
  ];
3348
4052
  case "not-built":
3349
4053
  return [
3350
4054
  chalk9.yellow(
3351
4055
  `${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
4056
  ),
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)}`
4057
+ ` \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uBA74 ${chalk9.cyan(`pnpm --filter ${TOKENS_PACKAGE} build`)}, \uC544\uB2C8\uBA74 \uC7AC\uC124\uCE58: ${chalk9.cyan(installCommand)}`
3354
4058
  ];
3355
4059
  }
3356
4060
  }
3357
- function renderThemeList(result) {
4061
+ function renderThemeList(result, installCommand) {
3358
4062
  if (result.status !== "ok") {
3359
- return { lines: ["", ...tokensStatusGuide(result.status, result.version), ""], exitCode: 1 };
4063
+ return { lines: ["", ...tokensStatusGuide(result.status, result.version, installCommand), ""], exitCode: 1 };
3360
4064
  }
3361
4065
  if (result.brands.length === 0) {
3362
4066
  return {
@@ -3401,9 +4105,9 @@ function tokenTable(brand) {
3401
4105
  }
3402
4106
  return lines;
3403
4107
  }
3404
- function renderThemeInfo(result, name) {
4108
+ function renderThemeInfo(result, name, installCommand) {
3405
4109
  if (result.status !== "ok") {
3406
- return { lines: ["", ...tokensStatusGuide(result.status, result.version), ""], exitCode: 1 };
4110
+ return { lines: ["", ...tokensStatusGuide(result.status, result.version, installCommand), ""], exitCode: 1 };
3407
4111
  }
3408
4112
  const brand = result.brand;
3409
4113
  if (!brand) {
@@ -3427,7 +4131,7 @@ function renderThemeInfo(result, name) {
3427
4131
  chalk9.bold("Import (globals.css, @charset \xB7 \uB2E4\uB978 @import \uB2E4\uC74C)"),
3428
4132
  ` @import "${brand.importPath}";`,
3429
4133
  "",
3430
- chalk9.bold("Provider (@axis-ds/theme 1.3.0 \uC774\uC0C1)"),
4134
+ chalk9.bold("\uBE0C\uB79C\uB4DC \uCF1C\uAE30 (ThemeProvider \uB294 @axis-ds/theme 1.3.0 \uC774\uC0C1)"),
3431
4135
  ...brand.providerExample.split("\n").map((l) => l ? ` ${l}` : ""),
3432
4136
  chalk9.gray(
3433
4137
  ` 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 +4216,32 @@ function renderThemeAdd(plan, written) {
3512
4216
  )
3513
4217
  );
3514
4218
  }
4219
+ const brandLine = `@import "${plan.importPath}";`;
3515
4220
  if (css?.action === "add" && css.file) {
3516
- lines.push(` ${chalk9.green("\u2713")} ${rel(plan, css.file)}: @import "${plan.importPath}"; \uCD94\uAC00`);
4221
+ const after = plan.writes.find((w) => w.file === css.file)?.after ?? "";
4222
+ for (const spec of css.baseImports) {
4223
+ lines.push(...baseImportLines(after, spec, plan.importPath, rel(plan, css.file)));
4224
+ }
4225
+ if (css.brandAdded) lines.push(` ${chalk9.green("\u2713")} ${rel(plan, css.file)}: ${brandLine} \uCD94\uAC00`);
4226
+ else lines.push(` ${chalk9.gray("-")} ${rel(plan, css.file)}: \uBE0C\uB79C\uB4DC import \uAC00 \uC774\uBBF8 \uC788\uC5B4 \uAC74\uB108\uB700`);
3517
4227
  } else if (css?.action === "exists" && css.file) {
3518
4228
  lines.push(` ${chalk9.gray("-")} ${rel(plan, css.file)}: import \uAC00 \uC774\uBBF8 \uC788\uC5B4 \uAC74\uB108\uB700`);
3519
4229
  } else {
3520
4230
  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}";`
4231
+ chalk9.yellow(
4232
+ " ! 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."
4233
+ ),
4234
+ ` \uC804\uC5ED CSS \uB9E8 \uC55E(@charset \xB7 \uB2E4\uB978 @import \uB2E4\uC74C)\uC5D0 \uC774 \uC21C\uC11C\uB85C \uC9C1\uC811 \uB123\uC73C\uC138\uC694: ${[
4235
+ ...(css?.requiredBaseImports ?? []).map((spec) => `@import "${spec}";`),
4236
+ brandLine
4237
+ ].join(" ")}`
4238
+ );
4239
+ }
4240
+ if (css && css.lateImports.length > 0) {
4241
+ lines.push(
4242
+ chalk9.yellow(
4243
+ ` ! \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(" ")}`
4244
+ )
3523
4245
  );
3524
4246
  }
3525
4247
  if (css && css.otherBrands.length > 0) {
@@ -3538,16 +4260,14 @@ function renderThemeAdd(plan, written) {
3538
4260
  } else if (dep?.action === "present") {
3539
4261
  lines.push(` ${chalk9.gray("-")} package.json: ${TOKENS_PACKAGE} ${dep.declaredRange} \uAC00 \uC774\uBBF8 \uC788\uC5B4 \uBC94\uC704\uB97C \uBC14\uAFB8\uC9C0 \uC54A\uC74C`);
3540
4262
  } 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
- );
4263
+ 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
4264
  }
3545
4265
  if (dep?.upgrade) {
3546
4266
  lines.push(
3547
4267
  chalk9.yellow(
3548
4268
  ` ! \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
4269
  ),
3550
- ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(TOKENS_INSTALL_COMMAND)}`
4270
+ ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(dep.tokensAddCommand)}`
3551
4271
  );
3552
4272
  }
3553
4273
  if (plan.configChanged) {
@@ -3555,16 +4275,44 @@ function renderThemeAdd(plan, written) {
3555
4275
  } else {
3556
4276
  lines.push(` ${chalk9.gray("-")} axis.config.json: brand \uAC00 \uC774\uBBF8 "${plan.brand}"`);
3557
4277
  }
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
- );
4278
+ const tw = plan.tailwindConfig;
4279
+ if (tw && (tw.presetMissing || tw.contentMissing.length > 0)) {
4280
+ lines.push("", chalk9.bold(`Tailwind \uC124\uC815 (${tw.configFile ?? "tailwind.config"}, \uD30C\uC77C\uC740 \uACE0\uCE58\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4)`));
4281
+ if (tw.presetMissing) {
4282
+ lines.push(
4283
+ // WI-0072: CommonJS 는 모든 tokens 버전에서 동작하는 `.default` 형태로 안내합니다(1.6.2 이하의 bare require 는 preset 이 빠짐)
4284
+ chalk9.yellow(` ! AXIS preset \uC774 \uC5C6\uC2B5\uB2C8\uB2E4. presets \uC5D0 \uB123\uC73C\uC138\uC694: presets: [${TOKENS_TAILWIND3_CJS_REQUIRE}]`) + chalk9.gray(` (ESM \xB7 TypeScript \uC774\uBA74 import axisPreset from "${TOKENS_TAILWIND3_PRESET}" \uB4A4 presets: [axisPreset])`)
4285
+ );
4286
+ }
4287
+ for (const t of tw.contentMissing) {
4288
+ lines.push(
4289
+ chalk9.yellow(
4290
+ ` ! 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}"`
4291
+ )
4292
+ );
4293
+ }
4294
+ }
4295
+ const act = plan.activation;
4296
+ lines.push("", chalk9.bold(`\uBE0C\uB79C\uB4DC \uCF1C\uAE30 (ThemeProvider \uB294 ${THEME_PACKAGE} ${THEME_MIN_VERSION} \uC774\uC0C1)`));
4297
+ if (act?.found) {
4298
+ 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.`);
4299
+ if (act.markupWithProvider) {
4300
+ lines.push(
4301
+ chalk9.yellow(
4302
+ ` ! <html data-brand> \uC640 ThemeProvider \uB97C \uD568\uAED8 \uC4F0\uBA74 ${THEME_PACKAGE} ${THEME_MARKUP_BRAND_MIN_VERSION} \uC774\uC0C1\uC774\uC5B4\uC57C \uB9C8\uC6B4\uD2B8 \uB54C \uC9C0\uC6CC\uC9C0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uBCF8 \uD655\uC778: axis check`
4303
+ )
4304
+ );
4305
+ }
4306
+ } else {
4307
+ lines.push(
4308
+ ...providerExample(plan.brand).split("\n").map((l) => l ? ` ${l}` : ""),
4309
+ chalk9.yellow(
4310
+ ` ! ${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.`
4311
+ )
4312
+ );
4313
+ }
3566
4314
  if (dep?.themeMissing) {
3567
- lines.push(` ${THEME_PACKAGE} \uC124\uCE58: ${chalk9.cyan(`pnpm add ${THEME_PACKAGE}@^1.3.0`)}`);
4315
+ lines.push(` ThemeProvider \uB97C \uC4F0\uBA74 ${THEME_PACKAGE} \uC124\uCE58: ${chalk9.cyan(dep.themeAddCommand)}`);
3568
4316
  }
3569
4317
  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
4318
  return { lines, exitCode: 0 };
@@ -3598,11 +4346,11 @@ function registerThemeCommand(program2) {
3598
4346
  const themeCmd = program2.command("theme").description("\uBE0C\uB79C\uB4DC \uD14C\uB9C8 (@axis-ds/tokens \uBE0C\uB79C\uB4DC)");
3599
4347
  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
4348
  const baseDir = path13.resolve(options.dir || ".");
3601
- print(renderThemeList(listBrands(baseDir)));
4349
+ print(renderThemeList(listBrands(baseDir), tokensInstallCommand(baseDir)));
3602
4350
  });
3603
4351
  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
4352
  const baseDir = path13.resolve(options.dir || ".");
3605
- print(renderThemeInfo(getBrand(baseDir, name), name));
4353
+ print(renderThemeInfo(getBrand(baseDir, name), name, tokensInstallCommand(baseDir)));
3606
4354
  });
3607
4355
  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
4356
  const baseDir = path13.resolve(options.dir || ".");
@@ -3925,13 +4673,13 @@ async function fetchRegistryJson(url, options = {}) {
3925
4673
  }
3926
4674
 
3927
4675
  // 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";
4676
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync6, lstatSync as lstatSync3, readFileSync as readFileSync6, realpathSync as realpathSync3, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
3929
4677
  import path15 from "path";
3930
4678
  import chalk10 from "chalk";
3931
4679
  import fs12 from "fs-extra";
3932
4680
 
3933
4681
  // ../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";
4682
+ import { constants as fsConstants, existsSync as existsSync5, lstatSync as lstatSync2, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
3935
4683
  import path14 from "path";
3936
4684
  import fs11 from "fs-extra";
3937
4685
  var NEW_FILE_FLAGS = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | (fsConstants.O_NOFOLLOW ?? 0);
@@ -3969,7 +4717,7 @@ function escapeReason(root, target, options = {}) {
3969
4717
  }
3970
4718
  let st = null;
3971
4719
  try {
3972
- st = lstatSync(target);
4720
+ st = lstatSync2(target);
3973
4721
  } catch {
3974
4722
  st = null;
3975
4723
  }
@@ -3987,7 +4735,7 @@ function escapeReason(root, target, options = {}) {
3987
4735
  }
3988
4736
  let followed;
3989
4737
  try {
3990
- followed = statSync2(realTarget);
4738
+ followed = statSync3(realTarget);
3991
4739
  } catch {
3992
4740
  return `\uAE30\uC874 \uD30C\uC77C \uC815\uBCF4\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${target})`;
3993
4741
  }
@@ -4003,7 +4751,7 @@ function escapeReason(root, target, options = {}) {
4003
4751
  }
4004
4752
  function isSymlink(p) {
4005
4753
  try {
4006
- return lstatSync(p).isSymbolicLink();
4754
+ return lstatSync2(p).isSymbolicLink();
4007
4755
  } catch {
4008
4756
  return false;
4009
4757
  }
@@ -4019,7 +4767,7 @@ function outsideLinkTarget(root, target) {
4019
4767
  }
4020
4768
  function hardLinkCount(target) {
4021
4769
  try {
4022
- const st = lstatSync(target);
4770
+ const st = lstatSync2(target);
4023
4771
  return st.isFile() && st.nlink >= 2 ? st.nlink : null;
4024
4772
  } catch {
4025
4773
  return null;
@@ -4150,11 +4898,45 @@ export function cn(...inputs: ClassValue[]) {
4150
4898
  return twMerge(clsx(inputs));
4151
4899
  }
4152
4900
  `;
4153
- var INIT_DEFAULTS = {
4154
- componentsDir: "./src/components/ui",
4155
- tailwindConfig: "./tailwind.config.ts",
4156
- globalCss: "./src/app/globals.css"
4157
- };
4901
+ async function resolveInitAnswers(baseDirInput, tailwind4) {
4902
+ const baseDir = path15.resolve(baseDirInput);
4903
+ const tw4 = tailwind4 ?? isTailwind4(planUtilsDependencies(baseDir).tailwind);
4904
+ const current = await existingConfig(baseDir);
4905
+ const found = detectProjectPaths(baseDir);
4906
+ const notes = [];
4907
+ const currentComponents = nonEmptyString(current?.componentsDir);
4908
+ const componentsDir = currentComponents ?? found.componentsDir;
4909
+ const pickFile = (key, value, source, label) => {
4910
+ const existing = nonEmptyString(current?.[key]);
4911
+ if (existing !== void 0 && projectFileExists(baseDir, existing)) return existing;
4912
+ if (existing !== void 0) {
4913
+ if (source === "found") {
4914
+ notes.push(`! \uAE30\uC874 ${key}(${existing})\uAC00 \uC5C6\uC5B4 \uCC3E\uC740 \uD30C\uC77C ${value} \uB85C \uBC14\uAFC9\uB2C8\uB2E4.`);
4915
+ return value;
4916
+ }
4917
+ 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.`);
4918
+ return existing;
4919
+ } else if (source === "default") {
4920
+ 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.`);
4921
+ }
4922
+ return value;
4923
+ };
4924
+ const globalCss = pickFile("globalCss", found.globalCss, found.globalCssSource, "\uC804\uC5ED CSS");
4925
+ const answers = { componentsDir, globalCss };
4926
+ if (!tw4) {
4927
+ answers.tailwindConfig = pickFile("tailwindConfig", found.tailwindConfig, found.tailwindConfigSource, "Tailwind \uC124\uC815");
4928
+ }
4929
+ return { answers, notes };
4930
+ }
4931
+ function animationGuideLine(pkg, tailwind4, installCommand) {
4932
+ const name = tailwind4 ? ANIMATION_PLUGIN_TAILWIND4 : ANIMATION_PLUGIN_TAILWIND3;
4933
+ if (pkg !== void 0 && (hasDependency(pkg, ANIMATION_PLUGIN_TAILWIND3) !== void 0 || hasDependency(pkg, ANIMATION_PLUGIN_TAILWIND4) !== void 0)) {
4934
+ return void 0;
4935
+ }
4936
+ const add = `${addCommandFor(installCommand, {}).trimEnd()} ${name}`;
4937
+ 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`;
4938
+ 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}.`;
4939
+ }
4158
4940
  var INIT_NEXT_STEP_COMMAND = "npx @axis-ds/cli add button";
4159
4941
  function isTailwind4(tailwind) {
4160
4942
  return tailwind.major !== void 0 && tailwind.major >= 4;
@@ -4163,15 +4945,15 @@ var TAILWIND4_TOKENS_VERSION = TAILWIND4_MIN_AXIS_VERSION;
4163
4945
  var TAILWIND4_CONFLICT_LINE = " ! create-next-app \uAE30\uBCF8 globals.css \uC758 :root \xB7 @theme inline \xB7 @media (prefers-color-scheme: dark) \xB7 body \uBE14\uB85D\uC740 AXIS \uC640 \uAC19\uC740 \uC774\uB984(--background \xB7 --foreground \xB7 --color-background \xB7 --font-sans \xB7 --font-mono)\uC744 \uB2E4\uB978 \uAC12\uC73C\uB85C \uC815\uC758\uD569\uB2C8\uB2E4. \uC774 \uBE14\uB85D\uB4E4\uC744 \uC9C0\uC6B0\uC138\uC694.";
4164
4946
  function readPackageJsonSafe(baseDir) {
4165
4947
  try {
4166
- const text = readFileSync4(path15.join(baseDir, "package.json"), "utf8");
4948
+ const text = readFileSync6(path15.join(baseDir, "package.json"), "utf8");
4167
4949
  const parsed = JSON.parse(text.startsWith(BOM) ? text.slice(1) : text);
4168
- return isPlainObject2(parsed) ? parsed : void 0;
4950
+ return isPlainObject(parsed) ? parsed : void 0;
4169
4951
  } catch {
4170
4952
  return void 0;
4171
4953
  }
4172
4954
  }
4173
4955
  function tailwind4GuideLines(pkg, installCommand, globalCss, baseDir) {
4174
- const used = TAILWIND4_COMPONENT_PACKAGES.filter((name) => pkg !== void 0 && hasDependency2(pkg, name) !== void 0);
4956
+ const used = TAILWIND4_COMPONENT_PACKAGES.filter((name) => pkg !== void 0 && hasDependency(pkg, name) !== void 0);
4175
4957
  const lines = [
4176
4958
  `Tailwind 4: \uC804\uC5ED CSS(${globalCss}) \uB9E8 \uC704\uC5D0 \uB2E4\uC74C \uC904\uC744 \uB123\uC73C\uC138\uC694(\uD30C\uC77C\uC740 \uACE0\uCE58\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4).`,
4177
4959
  ' @import "tailwindcss";',
@@ -4184,7 +4966,7 @@ function tailwind4GuideLines(pkg, installCommand, globalCss, baseDir) {
4184
4966
  );
4185
4967
  }
4186
4968
  lines.push(TAILWIND4_CONFLICT_LINE);
4187
- if (pkg === void 0 || hasDependency2(pkg, TOKENS_PACKAGE) === void 0) {
4969
+ if (pkg === void 0 || hasDependency(pkg, TOKENS_PACKAGE) === void 0) {
4188
4970
  const add = addCommandFor(installCommand, { [TOKENS_PACKAGE]: `^${TAILWIND4_TOKENS_VERSION}` });
4189
4971
  lines.push(` ${TOKENS_PACKAGE} \uC124\uCE58: ${add}`);
4190
4972
  }
@@ -4221,12 +5003,6 @@ function tailwind4GuideLines(pkg, installCommand, globalCss, baseDir) {
4221
5003
  return lines;
4222
5004
  }
4223
5005
  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
5006
  function planUtilsDependencies(baseDirInput) {
4231
5007
  const baseDir = path15.resolve(baseDirInput);
4232
5008
  const installCommand = detectInstallCommand(baseDir);
@@ -4253,7 +5029,7 @@ function planUtilsDependencies(baseDirInput) {
4253
5029
  if (!where.inside) return unwritable(`\uC2E4\uC81C \uC704\uCE58(${where.realPath})\uAC00 \uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD130\uB9AC \uBC16\uC785\uB2C8\uB2E4`);
4254
5030
  let text;
4255
5031
  try {
4256
- text = readFileSync4(pkgFile, "utf8");
5032
+ text = readFileSync6(pkgFile, "utf8");
4257
5033
  } catch (err) {
4258
5034
  return unwritable(err.message);
4259
5035
  }
@@ -4262,7 +5038,7 @@ function planUtilsDependencies(baseDirInput) {
4262
5038
  let pkg;
4263
5039
  try {
4264
5040
  const parsed = JSON.parse(body);
4265
- if (!isPlainObject2(parsed)) throw new Error("\uCD5C\uC0C1\uC704\uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
5041
+ if (!isPlainObject(parsed)) throw new Error("\uCD5C\uC0C1\uC704\uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
4266
5042
  pkg = parsed;
4267
5043
  } catch (err) {
4268
5044
  return unwritable(`JSON \uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${err.message}`);
@@ -4272,14 +5048,14 @@ function planUtilsDependencies(baseDirInput) {
4272
5048
  plan.addCommand = addCommandFor(installCommand, plan.ranges);
4273
5049
  const missing = {};
4274
5050
  for (const [name, range] of Object.entries(plan.ranges)) {
4275
- const declared = hasDependency2(pkg, name);
5051
+ const declared = hasDependency(pkg, name);
4276
5052
  if (declared !== void 0) plan.present.push({ name, range: declared });
4277
5053
  else missing[name] = range;
4278
5054
  }
4279
5055
  plan.added = Object.keys(missing);
4280
5056
  plan.addCommand = addCommandFor(installCommand, plan.added.length > 0 ? missing : plan.ranges);
4281
5057
  if (plan.added.length === 0) return { ...plan, action: "present" };
4282
- if (pkg.dependencies !== void 0 && !isPlainObject2(pkg.dependencies)) {
5058
+ if (pkg.dependencies !== void 0 && !isPlainObject(pkg.dependencies)) {
4283
5059
  return unwritable("dependencies \uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
4284
5060
  }
4285
5061
  try {
@@ -4296,7 +5072,7 @@ function applyUtilsDependencies(baseDirInput, plan, writeFile = defaultWriteFile
4296
5072
  const w = plan.write;
4297
5073
  if (plan.action !== "add" || !w) return false;
4298
5074
  const baseDir = path15.resolve(baseDirInput);
4299
- const current = existsSync6(w.file) ? readFileSync4(w.file, "utf8") : null;
5075
+ const current = existsSync6(w.file) ? readFileSync6(w.file, "utf8") : null;
4300
5076
  if (current !== w.before) {
4301
5077
  throw new Error(`${w.file} \uC774 \uACC4\uD68D \uB4A4\uC5D0 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.`);
4302
5078
  }
@@ -4312,7 +5088,7 @@ function applyUtilsDependencies(baseDirInput, plan, writeFile = defaultWriteFile
4312
5088
  let restored;
4313
5089
  try {
4314
5090
  writeFile(w.file, w.before);
4315
- if (readFileSync4(w.file, "utf8") !== w.before) throw new Error("\uB418\uB3CC\uB9B0 \uB0B4\uC6A9\uC774 \uC6D0\uB798 \uB0B4\uC6A9\uACFC \uB2E4\uB985\uB2C8\uB2E4");
5091
+ if (readFileSync6(w.file, "utf8") !== w.before) throw new Error("\uB418\uB3CC\uB9B0 \uB0B4\uC6A9\uC774 \uC6D0\uB798 \uB0B4\uC6A9\uACFC \uB2E4\uB985\uB2C8\uB2E4");
4316
5092
  restored = "\uC6D0\uB798 \uB0B4\uC6A9\uC73C\uB85C \uB418\uB3CC\uB838\uC2B5\uB2C8\uB2E4";
4317
5093
  } catch (restoreErr) {
4318
5094
  restored = `\uC6D0\uB798 \uB0B4\uC6A9\uC73C\uB85C \uB418\uB3CC\uB9AC\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${restoreErr.message}). \uD30C\uC77C\uC774 \uC798\uB838\uAC70\uB098 \uBE44\uC5C8\uC744 \uC218 \uC788\uC73C\uB2C8 \uBC84\uC804 \uAD00\uB9AC \uB3C4\uAD6C \uB4F1\uC73C\uB85C \uBCF5\uAD6C\uD558\uC138\uC694`;
@@ -4349,11 +5125,36 @@ function reportDependencies(plan, log) {
4349
5125
  }
4350
5126
  async function existingConfig(baseDir) {
4351
5127
  try {
4352
- return await readAxisConfig(baseDir);
5128
+ const parsed = await readAxisConfig(baseDir);
5129
+ return isPlainObject(parsed) ? parsed : null;
4353
5130
  } catch {
4354
5131
  return null;
4355
5132
  }
4356
5133
  }
5134
+ var INIT_MANAGED_KEYS = ["$schema", "componentsDir", "tailwindConfig", "globalCss", "registry"];
5135
+ function mergeInitConfig(current, next) {
5136
+ const removed = [];
5137
+ const entries = [];
5138
+ for (const [k, v] of Object.entries(current)) {
5139
+ if (k in next) entries.push([k, next[k]]);
5140
+ else if (INIT_MANAGED_KEYS.includes(k)) removed.push(k);
5141
+ else entries.push([k, v]);
5142
+ }
5143
+ const nextKeys = Object.keys(next);
5144
+ nextKeys.forEach((k, i) => {
5145
+ if (k in current) return;
5146
+ let at = 0;
5147
+ for (let j = i - 1; j >= 0; j--) {
5148
+ const idx = entries.findIndex(([key]) => key === nextKeys[j]);
5149
+ if (idx !== -1) {
5150
+ at = idx + 1;
5151
+ break;
5152
+ }
5153
+ }
5154
+ entries.splice(at, 0, [k, next[k]]);
5155
+ });
5156
+ return { merged: Object.fromEntries(entries), removed };
5157
+ }
4357
5158
  function configStateNote(state) {
4358
5159
  if (state === "created") return "axis.config.json \uC740 \uC774\uBBF8 \uB9CC\uB4E4\uC5C8\uC2B5\uB2C8\uB2E4.";
4359
5160
  if (state === "updated") return "axis.config.json \uC740 \uC774\uBBF8 \uAC31\uC2E0\uD588\uC2B5\uB2C8\uB2E4.";
@@ -4370,7 +5171,7 @@ function planUtilsWrite(baseDirInput, utilsPath) {
4370
5171
  const shown = path15.relative(baseDir, utilsPath).split(path15.sep).join("/") || utilsPath;
4371
5172
  let st;
4372
5173
  try {
4373
- st = lstatSync2(utilsPath);
5174
+ st = lstatSync3(utilsPath);
4374
5175
  } catch (e) {
4375
5176
  if (e.code === "ENOENT") return { action: "create" };
4376
5177
  return { action: "reject", reason: `${shown} \uC758 \uC815\uBCF4\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${errCode(e)})` };
@@ -4390,7 +5191,7 @@ function planUtilsWrite(baseDirInput, utilsPath) {
4390
5191
  }
4391
5192
  let target;
4392
5193
  try {
4393
- target = statSync3(real);
5194
+ target = statSync4(real);
4394
5195
  } catch (e) {
4395
5196
  return { action: "reject", reason: `${shown} \uB9C1\uD06C \uB300\uC0C1(${real})\uC758 \uC815\uBCF4\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${errCode(e)})` };
4396
5197
  }
@@ -4398,7 +5199,7 @@ function planUtilsWrite(baseDirInput, utilsPath) {
4398
5199
  if (!target.isFile()) return { action: "reject", reason: `${shown} \uB9C1\uD06C \uB300\uC0C1(${real})\uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4` };
4399
5200
  let content2;
4400
5201
  try {
4401
- content2 = readFileSync4(utilsPath, "utf8");
5202
+ content2 = readFileSync6(utilsPath, "utf8");
4402
5203
  } catch (e) {
4403
5204
  return { action: "reject", reason: `${shown} \uB9C1\uD06C \uB300\uC0C1(${real})\uC744 \uC77D\uC9C0 \uBABB\uD574 \uBE44\uAD50\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${errCode(e)})` };
4404
5205
  }
@@ -4413,7 +5214,7 @@ function planUtilsWrite(baseDirInput, utilsPath) {
4413
5214
  if (!st.isFile()) return { action: "reject", reason: `${shown} \uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2C8\uB77C \uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4` };
4414
5215
  let content;
4415
5216
  try {
4416
- content = readFileSync4(utilsPath, "utf8");
5217
+ content = readFileSync6(utilsPath, "utf8");
4417
5218
  } catch (e) {
4418
5219
  return { action: "reject", reason: `\uAE30\uC874 ${shown} \uC744 \uC77D\uC9C0 \uBABB\uD574 \uBE44\uAD50\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${errCode(e)})` };
4419
5220
  }
@@ -4446,8 +5247,6 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4446
5247
  registry
4447
5248
  );
4448
5249
  const configPath = path15.join(baseDir, "axis.config.json");
4449
- const nextText = `${JSON.stringify(axisConfig, null, 2)}
4450
- `;
4451
5250
  const rejects = [];
4452
5251
  let previousText = null;
4453
5252
  try {
@@ -4457,9 +5256,18 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4457
5256
  rejects.push(`\uAE30\uC874 axis.config.json \uC744 \uC77D\uC9C0 \uBABB\uD574 \uBE44\uAD50\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${errCode(e)})`);
4458
5257
  }
4459
5258
  }
5259
+ let removedKeys = [];
5260
+ let nextText = `${JSON.stringify(axisConfig, null, 2)}
5261
+ `;
5262
+ if (previousText !== null && current !== null) {
5263
+ const { merged, removed } = mergeInitConfig(current, axisConfig);
5264
+ removedKeys = removed;
5265
+ const body = previousText.startsWith(BOM) ? previousText.slice(1) : previousText;
5266
+ nextText = (previousText.startsWith(BOM) ? BOM : "") + formatJsonLike(body, merged);
5267
+ }
4460
5268
  if (previousText !== null && previousText !== nextText) {
4461
5269
  try {
4462
- const st = statSync3(configPath);
5270
+ const st = statSync4(configPath);
4463
5271
  if (st.isFile() && st.nlink >= 2) rejects.push(hardLinkMessage("axis.config.json", st.nlink));
4464
5272
  } catch {
4465
5273
  }
@@ -4500,7 +5308,8 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4500
5308
  configState = "updated";
4501
5309
  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
5310
  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})`));
5311
+ 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";
5312
+ log(chalk10.green(`\u2713 axis.config.json \uAC31\uC2E0(${mergeNote}, ${registryNote}${agenticNote})`));
4504
5313
  if (migratedRegistry && existingRegistry !== void 0) {
4505
5314
  log(
4506
5315
  chalk10.yellow(
@@ -4508,9 +5317,8 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4508
5317
  )
4509
5318
  );
4510
5319
  }
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)`));
5320
+ if (removedKeys.length > 0) {
5321
+ 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
5322
  }
4515
5323
  }
4516
5324
  let utilsStateNote = "utils.ts \uB294 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4(\uC774\uBBF8 \uAC19\uC740 \uB0B4\uC6A9).";
@@ -4552,6 +5360,8 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
4552
5360
  return;
4553
5361
  }
4554
5362
  reportDependencies(plan, log);
5363
+ const animation = animationGuideLine(readPackageJsonSafe(baseDir), tailwind4, plan.installCommand);
5364
+ if (animation !== void 0) log(chalk10.yellow(` ${animation}`));
4555
5365
  if (tailwind4) {
4556
5366
  const [head, ...rest] = tailwind4GuideLines(
4557
5367
  readPackageJsonSafe(baseDir),
@@ -4572,28 +5382,31 @@ ${head}`));
4572
5382
  function registerInitCommand(program2, registryUrl = DEFAULT_REGISTRY_URL) {
4573
5383
  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
5384
  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([
5385
+ const baseDir = process.cwd();
5386
+ const tailwind4 = isTailwind4(planUtilsDependencies(baseDir).tailwind);
5387
+ const resolved = await resolveInitAnswers(baseDir, tailwind4);
5388
+ for (const note of resolved.notes) console.log(note.startsWith("!") ? chalk11.yellow(note) : chalk11.gray(note));
5389
+ const config = options.yes ? { ...resolved.answers } : await prompts2([
4577
5390
  {
4578
5391
  type: "text",
4579
5392
  name: "componentsDir",
4580
5393
  message: "\uCEF4\uD3EC\uB10C\uD2B8 \uB514\uB809\uD1A0\uB9AC:",
4581
- initial: INIT_DEFAULTS.componentsDir
5394
+ initial: resolved.answers.componentsDir
4582
5395
  },
4583
5396
  {
4584
5397
  type: tailwind4 ? null : "text",
4585
5398
  name: "tailwindConfig",
4586
5399
  message: "Tailwind \uC124\uC815 \uD30C\uC77C:",
4587
- initial: INIT_DEFAULTS.tailwindConfig
5400
+ initial: resolved.answers.tailwindConfig
4588
5401
  },
4589
5402
  {
4590
5403
  type: "text",
4591
5404
  name: "globalCss",
4592
5405
  message: "\uAE00\uB85C\uBC8C CSS \uD30C\uC77C:",
4593
- initial: INIT_DEFAULTS.globalCss
5406
+ initial: resolved.answers.globalCss
4594
5407
  }
4595
5408
  ]);
4596
- await runInit(process.cwd(), config, registryUrl);
5409
+ await runInit(baseDir, config, registryUrl);
4597
5410
  });
4598
5411
  }
4599
5412
 
@@ -4602,12 +5415,12 @@ import chalk12 from "chalk";
4602
5415
  import prompts3 from "prompts";
4603
5416
 
4604
5417
  // ../axis-registry-core/src/add/add.ts
4605
- import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
5418
+ import { existsSync as existsSync8, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
4606
5419
  import path17 from "path";
4607
5420
  import fs13 from "fs-extra";
4608
5421
 
4609
5422
  // ../axis-registry-core/src/registry/bundled.ts
4610
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
5423
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
4611
5424
  import path16 from "path";
4612
5425
  import { fileURLToPath } from "url";
4613
5426
  function findBundledRegistryDir(moduleUrl = import.meta.url) {
@@ -4621,7 +5434,7 @@ function readBundledJson(dir, fileName) {
4621
5434
  const file = path16.join(dir, fileName);
4622
5435
  let text;
4623
5436
  try {
4624
- text = readFileSync5(file, "utf8");
5437
+ text = readFileSync7(file, "utf8");
4625
5438
  } catch {
4626
5439
  throw new Error(`\uB3D9\uBD09 \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0 ${fileName} \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(${dir})`);
4627
5440
  }
@@ -4809,7 +5622,7 @@ function createPinnedFetch(options = {}) {
4809
5622
  const judge = options.judge ?? blockedAddressReason;
4810
5623
  const send = options.request ?? httpsRequest;
4811
5624
  const maxBytes = options.maxBytes ?? PINNED_FETCH_MAX_BYTES;
4812
- return (input, init) => new Promise((resolve3, reject) => {
5625
+ return (input, init) => new Promise((resolve4, reject) => {
4813
5626
  let url;
4814
5627
  try {
4815
5628
  url = new URL(input);
@@ -4850,7 +5663,7 @@ function createPinnedFetch(options = {}) {
4850
5663
  if (failed) return;
4851
5664
  const body = Buffer.concat(chunks).toString("utf8");
4852
5665
  const status = res.statusCode ?? 0;
4853
- resolve3({ ok: status >= 200 && status < 300, status, text: async () => body });
5666
+ resolve4({ ok: status >= 200 && status < 300, status, text: async () => body });
4854
5667
  });
4855
5668
  }
4856
5669
  );
@@ -5261,7 +6074,7 @@ function relativeSpecifierResolves(fromFile, spec) {
5261
6074
  const base = path17.resolve(path17.dirname(fromFile), spec);
5262
6075
  return RESOLVE_EXTS.some((ext) => {
5263
6076
  try {
5264
- return statSync4(base + ext).isFile();
6077
+ return statSync5(base + ext).isFile();
5265
6078
  } catch {
5266
6079
  return false;
5267
6080
  }
@@ -5336,9 +6149,9 @@ function missingDependencies(root, items) {
5336
6149
  const installCommand = detectInstallCommand(root);
5337
6150
  let pkg;
5338
6151
  try {
5339
- const text = readFileSync6(path17.join(root, "package.json"), "utf8").replace(/^/, "");
6152
+ const text = readFileSync8(path17.join(root, "package.json"), "utf8").replace(/^\uFEFF/, "");
5340
6153
  const parsed = JSON.parse(text);
5341
- pkg = isPlainObject2(parsed) ? parsed : void 0;
6154
+ pkg = isPlainObject(parsed) ? parsed : void 0;
5342
6155
  } catch {
5343
6156
  pkg = void 0;
5344
6157
  }
@@ -5347,7 +6160,7 @@ function missingDependencies(root, items) {
5347
6160
  for (const item of items) {
5348
6161
  for (const dep of item.dependencies) {
5349
6162
  const name = dependencyName(dep);
5350
- if (pkg !== void 0 && hasDependency2(pkg, name) !== void 0) continue;
6163
+ if (pkg !== void 0 && hasDependency(pkg, name) !== void 0) continue;
5351
6164
  if (name in specs) continue;
5352
6165
  const explicit = dep.length > name.length ? dep.slice(name.length + 1) : void 0;
5353
6166
  specs[name] = explicit ?? ranges[name];
@@ -5457,7 +6270,7 @@ async function runAdd(cwd, requested, options, ctx) {
5457
6270
  continue;
5458
6271
  }
5459
6272
  try {
5460
- existing.set(file, readFileSync6(file.target, "utf8"));
6273
+ existing.set(file, readFileSync8(file.target, "utf8"));
5461
6274
  } catch (e) {
5462
6275
  readErrors.push(
5463
6276
  `${file.item.name}: \uAE30\uC874 \uD30C\uC77C ${rel2(root, file.target)} \uC744 \uC77D\uC9C0 \uBABB\uD574 \uBE44\uAD50\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${e.code ?? e.message})`