melta-app 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,113 @@
1
+ /**
2
+ * define-theme — 消費者ブランドの theme を注入するための入口(設計書 Step 2-①)。
3
+ *
4
+ * melta 既定の theme は melta-contracts から codegen した `nativeTheme` だが、消費者が自分の
5
+ * ブランドトークンで塗り替えられる正規の口をここで提供する。`defineTheme()` を通した成果物だけを
6
+ * `<ThemeProvider theme={...}>` が受け取る(Chakra の `$$chakra` と同じ brand 方式。
7
+ * 生オブジェクトの直渡しを型で塞ぎ、validation を必ず1回通すため)。
8
+ *
9
+ * 設計の要点:
10
+ * - **capability は宣言でなく導出する**。「この theme は dark しか持たない」は
11
+ * `color.semantic` のキー集合そのものが語る(Primer の colorSchemes / DTCG Resolver の contexts と同じ流儀)。
12
+ * 別欄で宣言すると「宣言と実体の不一致」という新種の嘘が生まれ、それを警備する検査が要るだけになる。
13
+ * - **嘘の値で埋めない**。単一 colorScheme の theme は存在しない mode を **書かない**。
14
+ * ゼロ埋め・他 mode のコピー・センチネル値はいずれも「その mode が存在する」という嘘をトークンに残す。
15
+ * - **Proxy は使わない**。Hermes は Proxy が使われていなくても全プロパティアクセスに分岐が入る
16
+ * (hermes#33)。存在しない mode の検出は non-enumerable な getter で行う(下記)。
17
+ *
18
+ * 注: accent(primary パレット / text-accent)・elevation・status の light 値は、単一 dark の
19
+ * theme でも現状 **必須のまま**。①の射程は colorScheme 軸だけで、それらの capability 化は
20
+ * 後続(accent 依存の除去)で扱う。
21
+ */
22
+ import type { NativeTheme, SemanticColors, ThemeColor, ThemeMode } from "./types";
23
+ /**
24
+ * 開発ビルドか。**裸の `__DEV__` を直接書かないこと。**
25
+ *
26
+ * `__DEV__` は Metro のグローバルで、Metro なら prod ビルド時に `false` へ畳み込まれて
27
+ * dev ブロックごと消える。一方 melta は package.json の exports の `import` / `default` 条件で
28
+ * 同じ ESM を非 Metro の web バンドラにも配っており、react-native-web は `__DEV__` を
29
+ * **定義しない**(vendored 側で `process.env.NODE_ENV` にローカル shim している)。
30
+ * そこで `typeof` で存在を確かめ、無い環境では `NODE_ENV === "development"` のときだけ有効にする
31
+ * (判定不能なら dev 機能を**切る**側に倒す = 本番に dev コストを漏らさない)。
32
+ * `process` を識別子として直接参照しないのは、それを書くと tsconfig.build.json(bob build)だけが
33
+ * TS2591 で落ちるため(typecheck は通ってしまうので気づけない)。
34
+ */
35
+ export declare const isDev: boolean;
36
+ /** theme が持つ配色の能力。`color.semantic` のキー集合から導出される(宣言ではない)。 */
37
+ export type ColorSchemeCapability = "light-dark" | "single-light" | "single-dark";
38
+ /**
39
+ * 解決済みの capability 集合。context 経由で全 component が読める。
40
+ * 後続で accent / elevation の軸が増えるのもこの器(それらはキー集合から導出できないので宣言になる)。
41
+ */
42
+ export interface ResolvedCapabilities {
43
+ colorScheme: ColorSchemeCapability;
44
+ }
45
+ /**
46
+ * `defineTheme()` の入力。`NativeTheme` との差は `color.semantic` が mode ごとに任意な点だけ。
47
+ * 持っていない mode は **書かない**(capability はキー集合から導出される)。
48
+ */
49
+ export interface ThemeOptions extends Omit<NativeTheme, "color"> {
50
+ /** 診断・エラーメッセージ用の識別子(任意)。preset compiler の成果物では preset 名を入れる。 */
51
+ readonly id?: string;
52
+ readonly version?: string;
53
+ color: Omit<ThemeColor, "semantic"> & {
54
+ semantic: Partial<Record<ThemeMode, SemanticColors>>;
55
+ };
56
+ }
57
+ /**
58
+ * `defineTheme()` の成果物。`ThemeProvider` の `theme` prop が受け取れる唯一の形。
59
+ * `$$melta` brand により生オブジェクトの直渡しを型で塞ぐ(= validation を必ず1回通す)。
60
+ */
61
+ export interface ResolvedNativeTheme extends ThemeOptions {
62
+ readonly $$melta: true;
63
+ readonly capabilities: ResolvedCapabilities;
64
+ }
65
+ /** 解決した mode が theme の能力と食い違ったこと。ThemeProvider は dev で報告し、値は clamp する。 */
66
+ export interface ThemeModeViolation {
67
+ kind: "forced-mode-unsupported";
68
+ /** 呼び出し側が forcedMode で明示した mode。 */
69
+ requested: ThemeMode;
70
+ /** 実際に使われる mode。 */
71
+ resolved: ThemeMode;
72
+ colorScheme: ColorSchemeCapability;
73
+ }
74
+ /** theme が実際に値を持っている mode(enumerable なキーのみ = dev getter を踏まない)。 */
75
+ export declare function declaredModes(semantic: Partial<Record<ThemeMode, SemanticColors>>): ThemeMode[];
76
+ /** colorScheme capability を `color.semantic` のキー集合から導出する。 */
77
+ export declare function deriveColorScheme(semantic: Partial<Record<ThemeMode, SemanticColors>>): ColorSchemeCapability;
78
+ /** capability が実際に描ける mode。 */
79
+ export declare function supportedModes(colorScheme: ColorSchemeCapability): ThemeMode[];
80
+ /**
81
+ * 表示 mode を解決する(純関数。ThemeProvider はこの結果に従うだけで判定はしない)。
82
+ *
83
+ * - **OS 由来の不一致は無反応で clamp する**。「light テーマを作らない」は消費者の意図的な設計判断で、
84
+ * OS が light なのは環境の事実にすぎない。ここで警告を出すと light 設定のユーザ全員に出てしまう
85
+ * (CSS の `color-scheme` / Expo の `userInterfaceStyle` も黙って clamp する)。
86
+ * - **`forcedMode` との不一致は報告する**。宣言同士の矛盾=呼び出し側の間違いなので violation を返す。
87
+ * ただし throw はしない(clamp して描き続ける)。厳格に落としたいテストや compiler が
88
+ * violation を error に昇格させる側の責務にする。
89
+ */
90
+ export declare function resolveMode(colorScheme: ColorSchemeCapability, forcedMode: ThemeMode | undefined, systemMode: ThemeMode): {
91
+ mode: ThemeMode;
92
+ violation?: ThemeModeViolation;
93
+ };
94
+ /**
95
+ * theme の形を検査して問題の一覧を返す(空配列 = 問題なし)。
96
+ *
97
+ * 「宣言した mode の色が欠けている」「as キャストでキーが抜けた」といった、型を通り抜けた欠落を拾う。
98
+ * `defineTheme()` が dev で1回だけ呼ぶ。**render 中に呼ばないこと**(theme-ui は Provider の
99
+ * render body で毎レンダー全色を再帰走査して性能を落とした前例がある)。
100
+ */
101
+ export declare function validateTheme(options: ThemeOptions): string[];
102
+ /**
103
+ * ブランド theme を組み立てる。`ThemeProvider` の `theme` prop に渡せるのはこの戻り値だけ。
104
+ *
105
+ * **module スコープで1回だけ呼ぶこと**(render の中で呼ぶと毎レンダー新しい参照になり、
106
+ * context の consumer が全部再レンダーする)。styled-components / Emotion が docs で
107
+ * 同じことを求めているのと同じ理由。
108
+ *
109
+ * dev では validateTheme を1回通し、問題があれば throw する(構造が壊れた theme は
110
+ * 後段で必ず事故になるので早く落とす)。production では検査ごと省く。
111
+ */
112
+ export declare function defineTheme(options: ThemeOptions): ResolvedNativeTheme;
113
+ //# sourceMappingURL=define-theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-theme.d.ts","sourceRoot":"","sources":["../../../../src/theme/define-theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAIV,WAAW,EAGX,cAAc,EAGd,UAAU,EACV,SAAS,EACV,MAAM,SAAS,CAAC;AAMjB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,EAAE,OAID,CAAC;AAMpB,4DAA4D;AAC5D,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,cAAc,GAAG,aAAa,CAAC;AAElF;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,qBAAqB,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9D,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG;QACpC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;KACtD,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;CAC7C;AAED,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,yBAAyB,CAAC;IAChC,oCAAoC;IACpC,SAAS,EAAE,SAAS,CAAC;IACrB,oBAAoB;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,EAAE,qBAAqB,CAAC;CACpC;AA0GD,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,GAAG,SAAS,EAAE,CAE/F;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,GACnD,qBAAqB,CAQvB;AAED,+BAA+B;AAC/B,wBAAgB,cAAc,CAAC,WAAW,EAAE,qBAAqB,GAAG,SAAS,EAAE,CAI9E;AAMD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,qBAAqB,EAClC,UAAU,EAAE,SAAS,GAAG,SAAS,EACjC,UAAU,EAAE,SAAS,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,kBAAkB,CAAA;CAAE,CAYrD;AAWD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,EAAE,CAwD7D;AAoDD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,mBAAmB,CAyBtE"}
@@ -1,11 +1,14 @@
1
1
  /**
2
2
  * melta-app theme エントリ。
3
3
  * 型は types.ts、値は native-theme.ts(melta-contracts から自動生成)。
4
+ * 消費者ブランドの theme を注入する口は define-theme.ts(`defineTheme`)。
4
5
  */
5
6
  export * from "./types";
6
7
  export { nativeTheme } from "./native-theme";
7
8
  export { ThemeProvider, useTheme } from "./ThemeProvider";
8
9
  export type { ThemeContextValue, ThemeMode } from "./ThemeProvider";
10
+ export { defineTheme, validateTheme, resolveMode, supportedModes, deriveColorScheme, declaredModes, } from "./define-theme";
11
+ export type { ColorSchemeCapability, ResolvedCapabilities, ResolvedNativeTheme, ThemeModeViolation, ThemeOptions, } from "./define-theme";
9
12
  /**
10
13
  * letterSpacingRatio(em 相当の比率)を、適用先の fontSize から RN の letterSpacing(pt) に解決する。
11
14
  * 例: resolveLetterSpacing(18, theme.typography.letterSpacingRatio.body)
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EACL,WAAW,EACX,aAAa,EACb,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5E"}
@@ -6,6 +6,11 @@
6
6
  * web 形式 → RN 形式の正規化規則は requirements-melta-app.md §4 を参照。
7
7
  */
8
8
  import type { ViewStyle } from "react-native";
9
+ /**
10
+ * 表示モード。ThemeProvider が OS の colorScheme / forcedMode / theme の能力から解決する。
11
+ * (define-theme.ts と ThemeProvider.tsx の相互 import を避けるため型の実体はここに置く)
12
+ */
13
+ export type ThemeMode = "light" | "dark";
9
14
  /** primary パレット(50〜950)。値は hex 文字列。 */
10
15
  export type PrimaryScale = "50" | "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900" | "950";
11
16
  /** semantic カラー(light / dark で同じキー集合を持つ)。値は色文字列。 */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/theme/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/theme/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAEzC,uCAAuC;AACvC,MAAM,MAAM,YAAY,GACpB,IAAI,GACJ,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,CAAC;AAEV,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QAAE,KAAK,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,cAAc,CAAA;KAAE,CAAC;IAC1D,MAAM,EAAE;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,OAAO,EAAE,YAAY,CAAC;QAAC,MAAM,EAAE,YAAY,CAAA;KAAE,CAAC;CAChF;AAED,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC;AACrF,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;AACtE,uDAAuD;AACvD,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,UAAU,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IACnD;;;;;;OAMG;IACH,kBAAkB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACvD;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,SAAS,EACT,aAAa,GAAG,cAAc,GAAG,eAAe,GAAG,cAAc,GAAG,WAAW,CAChF,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;AAE5D,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;AAE5D,MAAM,MAAM,UAAU,GAClB,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,CAAC;AAET,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,YAAY;IACZ,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,sDAAsD;IACtD,MAAM,EAAE;QACN,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1C,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;KACvC,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,UAAU,CAAC;IAClB,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAChD,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "melta-app",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "melta for APP — melta デザインシステムの React Native UI kit。melta-contracts(デザイン契約)を single source of truth に RN 実装でその契約を満たす。アプリ本体ではなくライブラリ。",
5
5
  "license": "MIT",
6
6
  "homepage": "https://app.melta.tsubotax.com",
@@ -6,47 +6,109 @@
6
6
  * useColorScheme() を Provider 内で1回だけ読む(RN コア、追加依存なし)。
7
7
  *
8
8
  * context value は useMemo 必須(§3): 毎レンダで新オブジェクトを配ると全 consumer が再レンダする。
9
+ *
10
+ * theme 注入(Step 2-①): `theme` を渡すと消費者ブランドの theme で塗り替わる。未指定なら
11
+ * 従来どおり codegen 済みの `nativeTheme`(=既存消費者に破壊的変更なし)。渡せるのは
12
+ * `defineTheme()` の戻り値だけ(define-theme.ts 参照)。
9
13
  */
10
14
 
11
15
  import { createContext, useContext, useMemo, type ReactNode } from "react";
12
16
  import { useColorScheme } from "react-native";
13
17
  import { nativeTheme } from "./native-theme";
14
- import type { NativeTheme, SemanticColors } from "./types";
18
+ import { defineTheme, isDev, resolveMode, type ResolvedCapabilities, type ResolvedNativeTheme, type ThemeModeViolation } from "./define-theme";
19
+ import type { NativeTheme, SemanticColors, ThemeMode } from "./types";
15
20
 
16
- export type ThemeMode = "light" | "dark";
21
+ export type { ThemeMode };
17
22
 
18
23
  export interface ThemeContextValue {
19
- /** 正規化済み theme 全体(色以外の形状トークンもここから取る)。 */
24
+ /**
25
+ * 正規化済み theme 全体(色以外の形状トークンもここから取る)。
26
+ *
27
+ * 注: 単一 colorScheme の theme を注入した場合、`color.semantic` は**宣言された mode しか持たない**
28
+ * (型は両方あるように見える)。持たない側を直接読むと原因を名指しするエラーで落ちる。
29
+ * 現在 mode の色は `colors` を使うこと。
30
+ */
20
31
  theme: NativeTheme;
21
32
  /** 現在の表示モード。 */
22
33
  mode: ThemeMode;
23
34
  /** 現在 mode の semantic 色(= theme.color.semantic[mode])。最頻アクセスなので展開済みで配る。 */
24
35
  colors: SemanticColors;
36
+ /** theme が持つ能力(`color.semantic` のキー集合などから導出済み)。 */
37
+ capabilities: ResolvedCapabilities;
25
38
  }
26
39
 
27
40
  const ThemeContext = createContext<ThemeContextValue | null>(null);
28
41
 
42
+ /**
43
+ * 既定 theme。melta-contracts 由来の codegen 結果を注入経路と同じ形に通す(自分でも dogfood する)。
44
+ * 副作用として dev では `nativeTheme` の入れ子オブジェクト(トークン群)も freeze される
45
+ * — 生成物のトークンを実行時に書き換える経路は元々無いので、意図した副作用として許容する。
46
+ */
47
+ const DEFAULT_THEME: ResolvedNativeTheme = defineTheme({ ...nativeTheme, id: "melta" });
48
+
49
+ /** 同じ矛盾を毎レンダー報告しないためのラッチ(StrictMode の二重 render も1回に畳む)。 */
50
+ const reportedViolations = new Set<string>();
51
+
52
+ function reportViolation(themeId: string | undefined, violation: ThemeModeViolation): void {
53
+ const key = `${themeId ?? "(id 未設定)"}:${violation.requested}`;
54
+ if (reportedViolations.has(key)) return;
55
+ reportedViolations.add(key);
56
+ console.error(
57
+ `melta: forcedMode="${violation.requested}" が指定されたが、この theme は ` +
58
+ `colorScheme=${violation.colorScheme} なので "${violation.resolved}" で描画する。` +
59
+ `useTheme().capabilities.colorScheme を見て、対応していない mode は UI 側で出さないこと。`,
60
+ );
61
+ }
62
+
29
63
  interface ThemeProviderProps {
30
64
  children: ReactNode;
31
65
  /**
32
66
  * 明示モード。指定時は OS の colorScheme を無視して固定する。
33
67
  * カタログの light/dark トグル(§6)や、特定画面の固定表示に使う。
68
+ *
69
+ * theme が対応していない mode を渡した場合は theme 側の mode に clamp する(描画は止めない)。
70
+ * dev ではその矛盾を console.error で1回だけ報告する。
34
71
  */
35
72
  forcedMode?: ThemeMode;
73
+ /**
74
+ * 消費者ブランドの theme。未指定なら melta 既定(`nativeTheme`)。
75
+ * `defineTheme()` の戻り値を **module スコープで保持したもの**を渡すこと
76
+ * (render 中に組み立てると毎レンダー参照が変わり、全 consumer が再レンダする)。
77
+ */
78
+ theme?: ResolvedNativeTheme;
36
79
  }
37
80
 
38
- export function ThemeProvider({ children, forcedMode }: ThemeProviderProps) {
81
+ export function ThemeProvider({ children, forcedMode, theme }: ThemeProviderProps) {
39
82
  const system = useColorScheme();
40
- const mode: ThemeMode = forcedMode ?? (system === "dark" ? "dark" : "light");
83
+ const activeTheme = theme ?? DEFAULT_THEME;
41
84
 
42
- const value = useMemo<ThemeContextValue>(
43
- () => ({
44
- theme: nativeTheme,
45
- mode,
46
- colors: nativeTheme.color.semantic[mode],
47
- }),
48
- [mode],
85
+ const { mode, violation } = resolveMode(
86
+ activeTheme.capabilities.colorScheme,
87
+ forcedMode,
88
+ system === "dark" ? "dark" : "light",
49
89
  );
90
+ if (isDev && violation) reportViolation(activeTheme.id, violation);
91
+
92
+ const value = useMemo<ThemeContextValue>(() => {
93
+ const colors = activeTheme.color.semantic[mode];
94
+ if (!colors) {
95
+ // resolveMode が capability に従って選んだ mode なので、ここに来るのは theme が壊れている場合だけ。
96
+ // 色が undefined のまま下流に流れると全 component が別々の場所で死ぬので、ここで落とす。
97
+ throw new Error(
98
+ `melta: theme "${activeTheme.id ?? "(id 未設定)"}" に mode="${mode}" の semantic 色が無い。`,
99
+ );
100
+ }
101
+ return {
102
+ // 配布時の型は NativeTheme のまま(既存の全 style resolver の署名を維持するため)。
103
+ // 単一 colorScheme の theme では持たない mode のキーが実体に無く、型が実体より広い。
104
+ // 到達しうる読みは resolveMode が保証した現在 mode だけで、他 mode を直接読んだ場合は
105
+ // define-theme.ts の guard getter が原因を名指しして落とす。
106
+ theme: activeTheme as unknown as NativeTheme,
107
+ mode,
108
+ colors,
109
+ capabilities: activeTheme.capabilities,
110
+ };
111
+ }, [activeTheme, mode]);
50
112
 
51
113
  return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
52
114
  }