@moewolf/moe-icons-cli 0.0.1 → 0.0.2

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.
Files changed (151) hide show
  1. package/README.md +243 -9
  2. package/bin/check-node.cjs +36 -0
  3. package/bin/moeicons.js +12 -1
  4. package/dist/api/client.d.ts +13 -2
  5. package/dist/api/client.js +145 -21
  6. package/dist/auth/device-login.d.ts +10 -7
  7. package/dist/auth/device-login.js +60 -37
  8. package/dist/auth/login-schemas.d.ts +24 -0
  9. package/dist/auth/login-schemas.js +67 -0
  10. package/dist/catalog/catalog.d.ts +11 -0
  11. package/dist/catalog/catalog.js +43 -0
  12. package/dist/catalog/catalog.json +538 -3395
  13. package/dist/cli.d.ts +11 -2
  14. package/dist/cli.js +773 -147
  15. package/dist/commands/parser.d.ts +13 -2
  16. package/dist/commands/parser.js +138 -50
  17. package/dist/config-package/generated/SOURCE.json +25 -0
  18. package/dist/config-package/generated/moeicons-config.schema.json +191 -0
  19. package/dist/config-package/generated/moeicons.config.jsonc +42 -0
  20. package/dist/config-package/generated/render-config.cjs +145 -0
  21. package/dist/config-package/generated/validate-config.cjs +293 -0
  22. package/dist/config-package/generated-config.d.ts +32 -0
  23. package/dist/config-package/generated-config.js +19 -0
  24. package/dist/core/auth.d.ts +24 -0
  25. package/dist/core/auth.js +127 -8
  26. package/dist/core/bitmap-assets.d.ts +19 -5
  27. package/dist/core/bitmap-assets.js +40 -31
  28. package/dist/core/bitmap-shard-budget.d.ts +43 -0
  29. package/dist/core/bitmap-shard-budget.js +16 -0
  30. package/dist/core/bitmap-shard-cache.d.ts +68 -0
  31. package/dist/core/bitmap-shard-cache.js +115 -0
  32. package/dist/core/bitmap-shard-download.d.ts +130 -0
  33. package/dist/core/bitmap-shard-download.js +359 -0
  34. package/dist/core/bitmap-shard-resolver.d.ts +129 -0
  35. package/dist/core/bitmap-shard-resolver.js +262 -0
  36. package/dist/core/bitmap-shards.d.ts +70 -0
  37. package/dist/core/bitmap-shards.js +249 -0
  38. package/dist/core/bootstrap.d.ts +37 -0
  39. package/dist/core/bootstrap.js +54 -0
  40. package/dist/core/cache.d.ts +34 -0
  41. package/dist/core/cache.js +96 -0
  42. package/dist/core/context.d.ts +26 -0
  43. package/dist/core/doctor.d.ts +48 -0
  44. package/dist/core/doctor.js +190 -0
  45. package/dist/core/free-download.d.ts +64 -6
  46. package/dist/core/free-download.js +402 -30
  47. package/dist/core/generate.d.ts +29 -0
  48. package/dist/core/generate.js +341 -65
  49. package/dist/core/icon-selection.d.ts +8 -0
  50. package/dist/core/icon-selection.js +19 -0
  51. package/dist/core/install.d.ts +14 -0
  52. package/dist/core/install.js +132 -13
  53. package/dist/core/library-update.d.ts +5 -1
  54. package/dist/core/library-update.js +176 -17
  55. package/dist/core/local-test-env.d.ts +24 -0
  56. package/dist/core/local-test-env.js +43 -0
  57. package/dist/core/metadata-sync.d.ts +30 -0
  58. package/dist/core/metadata-sync.js +97 -0
  59. package/dist/core/pro-download.d.ts +63 -0
  60. package/dist/core/pro-download.js +247 -27
  61. package/dist/core/pro-install.d.ts +11 -3
  62. package/dist/core/pro-install.js +181 -19
  63. package/dist/core/pro-resources.d.ts +79 -0
  64. package/dist/core/pro-resources.js +204 -0
  65. package/dist/core/pro-selected-download.d.ts +18 -0
  66. package/dist/core/pro-selected-download.js +269 -0
  67. package/dist/core/release-descriptor.d.ts +42 -1
  68. package/dist/core/release-descriptor.js +117 -9
  69. package/dist/core/resource-variant.d.ts +27 -10
  70. package/dist/core/resource-variant.js +75 -34
  71. package/dist/core/selected-resources.d.ts +59 -0
  72. package/dist/core/selected-resources.js +350 -0
  73. package/dist/core/signed-artifact.d.ts +8 -1
  74. package/dist/core/signed-artifact.js +35 -11
  75. package/dist/core/target-subtree.d.ts +8 -0
  76. package/dist/core/target-subtree.js +54 -3
  77. package/dist/core/version-service.d.ts +11 -0
  78. package/dist/core/version-service.js +129 -24
  79. package/dist/core/wizard.d.ts +20 -17
  80. package/dist/core/wizard.js +66 -28
  81. package/dist/errors/errors.d.ts +2 -1
  82. package/dist/errors/errors.js +1 -0
  83. package/dist/generator/generate.d.ts +1 -1
  84. package/dist/generator/generate.js +420 -213
  85. package/dist/generator/shared/SOURCE.json +7 -0
  86. package/dist/generator/shared/svg-model.cjs +156 -0
  87. package/dist/generator/svg-model.d.ts +16 -0
  88. package/dist/generator/svg-model.js +4 -0
  89. package/dist/generator/theme-resolve.d.ts +2 -0
  90. package/dist/generator/theme-resolve.js +57 -26
  91. package/dist/mcp/server.d.ts +23 -36
  92. package/dist/mcp/server.js +74 -152
  93. package/dist/mcp/services.d.ts +4 -0
  94. package/dist/mcp/services.js +230 -0
  95. package/dist/metadata/archive.d.ts +38 -0
  96. package/dist/metadata/archive.js +94 -0
  97. package/dist/metadata/manifest.d.ts +33 -0
  98. package/dist/metadata/manifest.js +136 -0
  99. package/dist/metadata/version.d.ts +22 -0
  100. package/dist/metadata/version.js +53 -0
  101. package/dist/project/anchors/application.d.ts +13 -0
  102. package/dist/project/anchors/application.js +377 -0
  103. package/dist/project/anchors/apply.d.ts +28 -0
  104. package/dist/project/anchors/apply.js +65 -0
  105. package/dist/project/anchors/diagnose.d.ts +26 -0
  106. package/dist/project/anchors/diagnose.js +90 -0
  107. package/dist/project/anchors/helpers.d.ts +20 -0
  108. package/dist/project/anchors/helpers.js +55 -0
  109. package/dist/project/anchors/moeicons-config.d.ts +21 -0
  110. package/dist/project/anchors/moeicons-config.js +83 -0
  111. package/dist/project/anchors/parse.d.ts +14 -0
  112. package/dist/project/anchors/parse.js +36 -0
  113. package/dist/project/anchors/project-manifest.d.ts +19 -0
  114. package/dist/project/anchors/project-manifest.js +109 -0
  115. package/dist/project/anchors/style.d.ts +12 -0
  116. package/dist/project/anchors/style.js +87 -0
  117. package/dist/project/anchors/types.d.ts +32 -0
  118. package/dist/project/anchors/types.js +9 -0
  119. package/dist/project/config.d.ts +59 -5
  120. package/dist/project/config.js +234 -102
  121. package/dist/project/install-metadata.d.ts +23 -2
  122. package/dist/project/install-metadata.js +58 -7
  123. package/dist/project/install.d.ts +24 -4
  124. package/dist/project/install.js +402 -190
  125. package/dist/project/project-lock.d.ts +2 -2
  126. package/dist/project/project-lock.js +4 -4
  127. package/dist/project/tailwind.d.ts +2 -2
  128. package/dist/project/tailwind.js +3 -4
  129. package/dist/project/tar-gz.d.ts +18 -1
  130. package/dist/project/tar-gz.js +40 -3
  131. package/dist/tui/components.d.ts +62 -0
  132. package/dist/tui/components.js +100 -0
  133. package/dist/tui/primitives.d.ts +1 -0
  134. package/dist/tui/primitives.js +8 -1
  135. package/dist/ui/banner.d.ts +31 -3
  136. package/dist/ui/banner.js +120 -25
  137. package/dist/ui/branded-prompts.d.ts +41 -0
  138. package/dist/ui/branded-prompts.js +80 -0
  139. package/dist/ui/clack.d.ts +6 -3
  140. package/dist/ui/clack.js +56 -5
  141. package/dist/ui/create-ui.d.ts +1 -0
  142. package/dist/ui/create-ui.js +5 -1
  143. package/dist/ui/generated/wordmark-large.d.ts +17 -0
  144. package/dist/ui/generated/wordmark-large.js +28 -0
  145. package/dist/ui/generated/wordmark.d.ts +2 -0
  146. package/dist/ui/generated/wordmark.js +2 -0
  147. package/dist/ui/non-interactive.js +8 -0
  148. package/dist/ui/stream.js +30 -0
  149. package/dist/ui/theme.d.ts +42 -0
  150. package/dist/ui/theme.js +59 -0
  151. package/package.json +14 -6
@@ -12,10 +12,20 @@ export type Command = {
12
12
  readonly name: "groups";
13
13
  } | {
14
14
  readonly name: "generate";
15
+ } | {
16
+ readonly name: "recover";
15
17
  } | {
16
18
  readonly name: "init";
19
+ readonly dryRun?: boolean;
20
+ } | {
21
+ readonly name: "doctor";
22
+ readonly check?: boolean;
23
+ readonly dryRun?: boolean;
17
24
  } | {
18
25
  readonly name: "mcp";
26
+ } | {
27
+ readonly name: "update";
28
+ readonly metadata?: boolean;
19
29
  } | {
20
30
  readonly name: "help";
21
31
  } | {
@@ -34,8 +44,9 @@ export interface ParseResult {
34
44
  }
35
45
  export type Target = "react" | "vue" | "vanilla" | "assets";
36
46
  /**
37
- * Parse argv into a Command via Commander. Never calls `process.exit()`.
47
+ * Parse argv into a Command. Never calls `process.exit()`.
38
48
  * `moeicons --pro` / `--ent` legacy aliases map to install for the compatibility window.
49
+ * Unknown commands and unknown options map to a stable VALIDATION_ERROR.
39
50
  */
40
51
  export declare function parseArgs(argv: readonly string[]): ParseResult;
41
- export declare const HELP_TEXT = "moeicons \u2014 Moeicons icon library CLI\n\nUsage:\n moeicons interactive guided flow (free / pro / login)\n moeicons install [group] install an icon group\n moeicons login browser login (PKCE)\n moeicons logout clear local session\n moeicons account show account/tier info\n moeicons groups list available icon groups\n moeicons generate generate React/Vue proxy components\n moeicons init create moeicons.config.json\n moeicons mcp start the MCP stdio server\n moeicons --version show version\n moeicons --help show help\n\nOptions:\n --json machine-readable JSON output\n --yes skip confirmations in noninteractive mode\n --target output target: react, vue, vanilla, or assets\n --no-tailwind skip Tailwind config auto-integration\n";
52
+ export declare const HELP_TEXT = "moeicons \u2014 Moeicons icon library CLI\n\nUsage:\n moeicons interactive guided flow (free / pro / login)\n moeicons install [group] install an icon group\n moeicons login browser login (PKCE)\n moeicons logout clear local session\n moeicons account show account/tier info\n moeicons groups list available icon groups\n moeicons generate generate React/Vue proxy components\n moeicons init create moeicons.config.jsonc\n moeicons init --dry-run print the full config/entry/style plan without writing\n moeicons doctor diagnose project anchors (read-only)\n moeicons recover recover interrupted writes (run at project root)\n moeicons doctor --check exit non-zero when any required anchor is not OK\n moeicons update metadata sync MANUAL.md/catalog.json/manifest.json only\n moeicons update full code + metadata update\n moeicons mcp start the MCP stdio server\n moeicons --version show version\n moeicons --help show help\n\nOptions:\n --json machine-readable JSON output\n --yes skip confirmations in noninteractive mode\n --target output target: react, vue, vanilla, or assets\n --no-tailwind skip Tailwind config auto-integration\n --dry-run (init/doctor) plan only, never write\n --check (doctor) exit 1 when required anchors are not OK\n";
@@ -6,10 +6,59 @@ const SIMPLE_COMMANDS = [
6
6
  "account",
7
7
  "groups",
8
8
  "generate",
9
- "init",
9
+ "recover",
10
10
  "mcp",
11
11
  "help",
12
12
  ];
13
+ /**
14
+ * Options accepted on every command. `--dry-run`/`--check` are additionally
15
+ * accepted on `init` / `doctor` (validated in parseArgs). Unknown options never
16
+ * pass silently; they produce a stable VALIDATION_ERROR.
17
+ */
18
+ const GLOBAL_OPTIONS = new Set([
19
+ "--json",
20
+ "--yes",
21
+ "--no-tailwind",
22
+ "--pro",
23
+ "--ent",
24
+ "--help",
25
+ "-h",
26
+ "--version",
27
+ "-v",
28
+ "--showVersion",
29
+ ]);
30
+ function commandName(argv) {
31
+ for (const arg of argv) {
32
+ if (!arg.startsWith("-"))
33
+ return arg;
34
+ }
35
+ return undefined;
36
+ }
37
+ /** Validate that no option is unknown for the given command. */
38
+ function rejectUnknownOptions(argv, command) {
39
+ const allowLocal = command === "init" || command === "doctor";
40
+ const local = new Set(["--dry-run", "--check"]);
41
+ for (const arg of argv) {
42
+ if (!arg.startsWith("-"))
43
+ continue;
44
+ const name = arg.split("=")[0] ?? arg;
45
+ if (arg.startsWith("--target"))
46
+ continue; // value validated separately
47
+ if (GLOBAL_OPTIONS.has(name))
48
+ continue;
49
+ if (allowLocal && local.has(name))
50
+ continue;
51
+ throw new CliError("VALIDATION_ERROR", `unknown option: ${name}`);
52
+ }
53
+ }
54
+ function parseTarget(argv) {
55
+ const targetIndex = argv.indexOf("--target");
56
+ const targetValue = targetIndex >= 0 ? argv[targetIndex + 1] : undefined;
57
+ if (targetIndex >= 0 && !["react", "vue", "vanilla", "assets"].includes(targetValue ?? "")) {
58
+ throw new CliError("VALIDATION_ERROR", `unknown target: ${targetValue ?? ""}`);
59
+ }
60
+ return targetValue;
61
+ }
13
62
  function addSharedOptions(command) {
14
63
  return command
15
64
  .allowUnknownOption(true)
@@ -24,46 +73,11 @@ function addSharedOptions(command) {
24
73
  .option("--pro")
25
74
  .option("--ent");
26
75
  }
27
- function createProgram(onSelect) {
28
- const program = addSharedOptions(new Commander());
29
- program
30
- .name("moeicons")
31
- .exitOverride()
32
- .addHelpCommand(false)
33
- .showHelpAfterError(false)
34
- .enablePositionalOptions()
35
- .configureOutput({
36
- writeOut: () => undefined,
37
- writeErr: () => undefined,
38
- })
39
- .action(() => {
40
- onSelect({ name: "wizard" });
41
- });
42
- addSharedOptions(program.command("install").argument("[group]")).action((group) => {
43
- onSelect(group ? { name: "install", group } : { name: "install" });
44
- });
45
- for (const name of SIMPLE_COMMANDS) {
46
- addSharedOptions(program.command(name)).action(() => {
47
- onSelect({ name });
48
- });
49
- }
50
- return program;
51
- }
52
- function toCliError(error) {
53
- if (error instanceof CommanderError && error.code === "commander.unknownCommand") {
54
- const match = /unknown command ['`]?([^'`\s]+)/.exec(error.message);
55
- throw new CliError("VALIDATION_ERROR", `unknown command: ${match?.[1] ?? "unknown"}`);
56
- }
57
- if (error instanceof CommanderError &&
58
- (error.code === "commander.help" || error.code === "commander.helpDisplayed")) {
59
- throw new CliError("VALIDATION_ERROR", error.message);
60
- }
61
- throw error;
62
- }
63
- const KNOWN_COMMANDS = new Set(["install", ...SIMPLE_COMMANDS]);
76
+ const KNOWN_COMMANDS = new Set(["install", "update", "init", "doctor", ...SIMPLE_COMMANDS]);
64
77
  /**
65
- * Parse argv into a Command via Commander. Never calls `process.exit()`.
78
+ * Parse argv into a Command. Never calls `process.exit()`.
66
79
  * `moeicons --pro` / `--ent` legacy aliases map to install for the compatibility window.
80
+ * Unknown commands and unknown options map to a stable VALIDATION_ERROR.
67
81
  */
68
82
  export function parseArgs(argv) {
69
83
  const json = argv.includes("--json");
@@ -73,12 +87,18 @@ export function parseArgs(argv) {
73
87
  const help = argv.includes("--help") || argv.includes("-h");
74
88
  const pro = argv.includes("--pro");
75
89
  const ent = argv.includes("--ent");
76
- const targetIndex = argv.indexOf("--target");
77
- const targetValue = targetIndex >= 0 ? argv[targetIndex + 1] : undefined;
78
- if (targetIndex >= 0 && !["react", "vue", "vanilla", "assets"].includes(targetValue ?? "")) {
79
- throw new CliError("VALIDATION_ERROR", `unknown target: ${targetValue ?? ""}`);
90
+ const target = parseTarget(argv);
91
+ const first = commandName(argv);
92
+ if (first !== undefined && !KNOWN_COMMANDS.has(first)) {
93
+ rejectUnknownOptions(argv, first);
94
+ throw new CliError("VALIDATION_ERROR", `unknown command: ${first}`);
95
+ }
96
+ if (first === "init" || first === "doctor") {
97
+ rejectUnknownOptions(argv, first);
98
+ }
99
+ else {
100
+ rejectUnknownOptions(argv);
80
101
  }
81
- const target = targetValue;
82
102
  const withTarget = (command) => target ? { ...command, target } : command;
83
103
  // Preserve previous flag precedence: version/help/legacy aliases beat subcommands.
84
104
  if (version)
@@ -101,16 +121,33 @@ export function parseArgs(argv) {
101
121
  noTailwind,
102
122
  ...(target ? { target } : {}),
103
123
  };
104
- const positional = argv.filter((arg) => !arg.startsWith("-"));
105
- const first = positional[0];
106
- if (first !== undefined && !KNOWN_COMMANDS.has(first)) {
107
- throw new CliError("VALIDATION_ERROR", `unknown command: ${first}`);
124
+ if (first === "init") {
125
+ return {
126
+ command: withTarget({ name: "init", ...(argv.includes("--dry-run") ? { dryRun: true } : {}) }),
127
+ json,
128
+ yes,
129
+ noTailwind,
130
+ ...(target ? { target } : {}),
131
+ };
132
+ }
133
+ if (first === "doctor") {
134
+ return {
135
+ command: {
136
+ name: "doctor",
137
+ ...(argv.includes("--check") ? { check: true } : {}),
138
+ ...(argv.includes("--dry-run") ? { dryRun: true } : {}),
139
+ },
140
+ json,
141
+ yes,
142
+ noTailwind,
143
+ ...(target ? { target } : {}),
144
+ };
108
145
  }
109
146
  let selected;
110
147
  try {
111
148
  createProgram((command) => {
112
149
  selected = withTarget(command);
113
- }).parse([...argv], { from: "user" });
150
+ }).parse(argv, { from: "user" });
114
151
  }
115
152
  catch (error) {
116
153
  toCliError(error);
@@ -123,6 +160,49 @@ export function parseArgs(argv) {
123
160
  ...(target ? { target } : {}),
124
161
  };
125
162
  }
163
+ function createProgram(onSelect) {
164
+ const program = addSharedOptions(new Commander());
165
+ program
166
+ .name("moeicons")
167
+ .exitOverride()
168
+ .addHelpCommand(false)
169
+ .showHelpAfterError(false)
170
+ .enablePositionalOptions()
171
+ .configureOutput({
172
+ writeOut: () => undefined,
173
+ writeErr: () => undefined,
174
+ })
175
+ .action(() => {
176
+ onSelect({ name: "wizard" });
177
+ });
178
+ addSharedOptions(program.command("install").argument("[group]")).action((group) => {
179
+ onSelect(group ? { name: "install", group } : { name: "install" });
180
+ });
181
+ addSharedOptions(program.command("update").argument("[sub]")).action((sub) => {
182
+ onSelect({ name: "update", metadata: sub === "metadata" });
183
+ });
184
+ for (const name of SIMPLE_COMMANDS) {
185
+ addSharedOptions(program.command(name)).action(() => {
186
+ onSelect({ name });
187
+ });
188
+ }
189
+ return program;
190
+ }
191
+ function toCliError(error) {
192
+ if (error instanceof CommanderError && error.code === "commander.unknownCommand") {
193
+ const match = /unknown command ['`]?([^'`\s]+)/.exec(error.message);
194
+ throw new CliError("VALIDATION_ERROR", `unknown command: ${match?.[1] ?? "unknown"}`);
195
+ }
196
+ if (error instanceof CommanderError && error.code === "commander.unknownOption") {
197
+ const match = /unknown option ['`]?([^'`\s]+)/.exec(error.message);
198
+ throw new CliError("VALIDATION_ERROR", `unknown option: ${match?.[1] ?? "unknown"}`);
199
+ }
200
+ if (error instanceof CommanderError &&
201
+ (error.code === "commander.help" || error.code === "commander.helpDisplayed")) {
202
+ throw new CliError("VALIDATION_ERROR", error.message);
203
+ }
204
+ throw error;
205
+ }
126
206
  export const HELP_TEXT = `moeicons — Moeicons icon library CLI
127
207
 
128
208
  Usage:
@@ -133,7 +213,13 @@ Usage:
133
213
  moeicons account show account/tier info
134
214
  moeicons groups list available icon groups
135
215
  moeicons generate generate React/Vue proxy components
136
- moeicons init create moeicons.config.json
216
+ moeicons init create moeicons.config.jsonc
217
+ moeicons init --dry-run print the full config/entry/style plan without writing
218
+ moeicons doctor diagnose project anchors (read-only)
219
+ moeicons recover recover interrupted writes (run at project root)
220
+ moeicons doctor --check exit non-zero when any required anchor is not OK
221
+ moeicons update metadata sync MANUAL.md/catalog.json/manifest.json only
222
+ moeicons update full code + metadata update
137
223
  moeicons mcp start the MCP stdio server
138
224
  moeicons --version show version
139
225
  moeicons --help show help
@@ -143,4 +229,6 @@ Options:
143
229
  --yes skip confirmations in noninteractive mode
144
230
  --target output target: react, vue, vanilla, or assets
145
231
  --no-tailwind skip Tailwind config auto-integration
232
+ --dry-run (init/doctor) plan only, never write
233
+ --check (doctor) exit 1 when required anchors are not OK
146
234
  `;
@@ -0,0 +1,25 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "sourceRepo": "moewolf-dev/moe-icons-code-library",
4
+ "sourceDir": "config-package",
5
+ "sourceCommit": "76d9924b2114afc317d0a630cde536fb035a908c",
6
+ "sourceCommitExact": true,
7
+ "files": {
8
+ "render-config.cjs": {
9
+ "size": 6002,
10
+ "sha256": "690418924769912a2e5b572ceed96b30cfe77e19ca3eb40189dc183bc32056c3"
11
+ },
12
+ "validate-config.cjs": {
13
+ "size": 11112,
14
+ "sha256": "36c35b686335eab4e7db002afb631aa631c80f66656f137b1e2437317b126b0f"
15
+ },
16
+ "moeicons-config.schema.json": {
17
+ "size": 4769,
18
+ "sha256": "0e15c0cf7c282abad7d7399e4a52b7804dc8211b427490fca5efc6c53d9319df"
19
+ },
20
+ "moeicons.config.jsonc": {
21
+ "size": 1207,
22
+ "sha256": "260496b4dee0796e03b37dffd442dafeb406b3fdf15eb43c2927e75b497cb6ef"
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,191 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://moeicons.dev/schema/moeicons-config-v3.json",
4
+ "title": "Moeicons user-project configuration (schema v3)",
5
+ "description": "Canonical user-project configuration. Version 3 adds the optional `integration` object (application/style anchors confirmed by the CLI) on top of the schema v2 fields. The pixel-ladder field remains `imageSize` and is never called `size`.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "schemaVersion",
10
+ "tier",
11
+ "target",
12
+ "outputDir",
13
+ "defaultTheme",
14
+ "themes",
15
+ "icons"
16
+ ],
17
+ "properties": {
18
+ "schemaVersion": {
19
+ "const": 3
20
+ },
21
+ "tier": {
22
+ "enum": [
23
+ "free",
24
+ "pro"
25
+ ]
26
+ },
27
+ "target": {
28
+ "enum": [
29
+ "react",
30
+ "vue",
31
+ "vanilla",
32
+ "assets"
33
+ ]
34
+ },
35
+ "outputDir": {
36
+ "type": "string",
37
+ "minLength": 1,
38
+ "pattern": "^(?!/)(?![A-Za-z]:)(?!.*(?:/|^)\\.{1,2}(?:/|$))(?!(?:\\.git|\\.moeicons|node_modules)(?:/|$))[A-Za-z0-9_-]+(?:/[A-Za-z0-9_.-]+)*$"
39
+ },
40
+ "defaultTheme": {
41
+ "type": "string",
42
+ "minLength": 1
43
+ },
44
+ "missingIconPolicy": {
45
+ "enum": [
46
+ "fallback",
47
+ "error"
48
+ ]
49
+ },
50
+ "themes": {
51
+ "type": "object",
52
+ "propertyNames": {
53
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
54
+ },
55
+ "additionalProperties": {
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "required": [
59
+ "styleGroup"
60
+ ],
61
+ "properties": {
62
+ "icons": {
63
+ "description": "Optional subset of project icons selected for this theme. Omit to select all registered icons; an empty array selects none. Missing variants follow missingIconPolicy.",
64
+ "type": "array",
65
+ "uniqueItems": true,
66
+ "items": {
67
+ "type": "string"
68
+ }
69
+ },
70
+ "styleGroup": {
71
+ "type": "string",
72
+ "pattern": "^moe-[a-z0-9]+(?:-[a-z0-9]+)*$"
73
+ },
74
+ "styles": {
75
+ "type": "array",
76
+ "items": {
77
+ "type": "string"
78
+ }
79
+ },
80
+ "format": {
81
+ "enum": [
82
+ "svg",
83
+ "webp",
84
+ "png"
85
+ ]
86
+ },
87
+ "imageSize": {
88
+ "enum": [
89
+ 64,
90
+ 128,
91
+ 256,
92
+ 512
93
+ ]
94
+ },
95
+ "defaultSize": {
96
+ "type": "number",
97
+ "exclusiveMinimum": 0
98
+ },
99
+ "strokeWidth": {
100
+ "type": "number",
101
+ "minimum": 0
102
+ },
103
+ "className": {
104
+ "type": "string"
105
+ }
106
+ }
107
+ }
108
+ },
109
+ "icons": {
110
+ "description": "Either a flat string array or a prefix-grouped object. Grouped form is canonical; each key is a prefix and each value an array of icon IDs.",
111
+ "oneOf": [
112
+ {
113
+ "type": "array",
114
+ "items": {
115
+ "type": "string"
116
+ }
117
+ },
118
+ {
119
+ "type": "object",
120
+ "additionalProperties": {
121
+ "type": "array",
122
+ "items": {
123
+ "type": "string"
124
+ }
125
+ }
126
+ }
127
+ ]
128
+ },
129
+ "integration": {
130
+ "type": "object",
131
+ "additionalProperties": false,
132
+ "required": [
133
+ "adapter"
134
+ ],
135
+ "properties": {
136
+ "adapter": {
137
+ "enum": [
138
+ "vite-react",
139
+ "next-app",
140
+ "next-pages",
141
+ "vite-vue",
142
+ "nuxt",
143
+ "vanilla",
144
+ "assets-only"
145
+ ]
146
+ },
147
+ "entry": {
148
+ "type": "string",
149
+ "minLength": 1,
150
+ "pattern": "^(?!/)(?![A-Za-z]:)(?!.*\\\\)(?!.*\\u0000)(?!.*(?:^|/)\\.{1,2}(?:/|$))[^/]+(?:/[^/]+)*$"
151
+ },
152
+ "style": {
153
+ "type": "string",
154
+ "minLength": 1,
155
+ "pattern": "^(?!/)(?![A-Za-z]:)(?!.*\\\\)(?!.*\\u0000)(?!.*(?:^|/)\\.{1,2}(?:/|$))[^/]+(?:/[^/]+)*$"
156
+ }
157
+ },
158
+ "allOf": [
159
+ {
160
+ "if": {
161
+ "not": {
162
+ "properties": {
163
+ "adapter": {
164
+ "const": "assets-only"
165
+ }
166
+ },
167
+ "required": [
168
+ "adapter"
169
+ ]
170
+ }
171
+ },
172
+ "then": {
173
+ "required": [
174
+ "entry"
175
+ ]
176
+ }
177
+ }
178
+ ]
179
+ },
180
+ "downloadMode": {
181
+ "type": "string",
182
+ "enum": [
183
+ "auto",
184
+ "icons",
185
+ "full"
186
+ ],
187
+ "default": "auto",
188
+ "description": "Prefer configuration-selected resources (auto), require them (icons), or download the full archive (full)."
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "schemaVersion": 3,
3
+ "tier": "free",
4
+ "target": "react",
5
+ "outputDir": "src/moeicons",
6
+ // auto: indexed resources when available; icons: require them; full: whole archive.
7
+ "downloadMode": "auto",
8
+ // Global icons registers IDs; per-theme icons selects a subset (empty means none).
9
+ "defaultTheme": "outline",
10
+ "themes": {
11
+ "outline": {
12
+ "styleGroup": "moe-outline"
13
+ },
14
+ // "solid": { "styleGroup": "moe-solid" },
15
+ // Bitmap style groups are commented out and selected with format/imageSize:
16
+ // cute — bitmap style (moe-cute-3d)
17
+ // format options: webp, png
18
+ // imageSize options: 64, 128, 256, 512
19
+ // "cute": {
20
+ // "styleGroup": "moe-cute-3d",
21
+ // "format": "webp",
22
+ // "imageSize": 256
23
+ // },
24
+ // Set defaultTheme above to any enabled theme key.
25
+ },
26
+ // Add icon ids from .moeicons/catalog.json as needed.
27
+ "icons": {
28
+ "arrow": [
29
+ "arrow-bold-right"
30
+ ],
31
+ "ui": [
32
+ "ui-search"
33
+ ]
34
+ },
35
+ "missingIconPolicy": "fallback",
36
+ // integration is written only after the user confirms anchors:
37
+ // "integration": {
38
+ // "adapter": "vite-react",
39
+ // "entry": "src/main.tsx",
40
+ // "style": "src/index.css"
41
+ // }
42
+ }
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+
3
+ // Canonical renderer for the user-project `moeicons.config.jsonc`.
4
+ //
5
+ // Ownership: this module (moe-icons-code-library/config-package) is the single
6
+ // source of truth for the editable config skeleton. The CLI bundles a generated
7
+ // copy of this module plus a source digest and must never maintain a second
8
+ // full renderer.
9
+ //
10
+ // Behaviour contract:
11
+ // - `fullCatalog: true` retains the historical exhaustive renderer for users
12
+ // who explicitly request it. The default is a small, valid first-run config.
13
+ // - When `integration` is supplied (adapter/entry/style confirmed by the user),
14
+ // the renderer emits schemaVersion 3 and adds the `integration` block. It
15
+ // never invents integration paths on its own.
16
+ // - Deterministic output for identical inputs.
17
+
18
+ /**
19
+ * @param {object} options
20
+ * @param {"react"|"vue"|"vanilla"|"assets"} options.target
21
+ * @param {"react"|"vue"} [options.framework] legacy alias; resolves to target
22
+ * @param {"free"|"pro"} [options.tier]
23
+ * @param {object} [options.integration] optional {adapter, entry, style}
24
+ * @returns {string} JSONC skeleton text
25
+ */
26
+ function renderMoeiconsConfigJsonc(options) {
27
+ const ALLOWED = new Set(['target', 'framework', 'tier', 'catalog', 'integration', 'fullCatalog']);
28
+ for (const key of Object.keys(options)) {
29
+ if (!ALLOWED.has(key)) {
30
+ throw new Error(`unknown render option "${key}"`);
31
+ }
32
+ }
33
+ const tier = options.tier ?? 'free';
34
+ if (!options.catalog) {
35
+ throw new Error('catalog is required');
36
+ }
37
+ const target = options.target ?? options.framework ?? 'react';
38
+
39
+ const availableGroups = (options.catalog ? options.catalog.styleGroups : [])
40
+ .filter((g) => Array.isArray(g.tiers) && g.tiers.includes(tier))
41
+ .sort((a, b) => a.id.localeCompare(b.id));
42
+
43
+ const fullCatalog = options.fullCatalog === true;
44
+ const defaultGroup = availableGroups.find((group) => group.id === 'moe-outline') ?? availableGroups.find((group) => group.type !== 'bitmap');
45
+ if (!defaultGroup) throw new Error(`no SVG style group is available in ${tier} tier`);
46
+ const defaultThemeName = fullCatalog ? 'outline' : defaultGroup.id.replace(/^moe-/, '');
47
+
48
+ const themeLines = [];
49
+ for (const group of availableGroups) {
50
+ const themeName = group.id.replace(/^moe-/, '');
51
+ if (group.type === 'bitmap') {
52
+ const formatsComment = `// format options: ${group.formats.join(', ')}`;
53
+ const sizesComment = `// imageSize options: ${group.imageSizes.join(', ')}`;
54
+ themeLines.push(
55
+ ` // ${themeName} — bitmap style (${group.id})`,
56
+ ` // ${formatsComment}`,
57
+ ` // ${sizesComment}`,
58
+ ` // ${JSON.stringify(themeName)}: {`,
59
+ ` // "styleGroup": ${JSON.stringify(group.id)},`,
60
+ ` // "format": "webp",`,
61
+ ` // "imageSize": 256`,
62
+ ` // },`,
63
+ );
64
+ } else if (fullCatalog || group.id === defaultGroup.id) {
65
+ themeLines.push(
66
+ ` ${JSON.stringify(themeName)}: {`,
67
+ ` "styleGroup": ${JSON.stringify(group.id)}`,
68
+ ` },`,
69
+ );
70
+ } else {
71
+ themeLines.push(` // ${JSON.stringify(themeName)}: { "styleGroup": ${JSON.stringify(group.id)} },`);
72
+ }
73
+ }
74
+
75
+ const tierGroupIds = new Set((fullCatalog ? availableGroups : [defaultGroup]).map((g) => g.id));
76
+ const groups = new Map();
77
+ const eligible = (options.catalog ? options.catalog.icons : [])
78
+ .filter((icon) => icon.availableIn.some((sg) => tierGroupIds.has(sg)));
79
+ const examples = fullCatalog ? eligible : [
80
+ ...['arrow-bold-right', 'ui-search'].map((id) => eligible.find((icon) => icon.id === id)).filter(Boolean),
81
+ ...eligible,
82
+ ].filter((icon, index, values) => values.findIndex((item) => item.id === icon.id) === index).slice(0, 2);
83
+ for (const icon of examples) {
84
+ const ids = groups.get(icon.prefix) ?? [];
85
+ ids.push(icon.id);
86
+ groups.set(icon.prefix, ids);
87
+ }
88
+ const iconLines = [...groups.entries()]
89
+ .sort(([a], [b]) => a.localeCompare(b))
90
+ .flatMap(([prefix, ids]) => [
91
+ ` // ${prefix} icons`,
92
+ ` ${JSON.stringify(prefix)}: [`,
93
+ ...ids.map((id) => ` ${JSON.stringify(id)},`),
94
+ ' ],',
95
+ ]);
96
+
97
+ const schemaVersion = options.integration || !fullCatalog ? 3 : 2;
98
+ const integrationBlock = options.integration
99
+ ? [
100
+ '',
101
+ ' // User-confirmed application/style anchors (written only after',
102
+ ' // confirmation; never auto-guessed).',
103
+ ` "integration": {`,
104
+ ` "adapter": ${JSON.stringify(options.integration.adapter)},`,
105
+ ...(options.integration.entry !== undefined
106
+ ? [` "entry": ${JSON.stringify(options.integration.entry)},`]
107
+ : []),
108
+ ...(options.integration.style !== undefined
109
+ ? [` "style": ${JSON.stringify(options.integration.style)},`]
110
+ : []),
111
+ ' }',
112
+ ]
113
+ : [];
114
+
115
+ return [
116
+ '{',
117
+ ` "schemaVersion": ${schemaVersion},`,
118
+ ` "tier": ${JSON.stringify(tier)},`,
119
+ ` "target": ${JSON.stringify(target)},`,
120
+ ` "outputDir": "src/moeicons",`,
121
+ ...(!fullCatalog ? [
122
+ ' // auto: indexed resources when available; icons: require them; full: whole archive.',
123
+ ' "downloadMode": "auto",',
124
+ ' // Global icons registers IDs; per-theme icons selects a subset (empty means none).',
125
+ ] : []),
126
+ ` "defaultTheme": ${JSON.stringify(defaultThemeName)},`,
127
+ ' "themes": {',
128
+ ...themeLines,
129
+ ` // Set defaultTheme above to one of: ${availableGroups
130
+ .filter((g) => g.type !== 'bitmap')
131
+ .map((g) => JSON.stringify(g.id.replace(/^moe-/, '')))
132
+ .join(', ')}`,
133
+ ' },',
134
+ fullCatalog ? ' // Comment out individual IDs or a complete prefix group to exclude it.' : ' // Add more icon ids from .moeicons/catalog.json when needed.',
135
+ ' "icons": {',
136
+ ...iconLines,
137
+ ' },',
138
+ ` "missingIconPolicy": "fallback"${options.integration ? ',' : ''}`,
139
+ ...integrationBlock,
140
+ '}',
141
+ '',
142
+ ].join('\n');
143
+ }
144
+
145
+ module.exports = { renderMoeiconsConfigJsonc };