intor-cli 0.0.15 → 0.0.17

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 (121) hide show
  1. package/README.md +24 -27
  2. package/package.json +2 -4
  3. package/src/cli/commands/check.ts +23 -17
  4. package/src/cli/commands/discover.ts +32 -0
  5. package/src/cli/commands/generate.ts +35 -40
  6. package/src/cli/commands/index.ts +4 -0
  7. package/src/cli/commands/options/index.ts +1 -0
  8. package/src/cli/commands/options/options.ts +55 -0
  9. package/src/cli/commands/utils/normalize-message-files.ts +49 -0
  10. package/src/cli/commands/utils/normalize-reader-options.ts +15 -28
  11. package/src/cli/commands/validate.ts +26 -30
  12. package/src/cli/index.ts +10 -6
  13. package/src/cli/menu/index.ts +1 -0
  14. package/src/cli/menu/prompts/prompt-check.ts +74 -0
  15. package/src/cli/menu/prompts/prompt-discover.ts +25 -0
  16. package/src/cli/menu/prompts/prompt-generate.ts +106 -0
  17. package/src/cli/menu/prompts/prompt-validate.ts +49 -0
  18. package/src/cli/menu/prompts/shared/prompt-reader-options.ts +63 -0
  19. package/src/cli/menu/prompts/shared/shared.ts +76 -0
  20. package/src/cli/menu/run.ts +72 -0
  21. package/src/cli/version.ts +3 -0
  22. package/src/constants.ts +6 -0
  23. package/src/core/artifacts/index.ts +5 -0
  24. package/src/core/artifacts/schema/build-schema.ts +13 -0
  25. package/src/core/artifacts/schema/index.ts +3 -0
  26. package/src/core/{generated → artifacts/schema}/read-schema.ts +4 -4
  27. package/src/core/artifacts/schema/write-schema.ts +14 -0
  28. package/src/{build/build-types → core/artifacts/types/build}/build-types.ts +9 -10
  29. package/src/{build/build-types → core/artifacts/types/build}/utils/normalize-rich-infer-node.ts +1 -1
  30. package/src/{build/build-types → core/artifacts/types/build}/utils/render-infer-node.ts +1 -1
  31. package/src/core/artifacts/types/index.ts +2 -0
  32. package/src/core/artifacts/types/write-types.ts +8 -0
  33. package/src/core/artifacts/types.ts +20 -0
  34. package/src/core/collect-messages/collect-other-locale-messages.ts +5 -7
  35. package/src/core/collect-messages/collect-runtime-messages.ts +10 -6
  36. package/src/core/collect-messages/index.ts +1 -0
  37. package/src/core/collect-messages/readers.ts +1 -0
  38. package/src/core/collect-messages/types.ts +7 -1
  39. package/src/core/constants/index.ts +2 -0
  40. package/src/core/discover-configs/discover-configs.ts +47 -26
  41. package/src/core/extract-usages/extract-usages.ts +33 -24
  42. package/src/core/index.ts +12 -7
  43. package/src/core/infer-shape/index.ts +4 -0
  44. package/src/core/{infer-schema/messages/infer-messages-schema.ts → infer-shape/infer-messages-shape.ts} +5 -10
  45. package/src/core/{infer-schema/replacements/infer-replacements-schema.ts → infer-shape/infer-replacements-shape.ts} +6 -11
  46. package/src/core/{infer-schema/rich/infer-rich-schema.ts → infer-shape/infer-rich-shape.ts} +5 -10
  47. package/src/core/infer-shape/infer-shapes.ts +21 -0
  48. package/src/core/{infer-schema → infer-shape}/types.ts +4 -4
  49. package/src/core/scan/index.ts +2 -0
  50. package/src/core/{extract-usages/load-source-files-from-tscofnig.ts → scan/load-source-files.ts} +34 -15
  51. package/src/core/scan/scan-files.ts +25 -0
  52. package/src/features/check/build-scoped-usages.ts +35 -0
  53. package/src/features/check/check.ts +51 -53
  54. package/src/features/check/diagnostics/collect.ts +6 -2
  55. package/src/features/check/diagnostics/group.ts +0 -1
  56. package/src/features/check/index.ts +1 -0
  57. package/src/features/check/render-config-summary.ts +47 -0
  58. package/src/features/check/types.ts +12 -0
  59. package/src/features/discover/discover.ts +22 -0
  60. package/src/features/discover/index.ts +1 -0
  61. package/src/features/generate/generate.ts +56 -49
  62. package/src/features/generate/index.ts +1 -0
  63. package/src/features/generate/render-overrides.ts +73 -0
  64. package/src/features/generate/render-summary.ts +28 -0
  65. package/src/features/generate/types.ts +12 -0
  66. package/src/features/generate/utils/resolve-message-source.ts +20 -0
  67. package/src/features/generate/utils/validate-message-source.ts +53 -0
  68. package/src/features/index.ts +4 -3
  69. package/src/features/shared/to-relative-path.ts +10 -0
  70. package/src/features/shared/write-json-report.ts +19 -0
  71. package/src/features/validate/index.ts +1 -0
  72. package/src/features/validate/{messages/validate-messages-schema.ts → missing/collect-missing-messages.ts} +5 -5
  73. package/src/features/validate/{replacements/validate-replacements-schema.ts → missing/collect-missing-replacements.ts} +4 -4
  74. package/src/features/validate/missing/collect-missing-requirements.ts +44 -0
  75. package/src/features/validate/{rich/validate-rich-schema.ts → missing/collect-missing-rich.ts} +5 -5
  76. package/src/features/validate/render-config-summary.ts +47 -0
  77. package/src/features/validate/render-locale-blocks.ts +56 -0
  78. package/src/features/validate/types.ts +14 -0
  79. package/src/features/validate/validate.ts +38 -43
  80. package/src/logger.ts +95 -0
  81. package/src/render.ts +57 -0
  82. package/src/build/build-schemas/build-schemas.ts +0 -13
  83. package/src/build/build-schemas/index.ts +0 -1
  84. package/src/build/index.ts +0 -3
  85. package/src/build/types.ts +0 -20
  86. package/src/cli/interactive/prompt-generate.ts +0 -121
  87. package/src/cli/interactive/prompt-validate.ts +0 -101
  88. package/src/cli/interactive/run.ts +0 -64
  89. package/src/core/generated/index.ts +0 -6
  90. package/src/core/generated/write-messages-snapshot.ts +0 -27
  91. package/src/core/generated/write-schema.ts +0 -9
  92. package/src/core/generated/write-types.ts +0 -8
  93. package/src/core/infer-schema/index.ts +0 -4
  94. package/src/core/infer-schema/infer-schemas.ts +0 -20
  95. package/src/core/infer-schema/messages/index.ts +0 -1
  96. package/src/core/infer-schema/replacements/index.ts +0 -1
  97. package/src/core/infer-schema/rich/index.ts +0 -1
  98. package/src/core/scan-logger.ts +0 -10
  99. package/src/features/check/print-summary.ts +0 -28
  100. package/src/features/generate/print-configs.ts +0 -8
  101. package/src/features/generate/print-overrides.ts +0 -62
  102. package/src/features/generate/print-summary.ts +0 -26
  103. package/src/features/print.ts +0 -43
  104. package/src/features/validate/messages/index.ts +0 -1
  105. package/src/features/validate/print-summary.ts +0 -65
  106. package/src/features/validate/replacements/index.ts +0 -1
  107. package/src/features/validate/rich/index.ts +0 -1
  108. package/src/features/validate/validate-locale-messages.ts +0 -38
  109. /package/src/core/{generated → artifacts}/ensure-and-write.ts +0 -0
  110. /package/src/{build/build-types → core/artifacts/types/build}/index.ts +0 -0
  111. /package/src/{build/build-types → core/artifacts/types/build}/output/append-config-block.ts +0 -0
  112. /package/src/{build/build-types → core/artifacts/types/build}/output/append-footer.ts +0 -0
  113. /package/src/{build/build-types → core/artifacts/types/build}/output/append-header.ts +0 -0
  114. /package/src/{build/build-types → core/artifacts/types/build}/output/index.ts +0 -0
  115. /package/src/{build/build-types → core/artifacts/types/build}/utils/indent.ts +0 -0
  116. /package/src/core/{infer-schema/replacements → infer-shape/utils}/extract-interpolation-names.ts +0 -0
  117. /package/src/core/{infer-schema → infer-shape}/utils/infer-object.ts +0 -0
  118. /package/src/core/{infer-schema → infer-shape}/utils/is-message-object.ts +0 -0
  119. /package/src/core/{infer-schema → infer-shape}/utils/should-skip-key.ts +0 -0
  120. /package/src/core/{infer-schema → infer-shape}/utils/strip-internal-keys.ts +0 -0
  121. /package/src/features/{spinner.ts → shared/spinner.ts} +0 -0
package/README.md CHANGED
@@ -6,59 +6,56 @@
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-%E2%9C%94-blue?style=flat&colorA=000000&colorB=000000)](https://www.typescriptlang.org/)
7
7
  [![License](https://img.shields.io/npm/l/intor-cli?style=flat&colorA=000000&colorB=000000)](LICENSE)
8
8
 
9
- <table>
10
- <tr>
11
- <td align="center">
12
- <img src="generate-demo.gif" />
13
- </td>
14
- <td align="center">
15
- <img src="check-demo.gif" />
16
- </td>
17
- </tr>
18
- </table>
19
9
  </div>
20
10
 
21
11
  ## Overview
22
12
 
23
- - **generate** — message schema & types
24
- - **check** — usage analysis
13
+ - **discover** — config discovery
14
+ - **generate** — schema and type generation
15
+ - **check** — static usage analysis
25
16
  - **validate** — locale completeness
26
17
 
18
+ <img src='demo.gif' />
19
+
27
20
  ## Commands
28
21
 
29
- #### generate
22
+ Running `intor-cli` without a command launches the interactive menu.
23
+
24
+ ### Discover
25
+
26
+ ```bash
27
+ npx intor-cli discover
28
+ ```
29
+
30
+ Discovers Intor configs in the current workspace.
31
+
32
+ ### Generate
30
33
 
31
34
  ```bash
32
35
  npx intor-cli generate
33
36
  ```
34
37
 
35
- - Generates TypeScript types and schema artifacts
36
- - Uses the default locale as the single source of truth
37
- - Reports message override behavior during generation
38
+ Generates schemas and TypeScript types from the default locale.
38
39
 
39
- #### check
40
+ ### Check
40
41
 
41
42
  ```bash
42
43
  npx intor-cli check
43
44
  ```
44
45
 
45
- - Statistically analyzes translator usage in your codebase
46
- - Detects incorrect keys, replacements, and rich tag usage
47
- - Reports diagnostics with precise source locations
46
+ Analyzes translator usage and reports diagnostics.
48
47
 
49
- #### validate
48
+ ### Validate
50
49
 
51
50
  ```bash
52
51
  npx intor-cli validate
53
52
  ```
54
53
 
55
- - Validates locale message completeness against schemas
56
- - Checks missing keys, replacements, and rich tags
57
- - Reports issues grouped by config and locale
54
+ Validates locale messages against generated schemas.
55
+
56
+ ---
58
57
 
59
58
  ## Design Guarantees
60
59
 
61
- - Message types are inferred from the **_default locale_** only.
60
+ - Schemas and types are inferred **only from the default locale**.
62
61
  - All locales are expected to share the same message shape.
63
- - Locale is treated strictly as a runtime dimension, not a structural one.
64
- - Generated types are intentionally conservative and do not enforce locale completeness.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intor-cli",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "The Intor CLI",
5
5
  "author": "Yiming Liao",
6
6
  "homepage": "https://github.com/yiming-liao/intor-cli#readme",
@@ -46,7 +46,7 @@
46
46
  "@intor/reader-yaml": "^0.1.1",
47
47
  "cac": "6.7.14",
48
48
  "fast-glob": "3.3.3",
49
- "intor": "2.3.26",
49
+ "intor": "^2.3.35",
50
50
  "logry": "2.1.6",
51
51
  "ora": "9.0.0",
52
52
  "picocolors": "1.1.1",
@@ -65,8 +65,6 @@
65
65
  "eslint-plugin-unused-imports": "4.3.0",
66
66
  "knip": "5.79.0",
67
67
  "prettier": "3.7.4",
68
- "remark": "15.0.1",
69
- "remark-gfm": "4.0.1",
70
68
  "typescript": "5.9.3",
71
69
  "typescript-eslint": "8.51.0",
72
70
  "vitest": "4.0.16"
@@ -1,33 +1,39 @@
1
- import type { ExtractUsagesOptions } from "../../core/extract-usages/extract-usages";
1
+ import type { CliOption } from "./options";
2
2
  import type { CAC } from "cac";
3
+ import { features } from "../../constants";
3
4
  import { check } from "../../features";
5
+ import { options } from "./options";
4
6
 
5
7
  export function registerCheckCommand(cli: CAC) {
6
8
  cli
7
9
  // -----------------------------------------------------------------------
8
10
  // Command
9
11
  // -----------------------------------------------------------------------
10
- .command("check", "Validate intor translation usage")
12
+ .command(features.check.name, features.check.title)
11
13
 
12
14
  // -----------------------------------------------------------------------
13
- // Option
15
+ // Options
14
16
  // -----------------------------------------------------------------------
15
- .option(
16
- "--tsconfig <path>",
17
- "Path to tsconfig.json (default: tsconfig.json)",
18
- )
19
- .option("--debug", "Enable debug logging")
17
+ .option(...options.debug)
18
+ .option(...options.tsconfig)
19
+ .option(...options.format)
20
+ .option(...options.output)
20
21
 
21
22
  // -----------------------------------------------------------------------
22
23
  // Action
23
24
  // -----------------------------------------------------------------------
24
- .action(async (options: ExtractUsagesOptions) => {
25
- const { tsconfigPath, debug } = options;
26
- try {
27
- await check({ tsconfigPath, debug });
28
- } catch (error) {
29
- console.error(error);
30
- process.exitCode = 1;
31
- }
32
- });
25
+ .action(
26
+ async (
27
+ options: Pick<CliOption, "debug" | "tsconfig" | "format" | "output">,
28
+ ) => {
29
+ const { debug, tsconfig, format, output } = options;
30
+
31
+ try {
32
+ await check({ debug, tsconfigPath: tsconfig, format, output });
33
+ } catch (error) {
34
+ console.error(error instanceof Error ? error.message : error);
35
+ process.exitCode = 1;
36
+ }
37
+ },
38
+ );
33
39
  }
@@ -0,0 +1,32 @@
1
+ import type { CliOption } from "./options";
2
+ import type { CAC } from "cac";
3
+ import { features } from "../../constants";
4
+ import { discover } from "../../features";
5
+ import { options } from "./options";
6
+
7
+ export function registerDiscoverCommand(cli: CAC) {
8
+ cli
9
+ // -----------------------------------------------------------------------
10
+ // Command
11
+ // -----------------------------------------------------------------------
12
+ .command(features.discover.name, features.discover.title)
13
+
14
+ // -----------------------------------------------------------------------
15
+ // Options
16
+ // -----------------------------------------------------------------------
17
+ .option(...options.debug)
18
+
19
+ // -----------------------------------------------------------------------
20
+ // Action
21
+ // -----------------------------------------------------------------------
22
+ .action(async (options: Pick<CliOption, "debug">) => {
23
+ const { debug } = options;
24
+
25
+ try {
26
+ await discover({ debug });
27
+ } catch (error) {
28
+ console.error(error instanceof Error ? error.message : error);
29
+ process.exitCode = 1;
30
+ }
31
+ });
32
+ }
@@ -1,6 +1,10 @@
1
- import type { ExtraExt } from "../../core";
1
+ import type { CliOption } from "./options";
2
2
  import type { CAC } from "cac";
3
+ import { features } from "../../constants";
3
4
  import { generate } from "../../features";
5
+ import { version } from "../version";
6
+ import { options } from "./options";
7
+ import { normalizeMessageFiles } from "./utils/normalize-message-files";
4
8
  import { normalizeReaderOptions } from "./utils/normalize-reader-options";
5
9
 
6
10
  export function registerGenerateCommand(cli: CAC) {
@@ -8,53 +12,44 @@ export function registerGenerateCommand(cli: CAC) {
8
12
  // -----------------------------------------------------------------------
9
13
  // Command
10
14
  // -----------------------------------------------------------------------
11
- .command("generate", "Generate intor types and schemas")
15
+ .command(features.generate.name, features.generate.title)
12
16
 
13
17
  // -----------------------------------------------------------------------
14
18
  // Option
15
19
  // -----------------------------------------------------------------------
16
- .option(
17
- "--messages <path>",
18
- "Explicit messages file for schema generation (bypass runtime loader)",
19
- )
20
- .option(
21
- "--ext <ext>",
22
- "Enable extra messages file extension (repeatable)",
23
- { default: [] },
24
- )
25
- .option(
26
- "--reader <mapping>",
27
- "Custom reader mapping in the form <ext=path> (repeatable)",
28
- { default: [] },
29
- )
30
- .option(
31
- "--debug",
32
- "Print debug information during config discovery and generation",
33
- )
20
+ .option(...options.debug)
21
+ .option(...options.messageFile)
22
+ .option(...options.messageFiles)
23
+ .option(...options.ext)
24
+ .option(...options.reader)
34
25
 
35
26
  // -----------------------------------------------------------------------
36
27
  // Action
37
28
  // -----------------------------------------------------------------------
38
- .action(async (options) => {
39
- const { messages, ext, reader, debug } = options as {
40
- messages?: string;
41
- ext?: Array<ExtraExt>;
42
- reader?: string[];
43
- debug?: boolean;
44
- };
29
+ .action(
30
+ async (
31
+ options: Pick<
32
+ CliOption,
33
+ "debug" | "messageFile" | "messageFiles" | "ext" | "reader"
34
+ >,
35
+ ) => {
36
+ const { debug, messageFile, messageFiles, ...readerOptions } = options;
45
37
 
46
- const { exts, customReaders } = normalizeReaderOptions({ ext, reader });
38
+ const result = normalizeMessageFiles(messageFile, messageFiles);
39
+ const { exts, customReaders } = normalizeReaderOptions(readerOptions);
47
40
 
48
- try {
49
- await generate({
50
- messageFilePath: messages,
51
- exts,
52
- customReaders,
53
- debug,
54
- });
55
- } catch (error) {
56
- console.error(error);
57
- process.exitCode = 1;
58
- }
59
- });
41
+ try {
42
+ await generate({
43
+ debug,
44
+ messageSource: result,
45
+ exts,
46
+ customReaders,
47
+ toolVersion: version,
48
+ });
49
+ } catch (error) {
50
+ console.error(error instanceof Error ? error.message : error);
51
+ process.exitCode = 1;
52
+ }
53
+ },
54
+ );
60
55
  }
@@ -0,0 +1,4 @@
1
+ export { registerDiscoverCommand } from "./discover";
2
+ export { registerGenerateCommand } from "./generate";
3
+ export { registerCheckCommand } from "./check";
4
+ export { registerValidateCommand } from "./validate";
@@ -0,0 +1 @@
1
+ export { options, type CliOptions as CliOption } from "./options";
@@ -0,0 +1,55 @@
1
+ import type { CAC } from "cac";
2
+
3
+ export const options = {
4
+ debug: ["--debug", "Enable debug logging", { default: false }],
5
+
6
+ tsconfig: [
7
+ "--tsconfig <path>",
8
+ "Path to tsconfig.json (default: tsconfig.json)",
9
+ ],
10
+
11
+ messageFile: [
12
+ "--message-file <file>",
13
+ "Explicit message file for single-config projects",
14
+ ],
15
+
16
+ messageFiles: [
17
+ "--message-files <mapping...>",
18
+ "Explicit message file mapping in the form <configId=path> (repeatable)",
19
+ { default: [] },
20
+ ],
21
+
22
+ ext: [
23
+ "--ext <ext>",
24
+ "Enable extra messages file extension (repeatable)",
25
+ { default: [] },
26
+ ],
27
+
28
+ reader: [
29
+ "--reader <mapping>",
30
+ "Custom reader mapping in the form <ext=path> (repeatable)",
31
+ { default: [] },
32
+ ],
33
+
34
+ format: [
35
+ "--format <format>",
36
+ "Output format: human | json",
37
+ { default: "human" },
38
+ ],
39
+
40
+ output: [
41
+ "--output <file>",
42
+ "Write output to file (only applies to json format)",
43
+ ],
44
+ } as const satisfies Record<string, Parameters<CAC["option"]>>;
45
+
46
+ export interface CliOptions {
47
+ debug?: boolean;
48
+ tsconfig?: string;
49
+ messageFile?: string;
50
+ messageFiles?: string[];
51
+ ext?: string[];
52
+ reader?: string[];
53
+ format?: "human" | "json";
54
+ output?: string;
55
+ }
@@ -0,0 +1,49 @@
1
+ import type { MessageSource } from "../../../features";
2
+
3
+ /**
4
+ * Normalize message file CLI options into a MessageSource.
5
+ */
6
+ export function normalizeMessageFiles(
7
+ messageFile?: string,
8
+ messageFiles: string[] = [],
9
+ ): MessageSource {
10
+ if (messageFile && messageFiles.length > 0) {
11
+ throw new Error(
12
+ "Cannot use --message-file and --message-files at the same time.",
13
+ );
14
+ }
15
+
16
+ // --------------------------------------------------
17
+ // single mode
18
+ // --------------------------------------------------
19
+ if (messageFile) {
20
+ return {
21
+ mode: "single",
22
+ file: messageFile,
23
+ };
24
+ }
25
+
26
+ // --------------------------------------------------
27
+ // mapping mode
28
+ // --------------------------------------------------
29
+ if (messageFiles.length > 0) {
30
+ const files: Record<string, string> = {};
31
+
32
+ for (const messageFile of messageFiles) {
33
+ const [id, path] = messageFile.split("=", 2);
34
+ if (!id || !path) {
35
+ throw new Error(
36
+ `Invalid --message-files entry: "${messageFile}". Each entry must be in the form: <configId=path>`,
37
+ );
38
+ }
39
+ files[id] = path;
40
+ }
41
+
42
+ return {
43
+ mode: "mapping",
44
+ files,
45
+ };
46
+ }
47
+
48
+ return { mode: "none" };
49
+ }
@@ -1,46 +1,33 @@
1
- import type { ExtraExt } from "../../../core";
2
-
3
- export interface NormalizedReaderOptions {
4
- exts: ExtraExt[];
5
- customReaders?: Record<string, string>;
6
- }
7
-
8
- export interface RawReaderOptions {
9
- ext?: string | string[];
10
- reader?: string[];
11
- }
1
+ import type { ReaderOptions, ExtraExt } from "../../../core";
2
+ import type { CliOptions } from "../options/options";
12
3
 
13
4
  /**
14
- * Normalize CLI reader-related options:
15
- * - ext: string | string[] → string[]
16
- * - reader: ["md=./reader.ts"] → { md: "./reader.ts" }
5
+ * Normalize CLI reader-related options
17
6
  */
18
- export function normalizeReaderOptions(
19
- options: RawReaderOptions,
20
- ): NormalizedReaderOptions {
21
- // Normalize exts
22
- const exts = options.ext
23
- ? Array.isArray(options.ext)
24
- ? options.ext
25
- : [options.ext]
26
- : [];
27
-
7
+ export function normalizeReaderOptions({
8
+ ext = [],
9
+ reader = [],
10
+ }: Pick<CliOptions, "ext" | "reader">): ReaderOptions {
28
11
  // Normalize custom readers
29
12
  let customReaders: Record<string, string> | undefined;
30
13
 
31
- if (options.reader && options.reader.length > 0) {
14
+ if (reader && reader.length > 0) {
32
15
  customReaders = {};
33
16
 
34
- for (const item of options.reader) {
17
+ for (const item of reader) {
35
18
  const [key, value] = item.split("=", 2);
36
- if (!key || !value) continue;
19
+ if (!key || !value) {
20
+ throw new Error(
21
+ `Invalid --reader entry: "${item}". Each entry must be in the form: <ext=path>`,
22
+ );
23
+ }
37
24
 
38
25
  customReaders[key] = value;
39
26
  }
40
27
  }
41
28
 
42
29
  return {
43
- exts: exts as ExtraExt[],
30
+ exts: ext as ExtraExt[],
44
31
  customReaders,
45
32
  };
46
33
  }
@@ -1,6 +1,8 @@
1
- import type { ExtraExt } from "../../core";
1
+ import type { CliOptions } from "./options/options";
2
2
  import type { CAC } from "cac";
3
+ import { features } from "../../constants";
3
4
  import { validate } from "../../features";
5
+ import { options } from "./options";
4
6
  import { normalizeReaderOptions } from "./utils/normalize-reader-options";
5
7
 
6
8
  export function registerValidateCommand(cli: CAC) {
@@ -8,43 +10,37 @@ export function registerValidateCommand(cli: CAC) {
8
10
  // -----------------------------------------------------------------------
9
11
  // Command
10
12
  // -----------------------------------------------------------------------
11
- .command("validate", "Validate intor locale translations")
13
+ .command(features.validate.name, features.validate.title)
12
14
 
13
15
  // -----------------------------------------------------------------------
14
16
  // Option
15
17
  // -----------------------------------------------------------------------
16
- .option(
17
- "--ext <ext>",
18
- "Enable extra messages file extension (repeatable)",
19
- { default: [] },
20
- )
21
- .option(
22
- "--reader <mapping>",
23
- "Custom reader mapping in the form <ext=path> (repeatable)",
24
- { default: [] },
25
- )
26
- .option(
27
- "--debug",
28
- "Print debug information during config discovery and generation",
29
- )
18
+ .option(...options.debug)
19
+ .option(...options.ext)
20
+ .option(...options.reader)
21
+ .option(...options.format)
22
+ .option(...options.output)
30
23
 
31
24
  // -----------------------------------------------------------------------
32
25
  // Action
33
26
  // -----------------------------------------------------------------------
34
- .action(async (options) => {
35
- const { ext, reader, debug } = options as {
36
- ext?: Array<ExtraExt>;
37
- reader?: string[];
38
- debug?: boolean;
39
- };
27
+ .action(
28
+ async (
29
+ options: Pick<
30
+ CliOptions,
31
+ "debug" | "ext" | "reader" | "format" | "output"
32
+ >,
33
+ ) => {
34
+ const { debug, format, output, ...readerOptions } = options;
40
35
 
41
- const { exts, customReaders } = normalizeReaderOptions({ ext, reader });
36
+ const { exts, customReaders } = normalizeReaderOptions(readerOptions);
42
37
 
43
- try {
44
- await validate({ exts, customReaders, debug });
45
- } catch (error) {
46
- console.error(error);
47
- process.exitCode = 1;
48
- }
49
- });
38
+ try {
39
+ await validate({ debug, format, output, exts, customReaders });
40
+ } catch (error) {
41
+ console.error(error instanceof Error ? error.message : error);
42
+ process.exitCode = 1;
43
+ }
44
+ },
45
+ );
50
46
  }
package/src/cli/index.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env tsx
2
2
 
3
3
  import { cac } from "cac";
4
- import { registerCheckCommand } from "./commands/check";
5
- import { registerGenerateCommand } from "./commands/generate";
6
- import { registerValidateCommand } from "./commands/validate";
7
- import { runInteractive } from "./interactive/run";
4
+ import {
5
+ registerDiscoverCommand,
6
+ registerGenerateCommand,
7
+ registerCheckCommand,
8
+ registerValidateCommand,
9
+ } from "./commands";
10
+ import { run } from "./menu";
8
11
 
9
12
  const VERSION = "0.1.10";
10
13
 
@@ -13,10 +16,10 @@ async function main() {
13
16
  const args = process.argv.slice(2);
14
17
 
15
18
  // -------------------------------------------------------------------
16
- // Interactive mode bypasses CAC entirely
19
+ // Interactive menu bypasses CAC entirely
17
20
  // -------------------------------------------------------------------
18
21
  if (args.length === 0) {
19
- await runInteractive();
22
+ await run();
20
23
  return;
21
24
  }
22
25
 
@@ -25,6 +28,7 @@ async function main() {
25
28
  // -------------------------------------------------------------------
26
29
  const cli = cac("intor");
27
30
 
31
+ registerDiscoverCommand(cli);
28
32
  registerGenerateCommand(cli);
29
33
  registerCheckCommand(cli);
30
34
  registerValidateCommand(cli);
@@ -0,0 +1 @@
1
+ export { run } from "./run";
@@ -0,0 +1,74 @@
1
+ import type { CheckOptions } from "../../../features";
2
+ import { text, isCancel, confirm } from "@clack/prompts";
3
+ import {
4
+ promptMode,
5
+ promptDebug,
6
+ printOptionsSummary,
7
+ promptFormat,
8
+ promptOutput,
9
+ } from "./shared/shared";
10
+
11
+ export async function promptCheck(): Promise<CheckOptions | null> {
12
+ // ------------------------------------------------------------------
13
+ // Mode
14
+ // ------------------------------------------------------------------
15
+ const mode = await promptMode();
16
+ if (!mode) return null;
17
+ if (mode === "default") return {};
18
+
19
+ const options: CheckOptions = {};
20
+
21
+ // ------------------------------------------------------------------
22
+ // tsconfig path
23
+ // ------------------------------------------------------------------
24
+ const useCustomTsconfig = await confirm({
25
+ message: "Do you want to use a custom tsconfig file?",
26
+ initialValue: false,
27
+ });
28
+ if (isCancel(useCustomTsconfig)) return null;
29
+
30
+ if (useCustomTsconfig) {
31
+ const path = await text({
32
+ message: "Path to tsconfig file",
33
+ placeholder: "tsconfig.json",
34
+ defaultValue: "tsconfig.json",
35
+ });
36
+ if (isCancel(path)) return null;
37
+ options.tsconfigPath = path || undefined;
38
+ }
39
+
40
+ // ------------------------------------------------------------------
41
+ // Output format
42
+ // ------------------------------------------------------------------
43
+ const format = await promptFormat();
44
+ if (!format) return null;
45
+ options.format = format;
46
+
47
+ // ------------------------------------------------------------------
48
+ // Output file (only for json)
49
+ // ------------------------------------------------------------------
50
+ if (format === "json") {
51
+ const output = await promptOutput();
52
+ if (output === null) return null;
53
+ options.output = output;
54
+ }
55
+
56
+ // ------------------------------------------------------------------
57
+ // Debug
58
+ // ------------------------------------------------------------------
59
+ const debug = await promptDebug();
60
+ if (debug === null) return null;
61
+ if (debug) options.debug = true;
62
+
63
+ // ------------------------------------------------------------------
64
+ // Summary
65
+ // ------------------------------------------------------------------
66
+ printOptionsSummary("Check options", [
67
+ ["tsconfig", options.tsconfigPath ?? "tsconfig.json"],
68
+ ["format", options.format ?? "human"],
69
+ ["output", options.output ?? "stdout"],
70
+ ["debug", options.debug ? "on" : "off"],
71
+ ]);
72
+
73
+ return options;
74
+ }