@axis-ds/cli 1.7.5 → 1.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,23 +2,38 @@
2
2
  import {
3
3
  GLOBAL_CSS_CANDIDATES,
4
4
  MonetClient,
5
+ PNP_ROOT_ONLY_NOTE,
5
6
  ROOT_LAYOUT_FILES,
7
+ TAILWIND4_MIN_AXIS_VERSION,
6
8
  addCommandFor,
7
9
  addCommandPrefix,
8
10
  compareExactVersions,
11
+ componentCssResolution,
12
+ componentCssResolvable,
13
+ cssImportViaNote,
9
14
  declaredDependency,
15
+ deferredComponentImportReason,
16
+ dependencyInstallGuide,
10
17
  detectInstallCommand,
11
18
  detectProjectPaths,
12
19
  findInstalledPackage,
20
+ findPnpUnpluggedPackages,
21
+ globalCssSelectionNote,
13
22
  hasDependency,
14
23
  isPlainObject,
15
24
  isPnpProject,
25
+ outsideCssPackageResolution,
26
+ pnpProjectRoot,
27
+ pnpRootOnlyDependency,
16
28
  projectFileExists,
17
29
  resolveBaseDir,
30
+ rootDeclarationsAllowed,
18
31
  runScriptCommand,
32
+ selectGlobalCss,
33
+ tokensCssResolution,
19
34
  workspaceRootPackage,
20
35
  workspaceRunCommand
21
- } from "./chunk-YYSEFWYD.js";
36
+ } from "./chunk-EBFXCLSV.js";
22
37
  import {
23
38
  V0Client
24
39
  } from "./chunk-2PNCUPCG.js";
@@ -1924,11 +1939,14 @@ async function readAxisConfig(baseDir) {
1924
1939
  }
1925
1940
 
1926
1941
  // ../axis-registry-core/src/template/tailwind-core.ts
1927
- import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync } from "fs";
1942
+ import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "fs";
1928
1943
  import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "path";
1929
1944
  function declaredRange(baseDir, pkg, name) {
1930
1945
  return declaredDependency(baseDir, pkg, name)?.range;
1931
1946
  }
1947
+ function declaredRangeForCheck(baseDir, pkg, name) {
1948
+ return declaredDependency(baseDir, pkg, name, void 0, { appOnlyInPnp: true })?.range;
1949
+ }
1932
1950
  function isPlainObject2(value) {
1933
1951
  return typeof value === "object" && value !== null && !Array.isArray(value);
1934
1952
  }
@@ -2052,6 +2070,157 @@ var TOKENS_CSS_IMPORT = /@import\s+(?:url\(\s*)?["'](@axis-ds\/tokens\/css(?:\/s
2052
2070
  function findTokensCssImport(css) {
2053
2071
  return TOKENS_CSS_IMPORT.exec(stripCssComments(css))?.[1];
2054
2072
  }
2073
+ var CSS_IMPORT_DEPTH_LIMIT = 32;
2074
+ var CSS_IMPORT_CYCLE_DEPTH_LIMIT = 100;
2075
+ var RELATIVE_CSS_IMPORT = /@import\s*(?:"(\.{1,2}\/[^"\n]*)"|'(\.{1,2}\/[^'\n]*)')(?=([^;{}]*))/g;
2076
+ function isConditionalCssImport(tail) {
2077
+ return tail !== void 0 && tail.trim() !== "";
2078
+ }
2079
+ function resolveRelativeCssImport(fromDir, spec) {
2080
+ if (spec.endsWith("/")) return void 0;
2081
+ const target = resolve(fromDir, spec);
2082
+ if (regularFile(target)) return target;
2083
+ if (regularFile(`${target}.css`)) return `${target}.css`;
2084
+ return void 0;
2085
+ }
2086
+ var CSS_IMPORT_SPEC = /@import\s+(?:url\(\s*)?(["'])([^"'\n]+)\1/g;
2087
+ function isPackageCssSpec(spec) {
2088
+ return !spec.startsWith("./") && !spec.startsWith("../") && !spec.startsWith("/") && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(spec);
2089
+ }
2090
+ function cssImportCycleIssue(files) {
2091
+ if (files.length === 1) return `${files[0]} \uC774 \uC790\uAE30 \uC790\uC2E0\uC744 @import \uD574 \uBE4C\uB4DC\uAC00 \uC21C\uD658\uC73C\uB85C \uC2E4\uD328\uD569\uB2C8\uB2E4 \u2192 \uC21C\uD658\uD558\uB294 @import \uB97C \uC5C6\uC560\uC138\uC694`;
2092
+ return `${files.join(" \xB7 ")} \uC774 \uC11C\uB85C @import \uD574 \uBE4C\uB4DC\uAC00 \uC21C\uD658\uC73C\uB85C \uC2E4\uD328\uD569\uB2C8\uB2E4 \u2192 \uC21C\uD658\uD558\uB294 @import \uB97C \uC5C6\uC560\uC138\uC694`;
2093
+ }
2094
+ function findImportCycle(start, edges) {
2095
+ const state = /* @__PURE__ */ new Map();
2096
+ const trail = [start];
2097
+ const stack = [{ node: start, next: 0 }];
2098
+ state.set(start, "open");
2099
+ while (stack.length > 0) {
2100
+ const top = stack[stack.length - 1];
2101
+ const out = edges.get(top.node) ?? [];
2102
+ if (top.next >= out.length) {
2103
+ state.set(top.node, "done");
2104
+ stack.pop();
2105
+ trail.pop();
2106
+ continue;
2107
+ }
2108
+ const to = out[top.next++];
2109
+ const s = state.get(to);
2110
+ if (s === "open") return trail.slice(trail.indexOf(to));
2111
+ if (s === "done") continue;
2112
+ state.set(to, "open");
2113
+ trail.push(to);
2114
+ stack.push({ node: to, next: 0 });
2115
+ }
2116
+ return void 0;
2117
+ }
2118
+ function regularFile(file) {
2119
+ try {
2120
+ return statSync(file).isFile();
2121
+ } catch {
2122
+ return false;
2123
+ }
2124
+ }
2125
+ function expandGlobalCss(baseDirInput, entryPath, entryRel, entryCssDir) {
2126
+ const baseDir = resolveBaseDir(baseDirInput);
2127
+ const entry = readFileSync(entryPath, "utf8");
2128
+ let entryReal = entryPath;
2129
+ try {
2130
+ entryReal = realpathSync(entryPath);
2131
+ } catch {
2132
+ }
2133
+ const raws = /* @__PURE__ */ new Map([[entryReal, entry]]);
2134
+ const files = [];
2135
+ const packageLines = [];
2136
+ const evidenceWalk = walkRelativeCssImports(baseDir, entryReal, raws, CSS_IMPORT_DEPTH_LIMIT, false);
2137
+ const relOfReal = /* @__PURE__ */ new Map([[entryReal, entryRel]]);
2138
+ for (const node of evidenceWalk.order) {
2139
+ const isEntry = node.real === entryReal;
2140
+ const rel3 = isEntry ? entryRel : node.rel;
2141
+ const css = stripCssComments(raws.get(node.real) ?? "");
2142
+ const file = { rel: rel3, path: node.real, css, depth: node.depth };
2143
+ const cssDir = isEntry ? entryCssDir : dirname(node.real);
2144
+ if (cssDir !== void 0) file.cssDir = cssDir;
2145
+ files.push(file);
2146
+ relOfReal.set(node.real, rel3);
2147
+ for (const m of css.matchAll(CSS_IMPORT_SPEC)) {
2148
+ if (!isPackageCssSpec(m[2])) continue;
2149
+ const line = { spec: m[2], file: rel3 };
2150
+ if (cssDir !== void 0) line.cssDir = cssDir;
2151
+ packageLines.push(line);
2152
+ }
2153
+ }
2154
+ const cycleWalk = walkRelativeCssImports(baseDir, entryReal, raws, CSS_IMPORT_CYCLE_DEPTH_LIMIT, true);
2155
+ const cycle = findImportCycle(entryReal, cycleWalk.edges);
2156
+ if (cycle !== void 0) {
2157
+ for (const node of cycleWalk.order) if (!relOfReal.has(node.real)) relOfReal.set(node.real, node.rel);
2158
+ const head = files[0];
2159
+ return {
2160
+ entry,
2161
+ evidence: entry,
2162
+ files: [head],
2163
+ packageLines: packageLines.filter((l) => l.file === head.rel),
2164
+ cycle: cycle.map((p) => relOfReal.get(p) ?? p)
2165
+ };
2166
+ }
2167
+ const evidence = files.length === 1 ? entry : files.map((f) => f.css).join("\n");
2168
+ return { entry, evidence, files, packageLines };
2169
+ }
2170
+ function walkRelativeCssImports(baseDir, entryReal, raws, limit, includeConditional) {
2171
+ const order = [];
2172
+ const edges = /* @__PURE__ */ new Map();
2173
+ const visited = /* @__PURE__ */ new Set([entryReal]);
2174
+ const queue = [{ real: entryReal, rel: "", depth: 0 }];
2175
+ while (queue.length > 0) {
2176
+ const cur = queue.shift();
2177
+ order.push(cur);
2178
+ const css = stripCssComments(raws.get(cur.real) ?? "");
2179
+ const out = [];
2180
+ edges.set(cur.real, out);
2181
+ for (const m of css.matchAll(RELATIVE_CSS_IMPORT)) {
2182
+ if (!includeConditional && isConditionalCssImport(m[3])) continue;
2183
+ const target = resolveRelativeCssImport(dirname(cur.real), m[1] ?? m[2]);
2184
+ if (target === void 0) continue;
2185
+ let real;
2186
+ try {
2187
+ real = realpathSync(target);
2188
+ } catch {
2189
+ continue;
2190
+ }
2191
+ if (visited.has(real)) {
2192
+ out.push(real);
2193
+ continue;
2194
+ }
2195
+ if (cur.depth >= limit) continue;
2196
+ const rel3 = relative(baseDir, real);
2197
+ if (rel3 === "" || rel3 === ".." || rel3.startsWith(`..${sep}`) || isAbsolute(rel3)) continue;
2198
+ if (!raws.has(real)) {
2199
+ try {
2200
+ raws.set(real, readFileSync(real, "utf8"));
2201
+ } catch {
2202
+ continue;
2203
+ }
2204
+ }
2205
+ visited.add(real);
2206
+ out.push(real);
2207
+ queue.push({ real, rel: rel3.split(sep).join("/"), depth: cur.depth + 1 });
2208
+ }
2209
+ }
2210
+ return { order, edges };
2211
+ }
2212
+ function evidenceOrigin(expanded, test) {
2213
+ return expanded.files.find((f) => test(f.css));
2214
+ }
2215
+ function packageLineCssDir(expanded, spec) {
2216
+ const line = expanded.packageLines.find((l) => l.spec === spec);
2217
+ return line !== void 0 ? line.cssDir : expanded.files[0]?.cssDir;
2218
+ }
2219
+ function viaFiles(origins) {
2220
+ const out = [];
2221
+ for (const o of origins) if (o !== void 0 && o.depth > 0 && !out.includes(o.rel)) out.push(o.rel);
2222
+ return out;
2223
+ }
2055
2224
  var TOKENS_CSS_JS_IMPORT = /(?:\bimport\s+|\bfrom\s+|\brequire\s*\(\s*)(["'])(@axis-ds\/tokens\/css(?:\/shadcn|\/tailwind4)?)\1/;
2056
2225
  var AXIS_VARIABLE_DEFINITION = /(?<![\w-])--axis-[\w-]+\s*:/;
2057
2226
  function findLayoutTokensCssImport(baseDirInput) {
@@ -2070,45 +2239,92 @@ function findLayoutTokensCssImport(baseDirInput) {
2070
2239
  }
2071
2240
  return void 0;
2072
2241
  }
2242
+ var TOKENS_PACKAGE_NAME = "@axis-ds/tokens";
2243
+ function outsideCssDir(selection) {
2244
+ if (selection.path === void 0 || selection.rel === void 0) return void 0;
2245
+ const outside = selection.rel === ".." || selection.rel.startsWith("../") || isAbsolute(selection.rel);
2246
+ return outside ? dirname(selection.path) : void 0;
2247
+ }
2248
+ function outsideCssUnresolvedIssue(spec, name) {
2249
+ return `${spec} import \uAC00 \uC788\uC9C0\uB9CC \uC774 \uD30C\uC77C \uC704\uCE58\uC5D0\uC11C ${name} \uB97C \uCC3E\uC9C0 \uBABB\uD574 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC124\uCE58\uBCF8\uC774 \uC571 \uCABD node_modules \uC5D0\uB9CC \uC788\uC74C) \u2192 \uC774 \uD30C\uC77C\uC758 \uC0C1\uC704 node_modules(\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8 \uB4F1)\uC5D0 \uC124\uCE58\uD558\uAC70\uB098 globalCss \uB97C \uC571 \uC548 \uD30C\uC77C\uB85C \uBC14\uAFB8\uC138\uC694`;
2250
+ }
2251
+ function tokensInstallPhrase(baseDir) {
2252
+ const g = dependencyInstallGuide(baseDir, TOKENS_PACKAGE_NAME, `^${TAILWIND4_MIN_AXIS_VERSION}`);
2253
+ if (g.kind === "install-at-root") return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0\uC11C ${g.installCommand}`;
2254
+ return g.kind === "install" ? g.installCommand : g.addCommand;
2255
+ }
2256
+ function tokensCssUnresolvedIssue(baseDir, pkg, spec, cssDir) {
2257
+ const r = tokensCssResolution(baseDir, pkg, TOKENS_PACKAGE_NAME);
2258
+ const head = `${spec} import \uAC00 \uC788\uC9C0\uB9CC`;
2259
+ if (r.kind === "pnp-root-only") return `${head} ${TOKENS_PACKAGE_NAME} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`;
2260
+ if (r.kind === "not-installed") {
2261
+ return `${head} ${TOKENS_PACKAGE_NAME} \uAC00 \uC124\uCE58\uB418\uC9C0 \uC54A\uC544 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 \uC124\uCE58(${tokensInstallPhrase(baseDir)})`;
2262
+ }
2263
+ if (cssDir !== void 0 && outsideCssPackageResolution(baseDir, cssDir, TOKENS_PACKAGE_NAME) === "unresolved") {
2264
+ return outsideCssUnresolvedIssue(spec, TOKENS_PACKAGE_NAME);
2265
+ }
2266
+ return void 0;
2267
+ }
2073
2268
  function checkGlobalsCss(baseDirInput) {
2074
2269
  const baseDir = resolveBaseDir(baseDirInput);
2075
- for (const candidate of TAILWIND_CSS_CANDIDATES) {
2076
- const file = join(baseDir, candidate);
2077
- if (!existsSync(file)) continue;
2078
- const content = readFileSync(file, "utf8");
2079
- const css = stripCssComments(content);
2080
- const imported = findTokensCssImport(content);
2081
- let pass;
2082
- if (imported !== void 0) pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${imported} import)`;
2083
- else if (AXIS_VARIABLE_DEFINITION.test(css)) pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218(--axis-*)\uAC00 \uC815\uC758\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4`;
2084
- else {
2085
- const layout = findLayoutTokensCssImport(baseDir);
2086
- if (layout) pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${layout.file}\uC758 ${layout.spec} import)`;
2087
- }
2088
- if (pass !== void 0) {
2089
- const tailwind = detectTailwind(baseDir, readPackageJson(baseDir));
2090
- const tailwind4 = tailwind.major !== void 0 && tailwind.major >= 4;
2091
- if (!tailwind4 && findCreateNextAppDefaults(content).some((d) => d.includes("font-family: Arial"))) {
2092
- return {
2093
- name: "globals.css",
2094
- severity: "warn",
2095
- 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)`
2096
- };
2097
- }
2098
- return { name: "globals.css", severity: "pass", message: pass };
2099
- }
2100
- if (css.includes("--background") && css.includes("--primary")) {
2270
+ const selection = selectCheckGlobalCss(baseDir);
2271
+ const note = globalCssSelectionNote(selection);
2272
+ if (selection.path !== void 0 && selection.rel !== void 0) {
2273
+ const candidate = selection.rel;
2274
+ const expanded = expandGlobalCss(baseDir, selection.path, candidate, outsideCssDir(selection));
2275
+ const judged = judgeGlobalsCss(baseDir, selection, candidate, expanded);
2276
+ if (expanded.cycle !== void 0) {
2277
+ return { name: "globals.css", severity: "warn", message: `${judged.message} / ${cssImportCycleIssue(expanded.cycle)}${note}` };
2278
+ }
2279
+ return { name: "globals.css", severity: judged.severity, message: `${judged.message}${note}` };
2280
+ }
2281
+ return { name: "globals.css", severity: "fail", message: `globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4${note}` };
2282
+ }
2283
+ function judgeGlobalsCss(baseDir, selection, candidate, expanded) {
2284
+ const content = expanded.entry;
2285
+ const css = stripCssComments(content);
2286
+ const imported = findTokensCssImport(expanded.evidence);
2287
+ const pkg = readPackageJson(baseDir);
2288
+ let pass;
2289
+ let unresolved;
2290
+ let via = "";
2291
+ if (imported !== void 0) {
2292
+ const origin = evidenceOrigin(expanded, (f) => findTokensCssImport(f) === imported);
2293
+ via = cssImportViaNote(viaFiles([origin]));
2294
+ const issue = tokensCssUnresolvedIssue(baseDir, pkg, imported, origin !== void 0 ? origin.cssDir : outsideCssDir(selection));
2295
+ if (issue !== void 0) unresolved = `${candidate}\uC5D0 ${issue}`;
2296
+ else pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${imported} import)`;
2297
+ } else if (AXIS_VARIABLE_DEFINITION.test(stripCssComments(expanded.evidence))) {
2298
+ via = cssImportViaNote(viaFiles([evidenceOrigin(expanded, (f) => AXIS_VARIABLE_DEFINITION.test(f))]));
2299
+ pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218(--axis-*)\uAC00 \uC815\uC758\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4`;
2300
+ } else {
2301
+ const layout = findLayoutTokensCssImport(baseDir);
2302
+ if (layout) {
2303
+ const issue = tokensCssUnresolvedIssue(baseDir, pkg, layout.spec);
2304
+ if (issue !== void 0) unresolved = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uACE0 ${layout.file}\uC758 ${issue}`;
2305
+ else pass = `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC788\uC2B5\uB2C8\uB2E4 (${layout.file}\uC758 ${layout.spec} import)`;
2306
+ }
2307
+ }
2308
+ const head = pass ?? unresolved;
2309
+ if (head !== void 0) {
2310
+ const tailwind = detectTailwind(baseDir, pkg);
2311
+ const tailwind4 = tailwind.major !== void 0 && tailwind.major >= 4;
2312
+ if (!tailwind4 && findCreateNextAppDefaults(content).some((d) => d.includes("font-family: Arial"))) {
2101
2313
  return {
2102
- name: "globals.css",
2103
2314
  severity: "warn",
2104
- message: `${candidate}\uC5D0 \uC790\uCCB4 CSS \uBCC0\uC218(--background \xB7 --primary)\uB9CC \uC788\uACE0 AXIS \uD1A0\uD070\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 @axis-ds/tokens CSS \uB97C import \uD558\uC138\uC694`
2315
+ message: `${head}. \uB2E4\uB9CC create-next-app \uAE30\uBCF8 body font-family: Arial \uC774 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4 \u2192 \uC9C0\uC6B0\uC138\uC694(AXIS \uAE00\uAF34 \uB300\uC2E0 Arial \uC774 \uC801\uC6A9\uB428)${via}`
2105
2316
  };
2106
2317
  }
2107
- return { name: "globals.css", severity: "warn", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4` };
2318
+ return { severity: pass !== void 0 ? "pass" : "warn", message: `${head}${via}` };
2108
2319
  }
2109
- return { name: "globals.css", severity: "fail", message: "globals.css \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" };
2320
+ if (css.includes("--background") && css.includes("--primary")) {
2321
+ return {
2322
+ severity: "warn",
2323
+ message: `${candidate}\uC5D0 \uC790\uCCB4 CSS \uBCC0\uC218(--background \xB7 --primary)\uB9CC \uC788\uACE0 AXIS \uD1A0\uD070\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 @axis-ds/tokens CSS \uB97C import \uD558\uC138\uC694`
2324
+ };
2325
+ }
2326
+ return { severity: "warn", message: `${candidate}\uC5D0 AXIS CSS \uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4` };
2110
2327
  }
2111
- var TAILWIND4_MIN_AXIS_VERSION = "1.4.0";
2112
2328
  var TAILWIND4_AXIS_PACKAGES = ["@axis-ds/tokens", "@axis-ds/ui-react", "@axis-ds/agentic-ui"];
2113
2329
  function lowerOf(token) {
2114
2330
  const v = versionParts(token);
@@ -2306,7 +2522,7 @@ function hasBareCjsPresetRequire(code) {
2306
2522
  function isCommonJsConfig(file, source) {
2307
2523
  return extname(file) === ".cjs" || /\bmodule\.exports\b/.test(source);
2308
2524
  }
2309
- function packageVersionGate(baseDirInput, pkg, name, min) {
2525
+ function packageVersionGate(baseDirInput, pkg, name, min, options = {}) {
2310
2526
  const baseDir = resolveBaseDir(baseDirInput);
2311
2527
  if (!isPnpProject(baseDir)) {
2312
2528
  const installed = findInstalledPackage(baseDir, name)?.version;
@@ -2315,7 +2531,11 @@ function packageVersionGate(baseDirInput, pkg, name, min) {
2315
2531
  if (cmp !== void 0) return { status: cmp >= 0 ? "ok" : "below", source: "installed", value: installed };
2316
2532
  }
2317
2533
  }
2318
- const declared = pkg ? declaredRange(baseDir, pkg, name) : void 0;
2534
+ const declared = pkg ? declaredDependency(baseDir, pkg, name, void 0, options)?.range : void 0;
2535
+ if (declared === void 0 && options.appOnlyInPnp) {
2536
+ const rootOnly = pnpRootOnlyDependency(baseDir, pkg, name);
2537
+ if (rootOnly !== void 0) return { status: "unresolved", source: "declared", value: rootOnly.range };
2538
+ }
2319
2539
  if (declared !== void 0) {
2320
2540
  const atLeast = rangeLowerAtLeast(declared, min);
2321
2541
  if (atLeast !== void 0) return { status: atLeast ? "ok" : "below", source: "declared", value: declared };
@@ -2324,6 +2544,7 @@ function packageVersionGate(baseDirInput, pkg, name, min) {
2324
2544
  return { status: "unknown", source: "none" };
2325
2545
  }
2326
2546
  function versionGateLabel(gate) {
2547
+ if (gate.status === "unresolved") return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8 \uC120\uC5B8 ${gate.value}`;
2327
2548
  if (gate.source === "installed") return `\uC124\uCE58\uBCF8 ${gate.value}`;
2328
2549
  if (gate.source === "declared") return `\uC120\uC5B8 ${gate.value}`;
2329
2550
  return "\uC124\uCE58\uBCF8 \xB7 \uC120\uC5B8 \uC5C6\uC74C";
@@ -2336,66 +2557,134 @@ function readPackageJson(baseDir) {
2336
2557
  return void 0;
2337
2558
  }
2338
2559
  }
2339
- function tailwind4Issues(css, pkg, versions, baseDir) {
2560
+ function tailwind4Issues(input, pkg, versions, baseDir) {
2561
+ const { entry, evidence } = input;
2562
+ const cssDirOf = input.cssDirOf ?? (() => void 0);
2340
2563
  const issues = [];
2341
- const hasEntry = hasTokensTailwind4Import(css);
2564
+ const tailwindDir = cssDirOf("tailwindcss");
2565
+ if (tailwindDir !== void 0 && outsideCssPackageResolution(baseDir, tailwindDir, "tailwindcss") === "unresolved") {
2566
+ issues.push(outsideCssUnresolvedIssue("tailwindcss", "tailwindcss"));
2567
+ }
2568
+ const hasEntry = hasTokensTailwind4Import(evidence);
2342
2569
  if (!hasEntry) issues.push(`AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${TOKENS_TAILWIND4_IMPORT_LINE}`);
2343
- const missing = TAILWIND4_COMPONENT_PACKAGES.filter(
2344
- (name) => declaredRange(baseDir, pkg, name) !== void 0 && !hasComponentTailwindImport(css, name) && !hasComponentSource(css, name)
2345
- );
2570
+ const resolutions = new Map(TAILWIND4_COMPONENT_PACKAGES.map((name) => [name, componentCssResolution(baseDir, pkg, name)]));
2571
+ const missing = [];
2572
+ const unresolvedMissing = [];
2573
+ for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2574
+ const r = resolutions.get(name);
2575
+ if (r.kind === "undeclared" || r.kind === "app-lockfile") continue;
2576
+ if (hasComponentTailwindImport(evidence, name) || hasComponentSource(evidence, name)) continue;
2577
+ if (componentCssResolvable(r)) missing.push(name);
2578
+ else unresolvedMissing.push(componentCssMissingIssue(baseDir, name, r));
2579
+ }
2346
2580
  if (missing.length > 0) {
2347
2581
  issues.push(`\uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCE94 \uACBD\uB85C(tailwind.css)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 ${missing.map(componentTailwindImportLine).join(" ")}`);
2348
2582
  }
2583
+ issues.push(...unresolvedMissing);
2349
2584
  if (hasEntry) {
2350
- const defaults = findCreateNextAppDefaults(css);
2585
+ const defaults = findCreateNextAppDefaults(entry);
2351
2586
  if (defaults.length > 0) issues.push(`create-next-app \uAE30\uBCF8 \uC815\uC758\uAC00 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4(${defaults.join(" \xB7 ")}) \u2192 \uC9C0\uC6B0\uC138\uC694`);
2352
2587
  if (versions.some((v) => v.name === TAILWIND4_AXIS_PACKAGES[0])) {
2353
2588
  issues.push(`AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC9C0\uB9CC ${TAILWIND4_AXIS_PACKAGES[0]} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2354
2589
  }
2355
2590
  }
2356
2591
  for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2357
- if (hasComponentTailwindImport(css, name) && versions.some((v) => v.name === name)) {
2358
- issues.push(`${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC ${name} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2359
- }
2592
+ if (!hasComponentTailwindImport(evidence, name)) continue;
2593
+ const unresolved = componentCssPresentIssue(baseDir, name, resolutions.get(name));
2594
+ const componentDir = cssDirOf(`${name}/tailwind.css`);
2595
+ if (unresolved !== void 0) issues.push(unresolved);
2596
+ else if (componentDir !== void 0 && outsideCssPackageResolution(baseDir, componentDir, name, "tailwind.css") === "unresolved") {
2597
+ issues.push(outsideCssUnresolvedIssue(`${name}/tailwind.css`, name));
2598
+ } else if (versions.some((v) => v.name === name)) issues.push(`${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC ${name} \uBC84\uC804 \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
2360
2599
  }
2361
2600
  return issues;
2362
2601
  }
2602
+ function componentInstallPhrase(baseDir, name, range) {
2603
+ const g = dependencyInstallGuide(baseDir, name, range);
2604
+ return g.kind === "install-at-root" ? `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0\uC11C ${g.installCommand}` : g.installCommand;
2605
+ }
2606
+ function componentCssMissingIssue(baseDir, name, r) {
2607
+ const line = componentTailwindImportLine(name);
2608
+ switch (r.kind) {
2609
+ case "pnp-root-only":
2610
+ return `${name} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD55C \uB4A4 ${line}`;
2611
+ case "root-not-installed":
2612
+ return `${name} \uAC00 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 \u2192 \uC124\uCE58(${componentInstallPhrase(baseDir, name, r.range)}) \uB4A4 ${line}`;
2613
+ case "root-old":
2614
+ return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${name} \uC758 \uC124\uCE58\uBCF8(${r.installedVersion === "?" ? "\uBC84\uC804 \uBAA8\uB984" : r.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(${TAILWIND4_MIN_AXIS_VERSION} \uBD80\uD130 \uC81C\uACF5) \u2192 ${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9B0 \uB4A4 ${line}`;
2615
+ case "root-missing-file":
2616
+ return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${name} \uC758 \uC124\uCE58\uBCF8(${r.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(\uC124\uCE58\uAC00 \uC190\uC0C1\uB410\uC744 \uC218 \uC788\uC74C) \u2192 \uB2E4\uC2DC \uC124\uCE58(${componentInstallPhrase(baseDir, name, r.range)}) \uB4A4 ${line}`;
2617
+ default:
2618
+ return line;
2619
+ }
2620
+ }
2621
+ function componentCssPresentIssue(baseDir, name, r) {
2622
+ const head = `${name}/tailwind.css import \uAC00 \uC788\uC9C0\uB9CC`;
2623
+ switch (r.kind) {
2624
+ case "pnp-root-only":
2625
+ return `${head} ${name} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`;
2626
+ case "root-not-installed":
2627
+ return `${head} ${name} \uAC00 \uC124\uCE58\uB418\uC9C0 \uC54A\uC544 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 \uC124\uCE58(${componentInstallPhrase(baseDir, name, r.range)})`;
2628
+ case "root-missing-file":
2629
+ return `${head} ${name} \uC124\uCE58\uBCF8(${r.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 \uB2E4\uC2DC \uC124\uCE58(${componentInstallPhrase(baseDir, name, r.range)})`;
2630
+ case "root-old":
2631
+ return r.installedVersion === "?" ? `${head} ${name} \uC124\uCE58\uBCF8(\uBC84\uC804 \uBAA8\uB984)\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4 \uD574\uC11D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 ${TAILWIND4_MIN_AXIS_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9AC\uC138\uC694` : void 0;
2632
+ default:
2633
+ return void 0;
2634
+ }
2635
+ }
2363
2636
  function checkTailwindSetup(baseDirInput) {
2364
2637
  const baseDir = resolveBaseDir(baseDirInput);
2365
2638
  const pkg = readPackageJson(baseDir);
2366
2639
  const tailwind = detectTailwind(baseDir, pkg);
2367
2640
  if (tailwind.major !== void 0 && tailwind.major >= 4) {
2368
2641
  const label = `Tailwind ${tailwind.major}`;
2369
- for (const candidate of TAILWIND_CSS_CANDIDATES) {
2370
- const file = join(baseDir, candidate);
2371
- if (!existsSync(file)) continue;
2372
- const css = readFileSync(file, "utf8");
2373
- if (!hasTailwindImport(css)) {
2642
+ const selection = selectCheckGlobalCss(baseDir);
2643
+ const note = globalCssSelectionNote(selection);
2644
+ if (selection.path !== void 0 && selection.rel !== void 0) {
2645
+ const candidate = selection.rel;
2646
+ const expanded = expandGlobalCss(baseDir, selection.path, candidate, outsideCssDir(selection));
2647
+ if (!hasTailwindImport(expanded.evidence)) {
2374
2648
  return {
2375
2649
  name: "tailwind.config",
2376
2650
  severity: "warn",
2377
- message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4`
2651
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4${note}`
2378
2652
  };
2379
2653
  }
2654
+ const via = cssImportViaNote(
2655
+ viaFiles([
2656
+ evidenceOrigin(expanded, hasTailwindImport),
2657
+ evidenceOrigin(expanded, hasTokensTailwind4Import),
2658
+ ...TAILWIND4_COMPONENT_PACKAGES.flatMap((name) => [
2659
+ evidenceOrigin(expanded, (f) => hasComponentTailwindImport(f, name)),
2660
+ evidenceOrigin(expanded, (f) => hasComponentSource(f, name))
2661
+ ])
2662
+ ])
2663
+ );
2380
2664
  const versions = axisPackagesBelowTailwind4(pkg, baseDir);
2381
- const issues = tailwind4Issues(css, pkg, versions, baseDir);
2665
+ const issues = tailwind4Issues(
2666
+ { entry: expanded.entry, evidence: expanded.evidence, cssDirOf: (spec) => packageLineCssDir(expanded, spec) },
2667
+ pkg,
2668
+ versions,
2669
+ baseDir
2670
+ );
2382
2671
  if (issues.length > 0) {
2383
2672
  return {
2384
2673
  name: "tailwind.config",
2385
2674
  severity: "warn",
2386
- message: `${label}: ${candidate}\uC5D0 ${issues.join(" / ")}${tailwind4VersionNote(versions)}`
2675
+ message: `${label}: ${candidate}\uC5D0 ${issues.join(" / ")}${tailwind4VersionNote(versions)}${via}${note}`
2387
2676
  };
2388
2677
  }
2389
2678
  return {
2390
2679
  name: "tailwind.config",
2391
2680
  severity: "pass",
2392
- message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uC640 AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC2B5\uB2C8\uB2E4`
2681
+ message: `${label}: ${candidate}\uC5D0 @import "tailwindcss" \uC640 AXIS \uD1A0\uD070 \uC9C4\uC785\uC810\uC774 \uC788\uC2B5\uB2C8\uB2E4${via}${note}`
2393
2682
  };
2394
2683
  }
2395
2684
  return {
2396
2685
  name: "tailwind.config",
2397
2686
  severity: "warn",
2398
- message: `${label}: @import "tailwindcss" \uB97C \uD655\uC778\uD560 CSS \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4`
2687
+ message: `${label}: @import "tailwindcss" \uB97C \uD655\uC778\uD560 CSS \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4${note}`
2399
2688
  };
2400
2689
  }
2401
2690
  for (const file of TAILWIND_CONFIG_FILES) {
@@ -2405,7 +2694,14 @@ function checkTailwindSetup(baseDirInput) {
2405
2694
  if (hasTokensPresetReference(content)) {
2406
2695
  const code = stripJsComments(content);
2407
2696
  if (isCommonJsConfig(file, code) && hasBareCjsPresetRequire(code)) {
2408
- const gate = packageVersionGate(baseDir, pkg, "@axis-ds/tokens", TOKENS_CJS_PRESET_FIXED_VERSION);
2697
+ const gate = packageVersionGate(baseDir, pkg, "@axis-ds/tokens", TOKENS_CJS_PRESET_FIXED_VERSION, { appOnlyInPnp: true });
2698
+ if (gate.status === "unresolved") {
2699
+ return {
2700
+ name: "tailwind.config",
2701
+ severity: "warn",
2702
+ message: `${file}\uC5D0 AXIS preset \uCC38\uC870\uAC00 \uC788\uC9C0\uB9CC @axis-ds/tokens \uAC00 ${PNP_ROOT_ONLY_NOTE}(${versionGateLabel(gate)}) \u2192 \uC571 package.json \uC5D0 @axis-ds/tokens ${TOKENS_CJS_PRESET_FIXED_VERSION} \uC774\uC0C1\uC744 \uC120\uC5B8\uD558\uC138\uC694`
2703
+ };
2704
+ }
2409
2705
  if (gate.status !== "ok") {
2410
2706
  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`;
2411
2707
  return {
@@ -2434,31 +2730,45 @@ function readAxisConfigLike(baseDir) {
2434
2730
  return void 0;
2435
2731
  }
2436
2732
  }
2437
- function contentGlobToRegExp(glob) {
2733
+ function selectCheckGlobalCss(baseDir) {
2734
+ return selectGlobalCss(baseDir, readAxisConfigLike(baseDir)?.globalCss, TAILWIND_CSS_CANDIDATES, { requireStylesheet: true });
2735
+ }
2736
+ function contentGlobToRegExp(glob, opts = {}) {
2737
+ const noDot = opts.noDot === true;
2438
2738
  const g = glob.trim().replace(/^\.\//, "");
2739
+ const lead = (i) => noDot && (i === 0 || g[i - 1] === "/") ? "(?!\\.)" : "";
2439
2740
  let re = "";
2440
2741
  for (let i = 0; i < g.length; i++) {
2441
2742
  const c = g[i];
2442
2743
  if (c === "*") {
2443
2744
  if (g[i + 1] === "*") {
2444
2745
  const slash = g[i + 2] === "/";
2445
- re += slash ? "(?:.*/)?" : ".*";
2746
+ if (noDot) re += slash ? `(?:${lead(i)}[^/]*/(?:(?!\\.)[^/]*/)*)?` : `${lead(i)}[^/]*(?:/(?!\\.)[^/]*)*`;
2747
+ else re += slash ? "(?:.*/)?" : ".*";
2446
2748
  i += slash ? 2 : 1;
2447
- } else re += "[^/]*";
2448
- } else if (c === "?") re += "[^/]";
2749
+ } else re += `${lead(i)}[^/]*`;
2750
+ } else if (c === "?") re += `${lead(i)}[^/]`;
2449
2751
  else if (c === "{") {
2450
2752
  const end = g.indexOf("}", i);
2451
2753
  if (end === -1) {
2452
2754
  re += "\\{";
2453
2755
  continue;
2454
2756
  }
2455
- re += `(?:${g.slice(i + 1, end).split(",").map((part) => part.replace(/[.+^$()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*")).join("|")})`;
2757
+ const atStart = lead(i) !== "";
2758
+ re += `(?:${g.slice(i + 1, end).split(",").map((part) => {
2759
+ const body = part.replace(/[.+^$()|[\]\\]/g, "\\$&");
2760
+ if (!noDot) return body.replace(/\*/g, "[^/]*");
2761
+ return body.replace(
2762
+ /(\/)?\*/g,
2763
+ (_m, slash, off) => slash !== void 0 || off === 0 && atStart ? `${slash ?? ""}(?!\\.)[^/]*` : "[^/]*"
2764
+ );
2765
+ }).join("|")})`;
2456
2766
  i = end;
2457
2767
  } else if (c === "[") {
2458
2768
  const end = g.indexOf("]", i);
2459
2769
  if (end === -1) re += "\\[";
2460
2770
  else {
2461
- re += g.slice(i, end + 1);
2771
+ re += lead(i) + g.slice(i, end + 1);
2462
2772
  i = end;
2463
2773
  }
2464
2774
  } else re += c.replace(/[.+^$()|\\]/g, "\\$&");
@@ -2527,22 +2837,46 @@ function realpathPosix(p) {
2527
2837
  }
2528
2838
  return void 0;
2529
2839
  }
2530
- function contentGlobBranchCovers(glob, sample, baseDir) {
2840
+ function contentGlobBranchCovers(glob, sample, baseDir, noDot) {
2531
2841
  if (isAbsoluteGlob(glob)) {
2532
- const re = contentGlobToRegExp(glob.trim().replace(/\\/g, "/"));
2842
+ const re = contentGlobToRegExp(glob.trim().replace(/\\/g, "/"), { noDot });
2533
2843
  const absSample = resolve(baseDir, sample);
2534
2844
  if (re.test(absSample.split(sep).join("/"))) return true;
2535
2845
  const real = realpathPosix(absSample);
2536
2846
  return real !== void 0 && re.test(real);
2537
2847
  }
2538
- return leadingParentSegments(glob) === leadingParentSegments(sample) && contentGlobToRegExp(glob).test(sample);
2848
+ return leadingParentSegments(glob) === leadingParentSegments(sample) && contentGlobToRegExp(glob, { noDot }).test(sample);
2539
2849
  }
2540
- function contentGlobCovers(glob, sample, baseDirInput) {
2850
+ function contentGlobCovers(glob, sample, baseDirInput, opts = {}) {
2541
2851
  const baseDir = resolveBaseDir(baseDirInput);
2542
2852
  const branches = expandBraces(glob.trim()) ?? [glob];
2543
- return branches.some((b) => contentGlobBranchCovers(b, sample, baseDir));
2853
+ return branches.some((b) => contentGlobBranchCovers(b, sample, baseDir, opts.noDot === true));
2854
+ }
2855
+ var CONTENT_PARENT_PATH_NOTE = "\uC0C1\uC704 \uACBD\uB85C\uB294 Tailwind 3 \uAC00 \uBE4C\uB4DC\uB97C \uC2E4\uD589\uD55C \uB514\uB809\uD130\uB9AC \uAE30\uC900\uC73C\uB85C \uD480\uB9BD\uB2C8\uB2E4. \uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8 \uB4F1 \uC571 \uB514\uB809\uD130\uB9AC \uBC16\uC5D0\uC11C \uBE4C\uB4DC\uD558\uBA74 content: { relative: true, files: [...] } \uB85C \uC124\uC815 \uD30C\uC77C \uAE30\uC900\uC73C\uB85C \uBC14\uAFB8\uC138\uC694";
2856
+ function contentPnpNote(names, unplugged = []) {
2857
+ const list = [...new Set(typeof names === "string" ? [names] : names)];
2858
+ const dyn = list.map((n) => `path.join(path.dirname(require.resolve("${n}")), "..", "dist/**/*.{js,mjs}")`).join(", ");
2859
+ const needsSetup = list.filter((n) => !unplugged.includes(n));
2860
+ const meta = needsSetup.map((n) => `"${n}": { "unplugged": true }`).join(", ");
2861
+ const setup = needsSetup.length > 0 ? ` \uC815\uC801 \uACBD\uB85C\uB97C \uC4F0\uB824\uBA74 package.json \uC758 dependenciesMeta \uC5D0 ${meta} \uB97C \uB450\uACE0 yarn install \uB4A4 .yarn/unplugged \uACBD\uB85C\uB97C \uB123\uC73C\uC138\uC694` : "";
2862
+ return `Plug'n'Play \uC5D0\uB294 node_modules \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. content \uC5D0 ${dyn} \uCC98\uB7FC \uC2E4\uC81C \uC704\uCE58\uB97C \uB123\uC73C\uC138\uC694(\uC815\uC801\uC73C\uB85C \uD655\uC778\uD560 \uC218 \uC5C6\uC5B4 WARN \uC73C\uB85C \uB0A8\uC2B5\uB2C8\uB2E4).${setup}`;
2863
+ }
2864
+ function contentPnpNoteTargets(targets) {
2865
+ return targets.filter((t) => t.pnp !== void 0 && t.pnp.state !== "rootOnly").map((t) => t.label);
2866
+ }
2867
+ function contentPnpNoteFor(targets) {
2868
+ const names = contentPnpNoteTargets(targets);
2869
+ if (names.length === 0) return void 0;
2870
+ const unplugged = targets.filter((t) => t.pnp?.state === "unplugged").map((t) => t.label);
2871
+ return contentPnpNote(names, unplugged);
2872
+ }
2873
+ var CONTENT_PNP_ROOT_ONLY_ACTION = "\uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694";
2874
+ var CONTENT_PNP_NO_NODE_MODULES = "Plug'n'Play \uB77C node_modules \uACBD\uB85C\uAC00 \uC5C6\uC74C";
2875
+ function contentTargetText(t) {
2876
+ if (t.pnp?.state === "noNodeModules") return `${t.label}(${CONTENT_PNP_NO_NODE_MODULES})`;
2877
+ if (t.pnp?.state === "rootOnly") return `${t.label} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 ${CONTENT_PNP_ROOT_ONLY_ACTION}`;
2878
+ return `${t.label} \u2192 "${t.suggest}"${t.note === void 0 ? "" : ` (${t.note})`}`;
2544
2879
  }
2545
- var CONTENT_PARENT_PATH_NOTE = "\uC0C1\uC704 \uACBD\uB85C\uB294 Tailwind 3 \uAC00 \uBE4C\uB4DC\uB97C \uC2E4\uD589\uD55C \uB514\uB809\uD130\uB9AC \uAE30\uC900\uC73C\uB85C \uD480\uB824\uC694. \uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8 \uB4F1 \uC571 \uB514\uB809\uD130\uB9AC \uBC16\uC5D0\uC11C \uBE4C\uB4DC\uD558\uBA74 content: { relative: true, files: [...] } \uB85C \uC124\uC815 \uD30C\uC77C \uAE30\uC900\uC73C\uB85C \uBC14\uAFB8\uC138\uC694";
2546
2880
  function relDirOf(baseDir, dir) {
2547
2881
  const rel3 = relative(baseDir, dir);
2548
2882
  return rel3 === "" || isAbsolute(rel3) ? void 0 : rel3.split(sep).join("/");
@@ -2562,8 +2896,26 @@ function packageVersion(dir) {
2562
2896
  return void 0;
2563
2897
  }
2564
2898
  }
2565
- function packageContentTarget(baseDir, name) {
2566
- const found = isPnpProject(baseDir) ? void 0 : findInstalledPackage(baseDir, name);
2899
+ function pnpPackageContentTarget(baseDir, name, pkg) {
2900
+ const legacySample = `node_modules/${name}/dist/index.js`;
2901
+ const none = { sample: "", suggest: "", alternates: [], others: [] };
2902
+ if (pnpRootOnlyDependency(baseDir, pkg, name) !== void 0) return { ...none, pnp: { state: "rootOnly", legacySample } };
2903
+ const range = pkg === void 0 ? void 0 : hasDependency(pkg, name);
2904
+ const rels = (range === void 0 ? [] : findPnpUnpluggedPackages(baseDir, name, range)).map((d) => relDirOf(baseDir, d)).filter((r) => r !== void 0);
2905
+ const root = pnpProjectRoot(baseDir);
2906
+ if (rels.length === 0 || root === void 0) return { ...none, pnp: { state: "noNodeModules", legacySample } };
2907
+ const rootRel = relDirOf(baseDir, root);
2908
+ return {
2909
+ sample: `${rels[0]}/dist/index.js`,
2910
+ suggest: `${rootRel === void 0 ? "." : rootRel}/.yarn/unplugged/*/node_modules/${name}/dist/**/*.{js,mjs}`,
2911
+ alternates: rels.slice(1).map((r) => `${r}/dist/index.js`),
2912
+ others: [],
2913
+ pnp: { state: "unplugged", legacySample }
2914
+ };
2915
+ }
2916
+ function packageContentTarget(baseDir, name, pkg) {
2917
+ if (isPnpProject(baseDir)) return pnpPackageContentTarget(baseDir, name, pkg);
2918
+ const found = findInstalledPackage(baseDir, name);
2567
2919
  const relDir = (found === void 0 ? void 0 : relDirOf(baseDir, found.dir)) ?? `node_modules/${name}`;
2568
2920
  const alternates = [];
2569
2921
  const others = [];
@@ -2584,7 +2936,7 @@ function packageContentTarget(baseDir, name) {
2584
2936
  const versions = theirs === void 0 || ours === void 0 ? "" : theirs === ours ? `(\uBC84\uC804\uC740 ${ours} \uB85C \uAC19\uC74C)` : `(${theirs}, \uC571 ${ours})`;
2585
2937
  others.push({
2586
2938
  sample: `${rel3}/dist/index.js`,
2587
- note: `\uC0C1\uC704 \uACBD\uB85C ${rel3} \uB294 \uC571\uC774 \uC4F0\uB294 \uC124\uCE58\uBCF8\uACFC \uB2E4\uB978 \uC124\uCE58\uBCF8${versions}\uC774\uB77C \uB36E\uC74C\uC73C\uB85C \uBCF4\uC9C0 \uC54A\uC544\uC694`
2939
+ note: `\uC0C1\uC704 \uACBD\uB85C ${rel3} \uB294 \uC571\uC774 \uC4F0\uB294 \uC124\uCE58\uBCF8\uACFC \uB2E4\uB978 \uC124\uCE58\uBCF8${versions}\uC774\uB77C \uB36E\uC74C\uC73C\uB85C \uBCF4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4`
2588
2940
  });
2589
2941
  }
2590
2942
  }
@@ -2661,7 +3013,7 @@ function inspectTailwindContent(baseDirInput) {
2661
3013
  }
2662
3014
  for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2663
3015
  if (declaredRange(baseDir, pkg, name) !== void 0) {
2664
- targets.push({ label: name, ...packageContentTarget(baseDir, name) });
3016
+ targets.push({ label: name, ...packageContentTarget(baseDir, name, pkg) });
2665
3017
  }
2666
3018
  }
2667
3019
  if (targets.length === 0) return null;
@@ -2671,11 +3023,14 @@ function inspectTailwindContent(baseDirInput) {
2671
3023
  configFile,
2672
3024
  unreadable: read === void 0,
2673
3025
  dynamic: read?.dynamic ?? false,
2674
- targets: targets.map(({ alternates = [], others = [], ...t }) => {
3026
+ targets: targets.map(({ alternates = [], others = [], pnp, ...t }) => {
2675
3027
  const covers = (sample) => globs.some((g) => contentGlobCovers(g, sample, baseDir));
2676
- const covered = [t.sample, ...alternates].some(covers);
3028
+ const samples = pnp !== void 0 && pnp.state !== "unplugged" ? [] : [t.sample, ...alternates];
3029
+ const coversSample = pnp?.state === "unplugged" ? (sample) => globs.some((g) => contentGlobCovers(g, sample, baseDir, { noDot: true })) : covers;
3030
+ const covered = samples.some(coversSample);
2677
3031
  const note = covered ? void 0 : others.find((o) => covers(o.sample))?.note;
2678
- return note === void 0 ? { ...t, covered } : { ...t, covered, note };
3032
+ const base = note === void 0 ? { ...t, covered } : { ...t, covered, note };
3033
+ return pnp === void 0 ? base : { ...base, pnp: { state: pnp.state, legacyCovered: covers(pnp.legacySample) } };
2679
3034
  })
2680
3035
  };
2681
3036
  }
@@ -2690,19 +3045,30 @@ function checkTailwindContent(baseDir) {
2690
3045
  message: `${r.configFile}\uC758 content \uAC00 ${r.targets.map((t) => t.label).join(" \xB7 ")} \uB97C \uB36E\uC2B5\uB2C8\uB2E4`
2691
3046
  };
2692
3047
  }
2693
- const list = missing.map((t) => `${t.label} \u2192 "${t.suggest}"${t.note === void 0 ? "" : ` (${t.note})`}`).join(" \xB7 ");
3048
+ const list = missing.map(contentTargetText).join(" \xB7 ");
2694
3049
  const note = missing.some((t) => t.suggest.startsWith("../")) ? ` (${CONTENT_PARENT_PATH_NOTE})` : "";
3050
+ const pnpText = contentPnpNoteFor(missing);
3051
+ const pnpNote = pnpText === void 0 ? "" : ` (${pnpText})`;
2695
3052
  if (r.unreadable || r.dynamic) {
2696
3053
  return {
2697
3054
  name: "tailwind.content",
2698
3055
  severity: "warn",
2699
- message: `${r.configFile}\uC758 content \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${list}${note}`
3056
+ message: `${r.configFile}\uC758 content \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${list}${note}${pnpNote}`
3057
+ };
3058
+ }
3059
+ if (missing.every((t) => t.pnp?.legacyCovered === true)) {
3060
+ const labels = missing.map((t) => t.label).join(" \xB7 ");
3061
+ const details = missing.filter((t) => t.pnp?.state !== "noNodeModules").map(contentTargetText);
3062
+ return {
3063
+ name: "tailwind.content",
3064
+ severity: "warn",
3065
+ message: `${r.configFile}\uC758 content \uAC00 Plug'n'Play \uC5D0\uC11C \uC2A4\uCE94\uB418\uC9C0 \uC54A\uB294 \uACBD\uB85C\uB85C ${labels} \uB97C \uB36E\uC2B5\uB2C8\uB2E4(node_modules \uACBD\uB85C \xB7 ./** \uB294 \uC2A4\uCE94\uB418\uC9C0 \uC54A\uC74C)${details.length > 0 ? `: ${details.join(" \xB7 ")}` : ""}${note}${pnpNote}`
2700
3066
  };
2701
3067
  }
2702
3068
  return {
2703
3069
  name: "tailwind.content",
2704
3070
  severity: "fail",
2705
- message: `${r.configFile}\uC758 content \uAC00 \uCEF4\uD3EC\uB10C\uD2B8 \uACBD\uB85C\uB97C \uB36E\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uD074\uB798\uC2A4\uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC74C): ${list}${note}`
3071
+ message: `${r.configFile}\uC758 content \uAC00 \uCEF4\uD3EC\uB10C\uD2B8 \uACBD\uB85C\uB97C \uB36E\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uD074\uB798\uC2A4\uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC74C): ${list}${note}${pnpNote}`
2706
3072
  };
2707
3073
  }
2708
3074
  var ANIMATION_PLUGIN_TAILWIND3 = "tailwindcss-animate";
@@ -2751,30 +3117,62 @@ function checkAnimationPlugin(baseDirInput) {
2751
3117
  if (!declared && !copied) return null;
2752
3118
  const by = declared ? "@axis-ds/ui-react \uC120\uC5B8" : "componentsDir \uC758 animate-in \uB958 \uD074\uB798\uC2A4";
2753
3119
  const tailwind = detectTailwind(baseDir, pkg);
3120
+ const pnp = isPnpProject(baseDir);
3121
+ const pluginProblem = (name) => {
3122
+ if (declaredRangeForCheck(baseDir, pkg, name) === void 0) {
3123
+ return pnpRootOnlyDependency(baseDir, pkg, name) !== void 0 ? `${name} \uAC00 ${PNP_ROOT_ONLY_NOTE}` : `${name} \uC120\uC5B8 \uC5C6\uC74C`;
3124
+ }
3125
+ if (!pnp && findInstalledPackage(baseDir, name) === void 0) return `${name} \uBBF8\uC124\uCE58(\uC120\uC5B8\uB9CC \uC788\uC74C)`;
3126
+ return void 0;
3127
+ };
2754
3128
  if (tailwind.major !== void 0 && tailwind.major >= 4) {
2755
- const file = TAILWIND_CSS_CANDIDATES.find((c) => existsSync(join(baseDir, c)));
2756
- const css = file !== void 0 ? stripCssComments(readFileSync(join(baseDir, file), "utf8")) : "";
2757
- const ok = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${ANIMATION_PLUGIN_TAILWIND4}["']`).test(css) || new RegExp(`@plugin\\s+["']${ANIMATION_PLUGIN_TAILWIND3}["']`).test(css);
2758
- return ok ? { name: "animation", severity: "pass", message: `${file}\uC5D0 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4` } : {
3129
+ const selection = selectCheckGlobalCss(baseDir);
3130
+ const note = globalCssSelectionNote(selection);
3131
+ const file = selection.rel;
3132
+ const expanded = selection.path !== void 0 && selection.rel !== void 0 ? expandGlobalCss(baseDir, selection.path, selection.rel, outsideCssDir(selection)) : void 0;
3133
+ const css = expanded !== void 0 ? stripCssComments(expanded.evidence) : "";
3134
+ const twAnimate = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${ANIMATION_PLUGIN_TAILWIND4}["']`);
3135
+ const animatePlugin = new RegExp(`@plugin\\s+["']${ANIMATION_PLUGIN_TAILWIND3}["']`);
3136
+ const lines = [];
3137
+ if (twAnimate.test(css)) lines.push(ANIMATION_PLUGIN_TAILWIND4);
3138
+ if (animatePlugin.test(css)) lines.push(ANIMATION_PLUGIN_TAILWIND3);
3139
+ const via = expanded !== void 0 ? cssImportViaNote(viaFiles([evidenceOrigin(expanded, (f) => twAnimate.test(f)), evidenceOrigin(expanded, (f) => animatePlugin.test(f))])) : "";
3140
+ if (lines.length === 0) {
3141
+ return {
3142
+ name: "animation",
3143
+ severity: "warn",
3144
+ message: `\uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(${by}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 ${ANIMATION_PLUGIN_TAILWIND4} \uC124\uCE58 \uB4A4 @import "${ANIMATION_PLUGIN_TAILWIND4}";${note}`
3145
+ };
3146
+ }
3147
+ const problems = lines.map((name) => ({ name, why: pluginProblem(name) })).filter((x) => x.why !== void 0);
3148
+ if (problems.length === 0) return { name: "animation", severity: "pass", message: `${file}\uC5D0 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4${via}${note}` };
3149
+ return {
2759
3150
  name: "animation",
2760
3151
  severity: "warn",
2761
- 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}";`
3152
+ message: `${file}\uC758 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${by}, ${problems.map((x) => x.why).join(" \xB7 ")}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uACE0 \uBE4C\uB4DC\uAC00 \uC2E4\uD328\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4 \u2192 ${problems.map((x) => x.name).join(" \xB7 ")} \uB97C package.json \uC5D0 \uC120\uC5B8 \xB7 \uC124\uCE58\uD558\uC138\uC694${via}${note}`
2762
3153
  };
2763
3154
  }
2764
3155
  const configFile = TAILWIND_CONFIG_FILES.find((f) => existsSync(join(baseDir, f)));
2765
3156
  const source = configFile !== void 0 ? stripJsComments(readFileSync(join(baseDir, configFile), "utf8")) : "";
2766
3157
  const plugins = configFile !== void 0 ? readAnimationPluginUse(source) : "absent";
2767
- const pluginDeclared = declaredRange(baseDir, pkg, ANIMATION_PLUGIN_TAILWIND3) !== void 0;
2768
- const pluginInstalled = pluginDeclared && (isPnpProject(baseDir) || findInstalledPackage(baseDir, ANIMATION_PLUGIN_TAILWIND3) !== void 0);
2769
- if (plugins === "found" && pluginDeclared && pluginInstalled) {
3158
+ const pluginReason = pluginProblem(ANIMATION_PLUGIN_TAILWIND3);
3159
+ if (plugins === "found" && pluginReason === void 0) {
2770
3160
  return { name: "animation", severity: "pass", message: `${configFile}\uC758 plugins \uC5D0 ${ANIMATION_PLUGIN_TAILWIND3} \uAC00 \uC788\uC2B5\uB2C8\uB2E4` };
2771
3161
  }
2772
3162
  const cfg = configFile ?? "tailwind.config";
3163
+ const configReason = plugins === "dynamic" ? `${cfg} plugins \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD568` : plugins !== "found" ? `${cfg} plugins \uC5D0 \uC5C6\uC74C` : void 0;
3164
+ if (pluginReason !== void 0 && pnpRootOnlyDependency(baseDir, pkg, ANIMATION_PLUGIN_TAILWIND3) !== void 0) {
3165
+ const extra = configReason !== void 0 ? `, ${configReason}` : "";
3166
+ const plug = configReason !== void 0 ? ` \xB7 plugins \uC5D0 require("${ANIMATION_PLUGIN_TAILWIND3}")` : "";
3167
+ return {
3168
+ name: "animation",
3169
+ severity: "warn",
3170
+ message: `\uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778 ${ANIMATION_PLUGIN_TAILWIND3} \uAC00 ${PNP_ROOT_ONLY_NOTE}(${by}${extra}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 \uC571 package.json \uC5D0 ${ANIMATION_PLUGIN_TAILWIND3} \uB97C \uC120\uC5B8\uD558\uC138\uC694${plug}`
3171
+ };
3172
+ }
2773
3173
  const reasons = [];
2774
- if (!pluginDeclared) reasons.push(`${ANIMATION_PLUGIN_TAILWIND3} \uC120\uC5B8 \uC5C6\uC74C`);
2775
- else if (!pluginInstalled) reasons.push(`${ANIMATION_PLUGIN_TAILWIND3} \uBBF8\uC124\uCE58(\uC120\uC5B8\uB9CC \uC788\uC74C)`);
2776
- if (plugins === "dynamic") reasons.push(`${cfg} plugins \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD568`);
2777
- else if (plugins !== "found") reasons.push(`${cfg} plugins \uC5D0 \uC5C6\uC74C`);
3174
+ if (pluginReason !== void 0) reasons.push(pluginReason);
3175
+ if (configReason !== void 0) reasons.push(configReason);
2778
3176
  return {
2779
3177
  name: "animation",
2780
3178
  severity: "warn",
@@ -3182,7 +3580,7 @@ import path10 from "path";
3182
3580
  import chalk7 from "chalk";
3183
3581
 
3184
3582
  // src/theme/brand-core.ts
3185
- import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "fs";
3583
+ import { existsSync as existsSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
3186
3584
  import { createRequire } from "module";
3187
3585
  import { dirname as dirname2, join as join2, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
3188
3586
  var TOKENS_PACKAGE = "@axis-ds/tokens";
@@ -3246,27 +3644,16 @@ function brandImportPath(entry) {
3246
3644
  }
3247
3645
  function isFile(file) {
3248
3646
  try {
3249
- return statSync(file).isFile();
3647
+ return statSync2(file).isFile();
3250
3648
  } catch {
3251
3649
  return false;
3252
3650
  }
3253
3651
  }
3254
3652
  function findGlobalCss(baseDir, config) {
3255
- const root = resolve2(baseDir);
3653
+ const selection = selectGlobalCss(resolve2(baseDir), config?.globalCss, GLOBAL_CSS_CANDIDATES);
3256
3654
  const result = {};
3257
- const configured = config?.globalCss;
3258
- if (typeof configured === "string" && configured.trim() !== "") {
3259
- const file = resolve2(root, configured);
3260
- if (isFile(file)) return { path: file };
3261
- result.configuredMissing = configured;
3262
- }
3263
- for (const candidate of GLOBAL_CSS_CANDIDATES) {
3264
- const file = join2(root, candidate);
3265
- if (isFile(file)) {
3266
- result.path = file;
3267
- break;
3268
- }
3269
- }
3655
+ if (selection.configuredMissing !== void 0) result.configuredMissing = selection.configuredMissing;
3656
+ if (selection.path !== void 0) result.path = selection.path;
3270
3657
  return result;
3271
3658
  }
3272
3659
  function findBrandImports(css) {
@@ -3293,18 +3680,24 @@ function inspectBrandImport(baseDir) {
3293
3680
  const brand = config?.brand;
3294
3681
  if (typeof brand !== "string" || brand.trim() === "") return null;
3295
3682
  const root = resolve2(baseDir);
3296
- const location = findGlobalCss(root, config);
3683
+ const location = selectGlobalCss(root, config?.globalCss, GLOBAL_CSS_CANDIDATES, { requireStylesheet: true });
3297
3684
  const base = { brand };
3298
3685
  if (location.configuredMissing !== void 0) base.configuredMissing = location.configuredMissing;
3686
+ if (location.configuredRejected !== void 0) base.configuredRejected = location.configuredRejected;
3299
3687
  if (!location.path) {
3300
3688
  return { ...base, state: "css-missing", imports: [] };
3301
3689
  }
3302
3690
  const cssFile = relative2(root, location.path).split(sep2).join("/");
3303
- const imports = [...new Set(findBrandImports(readFileSync2(location.path, "utf8")))];
3691
+ const expanded = expandGlobalCss(root, location.path, cssFile);
3692
+ const imports = [...new Set(findBrandImports(expanded.evidence))];
3304
3693
  let state;
3305
3694
  if (imports.includes(brand)) state = "match";
3306
3695
  else if (imports.length === 0) state = "import-missing";
3307
3696
  else state = "other-brand";
3697
+ const sub = expanded.files.filter((f) => f.depth > 0);
3698
+ const via = state === "match" ? sub.filter((f) => findBrandImports(f.css).includes(brand)).slice(0, 1) : state === "other-brand" ? sub.filter((f) => findBrandImports(f.css).length > 0) : [];
3699
+ const entryHas = state === "match" && findBrandImports(expanded.files[0].css).includes(brand);
3700
+ if (via.length > 0 && !entryHas) return { ...base, state, cssFile, imports, importVia: via.map((f) => f.rel) };
3308
3701
  return { ...base, state, cssFile, imports };
3309
3702
  }
3310
3703
  function isMissingBuildFile(err) {
@@ -3439,7 +3832,11 @@ function inspectBrandActivation(baseDir) {
3439
3832
  if (typeof brand !== "string" || brand.trim() === "") return null;
3440
3833
  const location = findBrandActivation(baseDir);
3441
3834
  if (!location.markupWithProvider) return { brand, ...location };
3442
- return { brand, ...location, theme: packageVersionGate(baseDir, readProjectPackageJson(baseDir), THEME_PACKAGE, THEME_MARKUP_BRAND_MIN_VERSION) };
3835
+ return {
3836
+ brand,
3837
+ ...location,
3838
+ theme: packageVersionGate(baseDir, readProjectPackageJson(baseDir), THEME_PACKAGE, THEME_MARKUP_BRAND_MIN_VERSION, { appOnlyInPnp: true })
3839
+ };
3443
3840
  }
3444
3841
  var THEME_PACKAGE = "@axis-ds/theme";
3445
3842
  function readProjectPackageJson(baseDir) {
@@ -3512,25 +3909,43 @@ function checkDependencies(baseDirInput) {
3512
3909
  return { name: "dependencies", severity: "fail", message: "package.json \uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(JSON \uD655\uC778)" };
3513
3910
  }
3514
3911
  const rootPkg = workspaceRootPackage(baseDir) ?? null;
3515
- const declared = (d) => declaredDependency(baseDir, pkg, d, rootPkg);
3912
+ const pnp = isPnpProject(baseDir);
3913
+ const declared = (d) => declaredDependency(baseDir, pkg, d, rootPkg, { appOnlyInPnp: true });
3914
+ const pnpRootOnly = (d) => pnpRootOnlyDependency(baseDir, pkg, d, rootPkg) !== void 0;
3516
3915
  const fromRoot = (d) => declared(d)?.source === "root";
3517
- const missingRequired = REQUIRED_DEPENDENCIES.filter((d) => declared(d) === void 0);
3518
- if (missingRequired.length > 0) {
3519
- return { name: "dependencies", severity: "fail", message: `\uD544\uC218 \uC758\uC874\uC131 \uB204\uB77D: ${missingRequired.join(", ")}` };
3520
- }
3521
- const missingRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => declared(d) === void 0);
3522
- const rootDeclared = [...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => fromRoot(d));
3916
+ const missingRequired = REQUIRED_DEPENDENCIES.filter((d) => declared(d) === void 0 && !pnpRootOnly(d));
3917
+ const pnpRootRequired = REQUIRED_DEPENDENCIES.filter((d) => pnpRootOnly(d));
3918
+ if (missingRequired.length > 0 || pnpRootRequired.length > 0) {
3919
+ const fails = [];
3920
+ if (missingRequired.length > 0) fails.push(`\uD544\uC218 \uC758\uC874\uC131 \uB204\uB77D: ${missingRequired.join(", ")}`);
3921
+ if (pnpRootRequired.length > 0) fails.push(`\uD544\uC218 \uC758\uC874\uC131 ${pnpRootRequired.join(", ")} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`);
3922
+ return { name: "dependencies", severity: "fail", message: fails.join(" / ") };
3923
+ }
3924
+ const missingRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => declared(d) === void 0 && !pnpRootOnly(d));
3925
+ const pnpRootRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => pnpRootOnly(d));
3926
+ const appAxis = declaredAxisPackages(pkg);
3927
+ const rootOnlyAxis = rootPkg !== null ? declaredAxisPackages(rootPkg).filter((d) => !appAxis.includes(d)) : [];
3928
+ const rootAxis = pnp ? [] : rootOnlyAxis;
3929
+ const pnpRootAxis = pnp ? rootOnlyAxis : [];
3930
+ const rootDeclared = [...[...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => fromRoot(d)), ...rootAxis];
3523
3931
  const rootNote = rootDeclared.length > 0 ? `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8: ${rootDeclared.join(", ")}` : void 0;
3524
3932
  const targets = [
3525
3933
  ...[...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => declared(d) !== void 0),
3526
- ...declaredAxisPackages(pkg)
3934
+ ...appAxis,
3935
+ ...rootAxis
3527
3936
  ];
3528
- const pnp = isPnpProject(baseDir);
3529
3937
  const notInstalled = pnp ? [] : targets.filter((d) => findInstalledPackage(baseDir, d) === void 0);
3938
+ const notInstalledRoot = notInstalled.filter((d) => rootAxis.includes(d));
3939
+ const notInstalledOther = notInstalled.filter((d) => !rootAxis.includes(d));
3530
3940
  const problems = [];
3531
3941
  if (missingRecommended.length > 0) problems.push(`\uAD8C\uC7A5 \uC758\uC874\uC131 \uB204\uB77D: ${missingRecommended.join(", ")}`);
3532
- if (notInstalled.length > 0) {
3533
- problems.push(`\uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC740 \uC758\uC874\uC131: ${notInstalled.join(", ")} \u2192 ${detectInstallCommand(baseDir)}`);
3942
+ if (pnpRootRecommended.length > 0) problems.push(`\uAD8C\uC7A5 \uC758\uC874\uC131 ${pnpRootRecommended.join(", ")} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`);
3943
+ if (pnpRootAxis.length > 0) problems.push(`${pnpRootAxis.join(", ")} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`);
3944
+ if (notInstalledOther.length > 0) {
3945
+ problems.push(`\uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC740 \uC758\uC874\uC131: ${notInstalledOther.join(", ")} \u2192 ${detectInstallCommand(baseDir)}`);
3946
+ }
3947
+ if (notInstalledRoot.length > 0) {
3948
+ problems.push(`\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB3FC \uC788\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: ${notInstalledRoot.join(", ")} \u2192 ${detectInstallCommand(baseDir)}`);
3534
3949
  }
3535
3950
  if (problems.length > 0) {
3536
3951
  return { name: "dependencies", severity: "warn", message: problems.join(" / ") + (rootNote ? ` (${rootNote})` : "") };
@@ -3640,13 +4055,14 @@ function checkBrand(baseDir) {
3640
4055
  }
3641
4056
  function brandCheckResult(check) {
3642
4057
  const { brand } = check;
3643
- const suffix = check.configuredMissing !== void 0 ? ` (globalCss ${check.configuredMissing} \uC5C6\uC74C)` : "";
4058
+ const suffix = globalCssSelectionNote(check);
4059
+ const via = cssImportViaNote(check.importVia);
3644
4060
  switch (check.state) {
3645
4061
  case "match":
3646
4062
  return {
3647
4063
  name: "brand",
3648
4064
  severity: "pass",
3649
- message: `brand "${brand}" import \uAC00 ${check.cssFile}\uC5D0 \uC788\uC2B5\uB2C8\uB2E4${suffix}`
4065
+ message: `brand "${brand}" import \uAC00 ${check.cssFile}\uC5D0 \uC788\uC2B5\uB2C8\uB2E4${via}${suffix}`
3650
4066
  };
3651
4067
  case "css-missing":
3652
4068
  return {
@@ -3664,11 +4080,18 @@ function brandCheckResult(check) {
3664
4080
  return {
3665
4081
  name: "brand",
3666
4082
  severity: "warn",
3667
- message: `brand "${brand}" \uC640 import(${check.imports.join(", ")})\uAC00 \uB2E4\uB985\uB2C8\uB2E4${suffix}`
4083
+ message: `brand "${brand}" \uC640 import(${check.imports.join(", ")})\uAC00 \uB2E4\uB985\uB2C8\uB2E4${via}${suffix}`
3668
4084
  };
3669
4085
  }
3670
4086
  }
3671
4087
  function brandActivationResult(check) {
4088
+ if (check.found && check.theme?.status === "unresolved") {
4089
+ return {
4090
+ name: "brand.activation",
4091
+ severity: "warn",
4092
+ message: `${check.found}\uC758 <html data-brand> \uB97C ThemeProvider \uC640 \uD568\uAED8 \uC4F0\uB294\uB370 @axis-ds/theme \uAC00 ${PNP_ROOT_ONLY_NOTE}(${versionGateLabel(check.theme)}) \u2192 \uC571 package.json \uC5D0 @axis-ds/theme ${THEME_MARKUP_BRAND_MIN_VERSION} \uC774\uC0C1\uC744 \uC120\uC5B8\uD558\uAC70\uB098 setBrand("${check.brand}")`
4093
+ };
4094
+ }
3672
4095
  if (check.found && check.theme && check.theme.status !== "ok") {
3673
4096
  const label = versionGateLabel(check.theme);
3674
4097
  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`;
@@ -3802,13 +4225,11 @@ import chalk9 from "chalk";
3802
4225
  import path12 from "path";
3803
4226
 
3804
4227
  // src/theme/theme-add.ts
3805
- import { accessSync, constants, existsSync as existsSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, statSync as statSync2, unlinkSync, writeFileSync } from "fs";
4228
+ import { accessSync, constants, existsSync as existsSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, unlinkSync, writeFileSync } from "fs";
3806
4229
  import path11 from "path";
3807
4230
  var TOKENS_DEPENDENCY_RANGE = `^${TOKENS_DEPENDENCY_MIN_VERSION}`;
3808
4231
  var TAILWIND3_BASE_IMPORTS = [`${TOKENS_PACKAGE}/css`, `${TOKENS_PACKAGE}/css/shadcn`];
3809
4232
  var THEME_MIN_VERSION = "1.3.0";
3810
- var COMPONENT_TAILWIND_CSS_MIN_VERSION = "1.4.0";
3811
- var APP_LOCKFILES = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"];
3812
4233
  function readText(file) {
3813
4234
  return existsSync3(file) ? readFileSync4(file, "utf8") : null;
3814
4235
  }
@@ -3907,13 +4328,6 @@ function realPathInside(baseDir, file) {
3907
4328
  const inside = relPath !== "" && !relPath.startsWith("..") && !path11.isAbsolute(relPath);
3908
4329
  return { inside, realPath };
3909
4330
  }
3910
- function isFile2(file) {
3911
- try {
3912
- return statSync2(file).isFile();
3913
- } catch {
3914
- return false;
3915
- }
3916
- }
3917
4331
  function isStylesheet(realPath) {
3918
4332
  return path11.extname(realPath).toLowerCase() === ".css";
3919
4333
  }
@@ -3928,6 +4342,7 @@ function planThemeAdd(baseDirInput, brand) {
3928
4342
  unverifiedBrand: false,
3929
4343
  configChanged: false,
3930
4344
  installCommand: detectInstallCommand(baseDir),
4345
+ tokensGuide: dependencyInstallGuide(baseDir, TOKENS_PACKAGE, TOKENS_DEPENDENCY_RANGE),
3931
4346
  writes: []
3932
4347
  };
3933
4348
  const configFile = path11.join(baseDir, AXIS_CONFIG_FILE);
@@ -3981,24 +4396,18 @@ function planThemeAdd(baseDirInput, brand) {
3981
4396
  return plan;
3982
4397
  }
3983
4398
  }
3984
- const rootDeclarationsAllowed = pkg !== void 0 && !isPnpProject(baseDir) && !APP_LOCKFILES.some((f) => existsSync3(path11.join(baseDir, f)));
3985
- const rootPkg = rootDeclarationsAllowed ? workspaceRootPackage(baseDir) ?? null : null;
4399
+ const rootPkg = rootDeclarationsAllowed(baseDir, pkg) ? workspaceRootPackage(baseDir) ?? null : null;
3986
4400
  const tailwind = detectTailwind(baseDir, pkg);
3987
4401
  const tailwind4 = tailwind.major !== void 0 && tailwind.major >= 4;
3988
4402
  const componentImports = [];
3989
4403
  const deferredComponentImports = [];
3990
4404
  if (tailwind4 && pkg !== void 0) {
3991
4405
  for (const n of TAILWIND4_COMPONENT_PACKAGES) {
3992
- if (hasDependency(pkg, n) !== void 0) {
3993
- componentImports.push(n);
3994
- continue;
3995
- }
3996
- if (declaredDependency(baseDir, pkg, n, rootPkg)?.source !== "root") continue;
3997
- const installed2 = findInstalledPackage(baseDir, n);
3998
- if (installed2 !== void 0 && isFile2(path11.join(installed2.dir, "tailwind.css"))) {
3999
- componentImports.push(n);
4000
- } else {
4001
- deferredComponentImports.push(installed2 === void 0 ? { name: n } : { name: n, installedVersion: installed2.version ?? "?" });
4406
+ const r = componentCssResolution(baseDir, pkg, n);
4407
+ if (r.kind === "app" || r.kind === "root-ok") componentImports.push(n);
4408
+ else if (r.kind === "root-not-installed") deferredComponentImports.push({ name: n });
4409
+ else if (r.kind === "root-old" || r.kind === "root-missing-file") {
4410
+ deferredComponentImports.push({ name: n, installedVersion: r.installedVersion });
4002
4411
  }
4003
4412
  }
4004
4413
  }
@@ -4186,30 +4595,44 @@ function baseImportLines(after, spec, brandImportPath2, file) {
4186
4595
  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`)
4187
4596
  ];
4188
4597
  }
4189
- function tokensInstallCommand(baseDir) {
4190
- return addCommandFor(detectInstallCommand(baseDir), { [TOKENS_PACKAGE]: TOKENS_DEPENDENCY_RANGE });
4191
- }
4192
4598
  var TOKENS_INSTALL_COMMAND = addCommandFor("pnpm install", { [TOKENS_PACKAGE]: TOKENS_DEPENDENCY_RANGE });
4193
- function tokensStatusGuide(status, version, installCommand = TOKENS_INSTALL_COMMAND) {
4599
+ function tokensInstallGuide(baseDir) {
4600
+ return dependencyInstallGuide(baseDir, TOKENS_PACKAGE, TOKENS_DEPENDENCY_RANGE);
4601
+ }
4602
+ function asGuide(guide) {
4603
+ return typeof guide === "string" ? { kind: "add", installCommand: guide, addCommand: guide } : guide;
4604
+ }
4605
+ function tokensPnpMessage(range) {
4606
+ return `${TOKENS_PACKAGE}${range ? ` ${range}` : ""} \uAC00 package.json \uC5D0 \uC120\uC5B8\uB3FC \uC788\uC9C0\uB9CC Plug'n'Play \uC124\uCE58\uB77C \uC124\uCE58\uBCF8\uC744 \uD310\uC815\uD558\uC9C0 \uBABB\uD574 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`;
4607
+ }
4608
+ var ROOT_TOKENS_UPGRADE_GUIDE = `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8 package.json \uC758 ${TOKENS_PACKAGE} \uC120\uC5B8\uC744 ^${MIN_BRAND_TOKENS_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9AC\uACE0 \uC124\uCE58\uD558\uC138\uC694.`;
4609
+ function installPhrase(g) {
4610
+ if (g.kind === "install-at-root") return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0\uC11C ${chalk9.cyan(g.installCommand)}`;
4611
+ return chalk9.cyan(g.kind === "install" ? g.installCommand : g.addCommand);
4612
+ }
4613
+ function rebuildGuide(g) {
4614
+ return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uBA74 ${chalk9.cyan(workspaceRunCommand(g.installCommand, TOKENS_PACKAGE, "build"))}, \uC544\uB2C8\uBA74 \uC7AC\uC124\uCE58: ${installPhrase(g)}`;
4615
+ }
4616
+ function tokensStatusGuide(status, version, guide = TOKENS_INSTALL_COMMAND) {
4617
+ const g = asGuide(guide);
4618
+ if (g.kind === "pnp") return [chalk9.yellow(tokensPnpMessage(g.range))];
4619
+ const install = installPhrase(g);
4194
4620
  switch (status) {
4195
4621
  case "not-installed":
4196
- return [
4197
- chalk9.yellow(`${TOKENS_PACKAGE} \uAC00 \uC124\uCE58\uB3FC \uC788\uC9C0 \uC54A\uC544 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`),
4198
- ` \uC124\uCE58: ${chalk9.cyan(installCommand)}`
4199
- ];
4622
+ return [chalk9.yellow(`${TOKENS_PACKAGE} \uAC00 \uC124\uCE58\uB3FC \uC788\uC9C0 \uC54A\uC544 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`), ` \uC124\uCE58: ${install}`];
4200
4623
  case "unsupported":
4201
4624
  return [
4202
4625
  chalk9.yellow(
4203
4626
  `${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).`
4204
4627
  ),
4205
- ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(installCommand)}`
4628
+ g.source === "root" ? ` ${ROOT_TOKENS_UPGRADE_GUIDE}` : ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk9.cyan(g.addCommand)}`
4206
4629
  ];
4207
4630
  case "not-built":
4208
4631
  return [
4209
4632
  chalk9.yellow(
4210
4633
  `${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.`
4211
4634
  ),
4212
- ` \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uBA74 ${chalk9.cyan(workspaceRunCommand(installCommand, TOKENS_PACKAGE, "build"))}, \uC544\uB2C8\uBA74 \uC7AC\uC124\uCE58: ${chalk9.cyan(installCommand)}`
4635
+ ` ${rebuildGuide(g)}`
4213
4636
  ];
4214
4637
  }
4215
4638
  }
@@ -4400,13 +4823,7 @@ function renderThemeAdd(plan, written) {
4400
4823
  );
4401
4824
  }
4402
4825
  for (const d of css?.deferredComponentImports ?? []) {
4403
- const line = `@import "${d.name}/tailwind.css";`;
4404
- const current = d.installedVersion !== void 0 && d.installedVersion !== "?" && (compareExactVersions(d.installedVersion, COMPONENT_TAILWIND_CSS_MIN_VERSION) ?? -1) >= 0;
4405
- lines.push(
4406
- chalk9.yellow(
4407
- d.installedVersion === void 0 ? ` ! ${d.name} \uAC00 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC544 ${line} \uB97C \uB123\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58(${plan.installCommand}) \uB4A4 theme add \uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uC9C1\uC811 \uB123\uC73C\uC138\uC694.` : current ? ` ! \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${d.name} \uC758 \uC124\uCE58\uBCF8(${d.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4 ${line} \uB97C \uB123\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uAC00 \uC190\uC0C1\uB410\uC744 \uC218 \uC788\uC73C\uB2C8 \uB2E4\uC2DC \uC124\uCE58(${plan.installCommand}) \uB4A4 theme add \uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uC9C1\uC811 \uB123\uC73C\uC138\uC694.` : ` ! \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB41C ${d.name} \uC758 \uC124\uCE58\uBCF8(${d.installedVersion === "?" ? "\uBC84\uC804 \uBAA8\uB984" : d.installedVersion})\uC5D0 tailwind.css \uAC00 \uC5C6\uC5B4(${COMPONENT_TAILWIND_CSS_MIN_VERSION} \uBD80\uD130 \uC81C\uACF5) ${line} \uB97C \uB123\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. ${COMPONENT_TAILWIND_CSS_MIN_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9B0 \uB4A4 theme add \uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uC9C1\uC811 \uB123\uC73C\uC138\uC694.`
4408
- )
4409
- );
4826
+ lines.push(chalk9.yellow(` ! ${deferredComponentImportReason(d, plan.installCommand)}`));
4410
4827
  }
4411
4828
  if (css && css.otherBrands.length > 0) {
4412
4829
  lines.push(
@@ -4416,6 +4833,8 @@ function renderThemeAdd(plan, written) {
4416
4833
  );
4417
4834
  }
4418
4835
  const dep = plan.dependency;
4836
+ const tokensGuide = plan.tokensGuide;
4837
+ const pnpTokens = tokensGuide.kind === "pnp" && plan.tokens.status !== "ok";
4419
4838
  if (dep?.action === "add") {
4420
4839
  lines.push(
4421
4840
  ` ${chalk9.green("\u2713")} package.json: dependencies \uC5D0 "${TOKENS_PACKAGE}": "${TOKENS_DEPENDENCY_RANGE}" \uCD94\uAC00`,
@@ -4426,12 +4845,16 @@ function renderThemeAdd(plan, written) {
4426
4845
  ` ${chalk9.gray("-")} package.json: ${TOKENS_PACKAGE} ${dep.declaredRange} \uAC00 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB3FC \uC788\uC5B4 \uC571 package.json \uC740 \uBC14\uAFB8\uC9C0 \uC54A\uC74C`
4427
4846
  );
4428
4847
  if (plan.tokens.status === "not-installed") lines.push(` \uC124\uCE58: ${chalk9.cyan(plan.installCommand)}`);
4848
+ else if (plan.tokens.status === "not-built") lines.push(` ${rebuildGuide(tokensGuide)}`);
4429
4849
  } else if (dep?.action === "present") {
4430
4850
  lines.push(` ${chalk9.gray("-")} package.json: ${TOKENS_PACKAGE} ${dep.declaredRange} \uAC00 \uC774\uBBF8 \uC788\uC5B4 \uBC94\uC704\uB97C \uBC14\uAFB8\uC9C0 \uC54A\uC74C`);
4851
+ if (pnpTokens) lines.push(` ${chalk9.yellow(tokensPnpMessage(tokensGuide.range))}`);
4852
+ else if (plan.tokens.status === "not-installed") lines.push(` \uC124\uCE58: ${chalk9.cyan(plan.installCommand)}`);
4853
+ else if (plan.tokens.status === "not-built") lines.push(` ${rebuildGuide(tokensGuide)}`);
4431
4854
  } else if (dep?.action === "no-package-json") {
4432
4855
  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}`));
4433
4856
  }
4434
- if (dep?.upgrade) {
4857
+ if (dep?.upgrade && !pnpTokens) {
4435
4858
  lines.push(
4436
4859
  chalk9.yellow(
4437
4860
  ` ! \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).`
@@ -4458,13 +4881,14 @@ function renderThemeAdd(plan, written) {
4458
4881
  );
4459
4882
  }
4460
4883
  for (const t of tw.contentMissing) {
4461
- lines.push(
4462
- chalk9.yellow(
4463
- ` ! 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}"`
4464
- )
4465
- );
4884
+ const not = tw.contentUnknown ? "\uC54A\uB294 \uAC83\uC73C\uB85C \uBCF4\uC785\uB2C8\uB2E4(\uC815\uC801 \uD310\uB3C5)" : "\uC54A\uC2B5\uB2C8\uB2E4";
4885
+ if (t.pnp?.state === "noNodeModules") lines.push(chalk9.yellow(` ! content \uAC00 ${t.label} \uB97C Plug'n'Play \uC5D0\uC11C \uC2A4\uCE94\uB418\uB294 \uACBD\uB85C\uB85C \uB36E\uC9C0 ${not}`));
4886
+ else if (t.pnp?.state === "rootOnly") lines.push(chalk9.yellow(` ! ${t.label} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 ${CONTENT_PNP_ROOT_ONLY_ACTION}`));
4887
+ else lines.push(chalk9.yellow(` ! content \uAC00 ${t.label} \uB97C \uB36E\uC9C0 ${not}. \uB123\uC73C\uC138\uC694: "${t.suggest}"`));
4466
4888
  }
4467
4889
  if (tw.contentMissing.some((t) => t.suggest.startsWith("../"))) lines.push(chalk9.gray(` ${CONTENT_PARENT_PATH_NOTE}`));
4890
+ const pnpNote = contentPnpNoteFor(tw.contentMissing);
4891
+ if (pnpNote !== void 0) lines.push(chalk9.gray(` ${pnpNote}`));
4468
4892
  }
4469
4893
  const act = plan.activation;
4470
4894
  lines.push("", chalk9.bold(`\uBE0C\uB79C\uB4DC \uCF1C\uAE30 (ThemeProvider \uB294 ${THEME_PACKAGE} ${THEME_MIN_VERSION} \uC774\uC0C1)`));
@@ -4520,11 +4944,11 @@ function registerThemeCommand(program2) {
4520
4944
  const themeCmd = program2.command("theme").description("\uBE0C\uB79C\uB4DC \uD14C\uB9C8 (@axis-ds/tokens \uBE0C\uB79C\uB4DC)");
4521
4945
  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) => {
4522
4946
  const baseDir = path12.resolve(options.dir || ".");
4523
- print(renderThemeList(listBrands(baseDir), tokensInstallCommand(baseDir)));
4947
+ print(renderThemeList(listBrands(baseDir), tokensInstallGuide(baseDir)));
4524
4948
  });
4525
4949
  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) => {
4526
4950
  const baseDir = path12.resolve(options.dir || ".");
4527
- print(renderThemeInfo(getBrand(baseDir, name), name, tokensInstallCommand(baseDir)));
4951
+ print(renderThemeInfo(getBrand(baseDir, name), name, tokensInstallGuide(baseDir)));
4528
4952
  });
4529
4953
  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) => {
4530
4954
  const baseDir = path12.resolve(options.dir || ".");