임동욱 임동욱 08-11
refactor: 토큰 매니페스트 제거
값의 출처가 컴파일된 CSS로 옮겨간 뒤 매니페스트가 제공하던 것은 Figma 변수 경로와
"원본에선 참조였으나 컬렉션이 없어 값으로 굳은 것"(chart-alt 18개) 표시 둘뿐이었다.
둘 다 쓰지 않기로 해 생성물·화면·변환기에서 모두 걷어낸다.

변환기는 이제 SCSS만 생성한다. 화면은 스타일시트 외에 아무것도 참조하지 않는다.

Co-Authored-By: Claude Opus 5 
@faaf03ebef2abc08ec70b06ad09faa2fe2d6f841
@fox/dev-test/dev-test.module.scss
--- @fox/dev-test/dev-test.module.scss
+++ @fox/dev-test/dev-test.module.scss
@@ -252,24 +252,12 @@
   word-break: break-all;
 }
 
-// Figma 변수 경로 — 변수 페이지와 눈으로 맞추기 위한 보조 표기.
-.tokenFigma {
-  display: block;
-  margin-block-start: fox.gap(1);
-  color: fox.color(font-neutral-subtler);
-  font-size: fox.font-size(body-xsm);
-  word-break: break-all;
-}
 
 // 다른 토큰을 가리키는 값. 실제 값과 구분되어야 "이게 참조구나"가 한눈에 보인다.
 .aliasRef {
   color: fox.color(font-theme-primary);
 }
 
-// 제공되지 않은 컬렉션을 가리키는 참조 — 감사에서 걸러내야 할 항목이다.
-.aliasForeign {
-  color: fox.color(font-system-warning);
-}
 
 // 서체 토큰 미리보기 문장.
 .demoType {
@fox/dev-test/read-css-tokens.ts
--- @fox/dev-test/read-css-tokens.ts
+++ @fox/dev-test/read-css-tokens.ts
@@ -1,5 +1,3 @@
-import { TOKEN_MANIFEST } from "./token-manifest";
-
 export interface CssToken {
   /** `--fox-` 를 뗀 이름 */
   name: string;
@@ -8,13 +6,6 @@
   declaration: string;
   /** 참조면 가리키는 토큰 이름, 아니면 null */
   alias: string | null;
-  /** Figma 변수 경로 — CSS에 없는 정보라 매니페스트에서 채운다. 없으면 null. */
-  figma: string | null;
-  /**
-   * 원본 Figma에서는 참조였지만 그 컬렉션이 제공되지 않아, 생성기가 리터럴로 떨궈
-   * CSS에는 참조 흔적이 남지 않은 경우. 이것도 CSS가 담지 못하는 정보다.
-   */
-  foreignAlias: string | null;
 }
 
 export interface CssSection {
@@ -27,13 +18,6 @@
 }
 
 const PREFIX = "--fox-";
-
-// CSS가 담지 못하는 정보(Figma 변수 경로, "미제공 컬렉션 참조였다"는 사실)만 매니페스트에서
-// 가져온다. 값 자체는 절대 여기서 읽지 않는다 — 값의 출처는 컴파일된 CSS다.
-const MANIFEST_INDEX = new Map<string, Map<string, (typeof TOKEN_MANIFEST)[number]["tokens"][number]>>();
-for (const section of TOKEN_MANIFEST) {
-  MANIFEST_INDEX.set(section.id, new Map(section.tokens.map((token) => [token.name, token])));
-}
 
 /** `var(--fox-x)` 형태면 가리키는 토큰 이름을 돌려준다. */
 function aliasOf(value: string): string | null {
@@ -99,17 +83,12 @@
   return null;
 }
 
-function toToken(sectionId: string, name: string, declaration: string): CssToken {
-  const meta = MANIFEST_INDEX.get(sectionId)?.get(name);
-  const foreign = meta?.alias?.includes("미제공") ? meta.alias : null;
-
+function toToken(name: string, declaration: string): CssToken {
   return {
     name,
     property: `${PREFIX}${name}`,
     declaration,
     alias: aliasOf(declaration),
-    figma: meta?.figma ?? null,
-    foreignAlias: foreign,
   };
 }
 
@@ -169,8 +148,7 @@
  *
  * **매니페스트가 아니라 실제 스타일시트가 값의 출처다.** 토큰 SCSS를 고치면(원칙적으로
  * 자동 생성물이라 고치면 안 되지만) 이 화면에 즉시 반영되므로, 생성물과 화면이 어긋날
- * 여지가 없다. CSS가 담지 못하는 Figma 변수 경로만 매니페스트에서 이름으로 가져온다.
- *
+ * 여지가 없다.
  * 선언 **원문**을 읽는 것이 요점이다 — 계산값을 읽으면 참조(`var(--fox-…)`)가 이미
  * 풀려 버리고, 현재 테마·뷰포트 한쪽 값만 보인다. 원문에는 light/dark와 PC/모바일이
  * 모두 남아 있어 현재 화면 상태와 무관하게 네 조합을 전부 볼 수 있다.
@@ -220,31 +198,31 @@
       label: "Light",
       category: "Mode",
       source: "컴파일된 CSS — light-dark()의 라이트 쪽",
-      tokens: colors.map((entry) => toToken("light", entry.name, entry.light)),
+      tokens: colors.map((entry) => toToken(entry.name, entry.light)),
     },
     {
       id: "dark",
       label: "Dark",
       category: "Mode",
       source: "컴파일된 CSS — light-dark()의 다크 쪽",
-      tokens: colors.map((entry) => toToken("dark", entry.name, entry.dark)),
+      tokens: colors.map((entry) => toToken(entry.name, entry.dark)),
     },
     {
       id: "pc",
       label: "PC",
       category: "Responsive",
       source: "컴파일된 CSS — @media (min-width: 768px) 블록",
-      tokens: responsive.map((name) => toToken("pc", name, pc.get(name) ?? "")),
+      tokens: responsive.map((name) => toToken(name, pc.get(name) ?? "")),
     },
     {
       id: "mobile",
       label: "Mobile",
       category: "Responsive",
       source: "컴파일된 CSS — :root 기본값(모바일 우선)",
-      tokens: responsive.map((name) => toToken("mobile", name, base.get(name) ?? "")),
+      tokens: responsive.map((name) => toToken(name, base.get(name) ?? "")),
     },
-    { id: "size", label: "Size", category: null, source: "컴파일된 CSS — :root", tokens: size.map((n) => toToken("size", n, base.get(n) ?? "")) },
-    { id: "theme", label: "Theme", category: null, source: "컴파일된 CSS — :root", tokens: theme.map((n) => toToken("theme", n, base.get(n) ?? "")) },
-    { id: "primitive", label: "Primitive", category: null, source: "컴파일된 CSS — :root", tokens: primitive.map((n) => toToken("primitive", n, base.get(n) ?? "")) },
+    { id: "size", label: "Size", category: null, source: "컴파일된 CSS — :root", tokens: size.map((n) => toToken(n, base.get(n) ?? "")) },
+    { id: "theme", label: "Theme", category: null, source: "컴파일된 CSS — :root", tokens: theme.map((n) => toToken(n, base.get(n) ?? "")) },
+    { id: "primitive", label: "Primitive", category: null, source: "컴파일된 CSS — :root", tokens: primitive.map((n) => toToken(n, base.get(n) ?? "")) },
   ];
 }
 
@fox/dev-test/token-manifest.ts (deleted)
--- @fox/dev-test/token-manifest.ts
This file is too big to display.
@fox/dev-test/token-view.tsx
--- @fox/dev-test/token-view.tsx
+++ @fox/dev-test/token-view.tsx
@@ -133,7 +133,6 @@
 
 export function TokenView({ section }: { section: CssSection }) {
   const hasPreview = section.tokens.some((token) => previewFor(token) !== null);
-  const foreignCount = section.tokens.filter((token) => token.foreignAlias).length;
 
   return (
     <section className={styles.section}>
@@ -141,11 +140,8 @@
         {section.label} · {section.tokens.length}
       </h2>
       <p className={styles.sectionNote}>
-        {section.source} — 값은 매니페스트가 아니라 실제 스타일시트에서 읽습니다. 참조면
-        가리키는 토큰을, 아니면 값을 표시합니다.
-        {foreignCount > 0
-          ? ` 이 중 ${foreignCount}개는 원본 Figma에서 제공되지 않은 컬렉션을 가리켜 값으로 굳어졌습니다.`
-          : ""}
+        {section.source} — 값은 컴파일된 스타일시트에서 직접 읽습니다. 참조면 가리키는 토큰을,
+        아니면 값을 표시합니다.
       </p>
 
       <table className={styles.table}>
@@ -161,7 +157,6 @@
             <tr key={token.name}>
               <td>
                 <span className={styles.tokenName}>{token.name}</span>
-                <span className={styles.tokenFigma}>{token.figma}</span>
               </td>
               <td className={styles.tdValue}>
                 {isColor(token) ? (
@@ -174,15 +169,6 @@
                 ) : (
                   token.declaration
                 )}
-                {token.foreignAlias ? (
-                  <span
-                    className={styles.aliasForeign}
-                    title="원본 Figma에서는 참조였으나 해당 컬렉션이 제공되지 않아 값으로 굳어졌습니다"
-                  >
-                    {" "}
-                    ({token.foreignAlias})
-                  </span>
-                ) : null}
               </td>
               {hasPreview ? <td className={styles.tdDemo}>{previewFor(token)}</td> : null}
             </tr>
@fox/tools/build-tokens.py
--- @fox/tools/build-tokens.py
+++ @fox/tools/build-tokens.py
@@ -48,13 +48,6 @@
     raw[key] = OrderedDict(leaves(doc))
 
 
-# 참조 판별용 원본 값 — resolve_refs가 덮어쓰기 전에 보관한다. 감사 화면이
-# "파일에 실제 값이 적혀 있었는가, 다른 토큰을 가리켰는가"를 구분하려면 원본이 필요하다.
-ORIGINAL_VALUE = {
-    (coll, path): node["$value"] for coll, table in raw.items() for path, node in table.items()
-}
-
-
 def resolve_refs(table):
     """같은 파일 안의 {a.b.c} 참조를 실제 값으로 치환한다."""
     for path, node in table.items():
@@ -479,139 +472,6 @@
 
 @use "root";
 """))
-
-# ── 감사용 매니페스트 ────────────────────────────────────────────────────────
-# Figma 변수 페이지와 1:1로 대조하기 위한 데이터다. SCSS/CSS는 alias를 var() 참조로
-# 흡수하고 light/dark를 light-dark() 한 줄로 합치기 때문에, 브라우저에서 읽으면
-# "원래 파일이 무엇을 적어 뒀는지"를 되살릴 수 없다. 그래서 원본 구조 그대로를 따로 낸다.
-NAMERS = {
-    "primitive": name_primitive,
-    "theme": name_theme,
-    "size": name_size,
-    "light": name_semantic,
-    "dark": name_semantic,
-    "pc": name_responsive,
-    "mobile": name_responsive,
-}
-
-SECTIONS = [
-    ("light", "Light", "Mode"),
-    ("dark", "Dark", "Mode"),
-    ("pc", "PC", "Responsive"),
-    ("mobile", "Mobile", "Responsive"),
-    ("size", "Size", None),
-    ("theme", "Theme", None),
-    ("primitive", "Primitive", None),
-]
-
-
-def literal_text(coll, path, node):
-    """파일에 적힌 값을 Figma 표기 그대로 낸다 — 변수 페이지와 눈으로 맞추기 위함이다."""
-    value = node["$value"]
-    if node["$type"] == "color":
-        return css_color(value)
-    if node["$type"] == "string":
-        return str(value)
-    return str(value)
-
-
-def resolved_text(coll, path, node):
-    """우리 단위로 변환된 최종 값. 길이는 rem, 색은 hex/rgb()."""
-    value = node["$value"]
-    if node["$type"] == "color":
-        return css_color(value)
-    if node["$type"] == "string":
-        return str(value)
-    if coll == "primitive" and path.startswith("font.weight"):
-        return str(value)
-    return css_len(value)
-
-
-def manifest_rows(coll):
-    namer = NAMERS[coll]
-    rows = []
-    # light/dark는 SCSS 출력과 같은 순서로 맞춘다 — 소속이 불분명한 chart-alt 29개를
-    # 뒤로 보내야 주요 색상이 먼저 보인다(원본 JSON에서는 이들이 루트에 먼저 온다).
-    entries = raw[coll].items()
-    if coll in ("light", "dark"):
-        entries = sorted(entries, key=lambda kv: kv[0] in CHART_ALT_GROUP)
-    for path, node in entries:
-        original = ORIGINAL_VALUE[(coll, path)]
-        alias = None
-        literal = None
-
-        if isinstance(original, str) and original.startswith("{"):
-            # 같은 파일 안의 다른 토큰을 가리킨다.
-            alias = namer(original[1:-1])
-        else:
-            data = node.get("$extensions", {}).get("com.figma.aliasData")
-            if data:
-                hit = alias_lookup.get((data["targetVariableSetName"], data["targetVariableName"]))
-                # ⚠️ 이름만으로 매칭하면 안 된다 — 이 export에는 `theme`이라는 이름의
-                # 컬렉션이 둘 있고(제공된 파랑 계열, 미제공 청록 계열) 변수 경로까지
-                # 같다. 값이 일치할 때만 우리 토큰으로 인정하고, 아니면 우리가 갖고
-                # 있지 않은 컬렉션을 가리키는 것으로 표시한다.
-                if hit and same_value(hit[1], node["$value"]):
-                    alias = hit[0]
-                else:
-                    alias = f'{data["targetVariableSetName"]}/{data["targetVariableName"]} (미제공 컬렉션)'
-            else:
-                literal = literal_text(coll, path, node)
-
-        rows.append(
-            {
-                "name": namer(path),
-                "figma": path.replace(".", "/"),
-                "type": node["$type"],
-                "literal": literal,
-                "alias": alias,
-                # 그 파일/모드에서 최종 해석된 값(우리 단위 기준). 미리보기가 현재
-                # 테마·뷰포트가 아니라 **그 섹션이 정의한 값**을 그려야 하므로, CSS
-                # 변수가 아니라 이 값을 직접 쓴다.
-                "resolved": resolved_text(coll, path, node),
-            }
-        )
-    return rows
-
-
-manifest = [
-    {"id": coll, "label": label_, "category": category, "file": FILES[coll], "tokens": manifest_rows(coll)}
-    for coll, label_, category in SECTIONS
-]
-
-manifest_ts = """// ⚠️ 이 파일은 Figma Design Tokens export에서 **자동 생성**되었습니다.
-// 손으로 고치지 말고 원본 JSON을 다시 export한 뒤 `@fox/tools/build-tokens.py`를 재실행하세요.
-//
-// Figma 변수 페이지와 대조하기 위한 데이터입니다. CSS는 alias를 var() 참조로 흡수하고
-// light/dark를 light-dark() 한 줄로 합치므로, 브라우저에서 읽어서는 "원본 파일이 무엇을
-// 적어 뒀는지"를 되살릴 수 없습니다 — 그래서 원본 구조를 그대로 담습니다.
-
-export interface ManifestToken {
-  /** 우리 CSS 변수 이름에서 `--fox-`를 뗀 것 */
-  name: string;
-  /** 원본 JSON에서의 경로 (= Figma 변수 이름) */
-  figma: string;
-  type: "color" | "number" | "string";
-  /** 파일에 값이 직접 적혀 있을 때만 채워진다 */
-  literal: string | null;
-  /** 다른 토큰을 가리킬 때만 채워진다 */
-  alias: string | null;
-  /** 그 파일/모드에서 최종 해석된 값 — 미리보기 전용(현재 테마·뷰포트와 무관) */
-  resolved: string;
-}
-
-export interface ManifestSection {
-  id: string;
-  label: string;
-  /** 사이드바 상위 분류. 없으면 최상위 항목이다. */
-  category: string | null;
-  file: string;
-  tokens: ManifestToken[];
-}
-
-export const TOKEN_MANIFEST: ManifestSection[] = """ + json.dumps(manifest, ensure_ascii=False, indent=2) + ";\n"
-
-written.append(write("../dev-test/token-manifest.ts", manifest_ts))
 
 print("생성된 파일:")
 for w in written:
Add a comment
List