intor-cli 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -3
- package/src/cli/commands/check.ts +23 -17
- package/src/cli/commands/discover.ts +32 -0
- package/src/cli/commands/generate.ts +35 -40
- package/src/cli/commands/index.ts +4 -0
- package/src/cli/commands/options/index.ts +1 -0
- package/src/cli/commands/options/options.ts +55 -0
- package/src/cli/commands/utils/normalize-message-files.ts +49 -0
- package/src/cli/commands/utils/normalize-reader-options.ts +15 -28
- package/src/cli/commands/validate.ts +26 -30
- package/src/cli/index.ts +38 -23
- package/src/cli/menu/index.ts +1 -0
- package/src/cli/menu/prompts/prompt-check.ts +74 -0
- package/src/cli/menu/prompts/prompt-discover.ts +25 -0
- package/src/cli/menu/prompts/prompt-generate.ts +106 -0
- package/src/cli/menu/prompts/prompt-validate.ts +49 -0
- package/src/cli/menu/prompts/shared/prompt-reader-options.ts +63 -0
- package/src/cli/menu/prompts/shared/shared.ts +76 -0
- package/src/cli/menu/run.ts +72 -0
- package/src/cli/version.ts +3 -0
- package/src/constants.ts +6 -0
- package/src/core/artifacts/index.ts +5 -0
- package/src/core/artifacts/schema/build-schema.ts +13 -0
- package/src/core/artifacts/schema/index.ts +3 -0
- package/src/core/{generated → artifacts/schema}/read-schema.ts +4 -4
- package/src/core/artifacts/schema/write-schema.ts +14 -0
- package/src/{build/build-types → core/artifacts/types/build}/build-types.ts +9 -10
- package/src/{build/build-types → core/artifacts/types/build}/utils/normalize-rich-infer-node.ts +1 -1
- package/src/{build/build-types → core/artifacts/types/build}/utils/render-infer-node.ts +1 -1
- package/src/core/artifacts/types/index.ts +2 -0
- package/src/core/artifacts/types/write-types.ts +8 -0
- package/src/core/artifacts/types.ts +20 -0
- package/src/core/collect-messages/collect-other-locale-messages.ts +5 -7
- package/src/core/collect-messages/collect-runtime-messages.ts +8 -6
- package/src/core/collect-messages/index.ts +1 -0
- package/src/core/collect-messages/readers.ts +1 -0
- package/src/core/collect-messages/types.ts +7 -1
- package/src/core/constants/index.ts +2 -0
- package/src/core/discover-configs/discover-configs.ts +47 -26
- package/src/core/extract-usages/extract-usages.ts +33 -24
- package/src/core/index.ts +12 -7
- package/src/core/infer-shape/index.ts +4 -0
- package/src/core/{infer-schema/messages/infer-messages-schema.ts → infer-shape/infer-messages-shape.ts} +5 -10
- package/src/core/{infer-schema/replacements/infer-replacements-schema.ts → infer-shape/infer-replacements-shape.ts} +6 -11
- package/src/core/{infer-schema/rich/infer-rich-schema.ts → infer-shape/infer-rich-shape.ts} +5 -10
- package/src/core/infer-shape/infer-shapes.ts +21 -0
- package/src/core/{infer-schema → infer-shape}/types.ts +4 -4
- package/src/core/scan/index.ts +2 -0
- package/src/core/{extract-usages/load-source-files-from-tscofnig.ts → scan/load-source-files.ts} +34 -15
- package/src/core/scan/scan-files.ts +25 -0
- package/src/features/check/build-scoped-usages.ts +35 -0
- package/src/features/check/check.ts +51 -53
- package/src/features/check/diagnostics/collect.ts +6 -2
- package/src/features/check/diagnostics/group.ts +0 -1
- package/src/features/check/index.ts +1 -0
- package/src/features/check/render-config-summary.ts +47 -0
- package/src/features/check/types.ts +12 -0
- package/src/features/discover/discover.ts +22 -0
- package/src/features/discover/index.ts +1 -0
- package/src/features/generate/generate.ts +56 -49
- package/src/features/generate/index.ts +1 -0
- package/src/features/generate/render-overrides.ts +73 -0
- package/src/features/generate/render-summary.ts +28 -0
- package/src/features/generate/types.ts +12 -0
- package/src/features/generate/utils/resolve-message-source.ts +20 -0
- package/src/features/generate/utils/validate-message-source.ts +53 -0
- package/src/features/index.ts +4 -3
- package/src/features/shared/to-relative-path.ts +10 -0
- package/src/features/shared/write-json-report.ts +19 -0
- package/src/features/validate/index.ts +1 -0
- package/src/features/validate/{messages/validate-messages-schema.ts → missing/collect-missing-messages.ts} +5 -5
- package/src/features/validate/{replacements/validate-replacements-schema.ts → missing/collect-missing-replacements.ts} +4 -4
- package/src/features/validate/missing/collect-missing-requirements.ts +44 -0
- package/src/features/validate/{rich/validate-rich-schema.ts → missing/collect-missing-rich.ts} +5 -5
- package/src/features/validate/render-config-summary.ts +47 -0
- package/src/features/validate/render-locale-blocks.ts +56 -0
- package/src/features/validate/types.ts +14 -0
- package/src/features/validate/validate.ts +38 -43
- package/src/logger.ts +95 -0
- package/src/render.ts +57 -0
- package/src/build/build-schemas/build-schemas.ts +0 -13
- package/src/build/build-schemas/index.ts +0 -1
- package/src/build/index.ts +0 -3
- package/src/build/types.ts +0 -20
- package/src/core/generated/index.ts +0 -6
- package/src/core/generated/write-messages-snapshot.ts +0 -27
- package/src/core/generated/write-schema.ts +0 -9
- package/src/core/generated/write-types.ts +0 -8
- package/src/core/infer-schema/index.ts +0 -4
- package/src/core/infer-schema/infer-schemas.ts +0 -20
- package/src/core/infer-schema/messages/index.ts +0 -1
- package/src/core/infer-schema/replacements/index.ts +0 -1
- package/src/core/infer-schema/rich/index.ts +0 -1
- package/src/core/scan-logger.ts +0 -10
- package/src/features/check/print-summary.ts +0 -28
- package/src/features/generate/print-configs.ts +0 -8
- package/src/features/generate/print-overrides.ts +0 -62
- package/src/features/generate/print-summary.ts +0 -26
- package/src/features/print.ts +0 -43
- package/src/features/validate/messages/index.ts +0 -1
- package/src/features/validate/print-summary.ts +0 -65
- package/src/features/validate/replacements/index.ts +0 -1
- package/src/features/validate/rich/index.ts +0 -1
- package/src/features/validate/validate-locale-messages.ts +0 -38
- /package/src/core/{generated → artifacts}/ensure-and-write.ts +0 -0
- /package/src/{build/build-types → core/artifacts/types/build}/index.ts +0 -0
- /package/src/{build/build-types → core/artifacts/types/build}/output/append-config-block.ts +0 -0
- /package/src/{build/build-types → core/artifacts/types/build}/output/append-footer.ts +0 -0
- /package/src/{build/build-types → core/artifacts/types/build}/output/append-header.ts +0 -0
- /package/src/{build/build-types → core/artifacts/types/build}/output/index.ts +0 -0
- /package/src/{build/build-types → core/artifacts/types/build}/utils/indent.ts +0 -0
- /package/src/core/{infer-schema/replacements → infer-shape/utils}/extract-interpolation-names.ts +0 -0
- /package/src/core/{infer-schema → infer-shape}/utils/infer-object.ts +0 -0
- /package/src/core/{infer-schema → infer-shape}/utils/is-message-object.ts +0 -0
- /package/src/core/{infer-schema → infer-shape}/utils/should-skip-key.ts +0 -0
- /package/src/core/{infer-schema → infer-shape}/utils/strip-internal-keys.ts +0 -0
- /package/src/features/{spinner.ts → shared/spinner.ts} +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { GenerateOptions } from "../../../features";
|
|
2
|
+
import { text, isCancel, confirm } from "@clack/prompts";
|
|
3
|
+
import { discoverConfigs } from "../../../core";
|
|
4
|
+
import { promptReaderOptions } from "./shared/prompt-reader-options";
|
|
5
|
+
import { promptMode, promptDebug, printOptionsSummary } from "./shared/shared";
|
|
6
|
+
|
|
7
|
+
export async function promptGenerate(): Promise<GenerateOptions | null> {
|
|
8
|
+
// ------------------------------------------------------------------
|
|
9
|
+
// Discover configs early (for prompt decisions)
|
|
10
|
+
// ------------------------------------------------------------------
|
|
11
|
+
const configs = await discoverConfigs();
|
|
12
|
+
if (configs.length === 0) throw new Error("No Intor config found.");
|
|
13
|
+
const isSingleConfig = configs.length === 1;
|
|
14
|
+
|
|
15
|
+
// ------------------------------------------------------------------
|
|
16
|
+
// Mode
|
|
17
|
+
// ------------------------------------------------------------------
|
|
18
|
+
const mode = await promptMode();
|
|
19
|
+
if (!mode) return null;
|
|
20
|
+
if (mode === "default") return {};
|
|
21
|
+
|
|
22
|
+
const options: GenerateOptions = {};
|
|
23
|
+
|
|
24
|
+
// ------------------------------------------------------------------
|
|
25
|
+
// Message source
|
|
26
|
+
// ------------------------------------------------------------------
|
|
27
|
+
const useCustomMessages = await confirm({
|
|
28
|
+
message:
|
|
29
|
+
"Do you want to provide message files instead of using the loader?",
|
|
30
|
+
initialValue: false,
|
|
31
|
+
});
|
|
32
|
+
if (isCancel(useCustomMessages)) return null;
|
|
33
|
+
|
|
34
|
+
if (useCustomMessages) {
|
|
35
|
+
const sourceMode = isSingleConfig ? "single" : "mapping";
|
|
36
|
+
|
|
37
|
+
// single mode
|
|
38
|
+
if (sourceMode === "single") {
|
|
39
|
+
const file = await text({
|
|
40
|
+
message: "Path to the message file (default locale)",
|
|
41
|
+
placeholder: "messages/en/index.json",
|
|
42
|
+
});
|
|
43
|
+
if (isCancel(file)) return null;
|
|
44
|
+
options.messageSource = { mode: "single", file };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// mapping mode
|
|
48
|
+
if (sourceMode === "mapping") {
|
|
49
|
+
const files: Record<string, string> = {};
|
|
50
|
+
for (const { config } of configs) {
|
|
51
|
+
const path = await text({
|
|
52
|
+
message: `Message file for config "${config.id}" (default locale)`,
|
|
53
|
+
placeholder: "messages/en/index.json",
|
|
54
|
+
});
|
|
55
|
+
if (isCancel(path)) return null;
|
|
56
|
+
if (path) files[config.id] = path;
|
|
57
|
+
}
|
|
58
|
+
options.messageSource = { mode: "mapping", files };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ------------------------------------------------------------------
|
|
63
|
+
// Reader options
|
|
64
|
+
// ------------------------------------------------------------------
|
|
65
|
+
const readerOptions = await promptReaderOptions();
|
|
66
|
+
if (readerOptions === null) return null;
|
|
67
|
+
if (readerOptions.exts?.length) {
|
|
68
|
+
options.exts = readerOptions.exts;
|
|
69
|
+
}
|
|
70
|
+
if (readerOptions.customReaders) {
|
|
71
|
+
options.customReaders = readerOptions.customReaders;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ------------------------------------------------------------------
|
|
75
|
+
// Debug
|
|
76
|
+
// ------------------------------------------------------------------
|
|
77
|
+
const debug = await promptDebug();
|
|
78
|
+
if (debug === null) return null;
|
|
79
|
+
if (debug) options.debug = true;
|
|
80
|
+
|
|
81
|
+
// ------------------------------------------------------------------
|
|
82
|
+
// Summary
|
|
83
|
+
// ------------------------------------------------------------------
|
|
84
|
+
printOptionsSummary("Generate options", [
|
|
85
|
+
[
|
|
86
|
+
"message files",
|
|
87
|
+
options.messageSource?.mode === "single"
|
|
88
|
+
? options.messageSource.file
|
|
89
|
+
: options.messageSource?.mode === "mapping"
|
|
90
|
+
? Object.entries(options.messageSource.files)
|
|
91
|
+
.map(([id, path]) => `${id}: ${path}`)
|
|
92
|
+
.join(", ")
|
|
93
|
+
: "(loader)",
|
|
94
|
+
],
|
|
95
|
+
["exts", options.exts?.join(", ") ?? "(none)"],
|
|
96
|
+
[
|
|
97
|
+
"custom readers",
|
|
98
|
+
options.customReaders
|
|
99
|
+
? Object.keys(options.customReaders).join(", ")
|
|
100
|
+
: "(none)",
|
|
101
|
+
],
|
|
102
|
+
["debug", options.debug ? "on" : "off"],
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
return options;
|
|
106
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ValidateOptions } from "../../../features";
|
|
2
|
+
import { promptReaderOptions } from "./shared/prompt-reader-options";
|
|
3
|
+
import { promptMode, promptDebug, printOptionsSummary } from "./shared/shared";
|
|
4
|
+
|
|
5
|
+
export async function promptValidate(): Promise<ValidateOptions | null> {
|
|
6
|
+
// ------------------------------------------------------------------
|
|
7
|
+
// Mode
|
|
8
|
+
// ------------------------------------------------------------------
|
|
9
|
+
const mode = await promptMode();
|
|
10
|
+
if (!mode) return null;
|
|
11
|
+
if (mode === "default") return {};
|
|
12
|
+
|
|
13
|
+
const options: ValidateOptions = {};
|
|
14
|
+
|
|
15
|
+
// ------------------------------------------------------------------
|
|
16
|
+
// Reader options
|
|
17
|
+
// ------------------------------------------------------------------
|
|
18
|
+
const readerOptions = await promptReaderOptions();
|
|
19
|
+
if (readerOptions === null) return null;
|
|
20
|
+
if (readerOptions.exts?.length) {
|
|
21
|
+
options.exts = readerOptions.exts;
|
|
22
|
+
}
|
|
23
|
+
if (readerOptions.customReaders) {
|
|
24
|
+
options.customReaders = readerOptions.customReaders;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ------------------------------------------------------------------
|
|
28
|
+
// Debug
|
|
29
|
+
// ------------------------------------------------------------------
|
|
30
|
+
const debug = await promptDebug();
|
|
31
|
+
if (debug === null) return null;
|
|
32
|
+
if (debug) options.debug = true;
|
|
33
|
+
|
|
34
|
+
// ------------------------------------------------------------------
|
|
35
|
+
// Summary
|
|
36
|
+
// ------------------------------------------------------------------
|
|
37
|
+
printOptionsSummary("Validate options", [
|
|
38
|
+
["exts", options.exts?.join(", ") ?? "(none)"],
|
|
39
|
+
[
|
|
40
|
+
"custom readers",
|
|
41
|
+
options.customReaders
|
|
42
|
+
? Object.keys(options.customReaders).join(", ")
|
|
43
|
+
: "(none)",
|
|
44
|
+
],
|
|
45
|
+
["debug", options.debug ? "on" : "off"],
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
return options;
|
|
49
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ReaderOptions, ExtraExt } from "../../../../core";
|
|
2
|
+
import { confirm, multiselect, text, isCancel } from "@clack/prompts";
|
|
3
|
+
|
|
4
|
+
type FormatOption = ExtraExt | "custom";
|
|
5
|
+
|
|
6
|
+
export async function promptReaderOptions(): Promise<ReaderOptions | null> {
|
|
7
|
+
const enable = await confirm({
|
|
8
|
+
message: "Enable additional message readers?",
|
|
9
|
+
initialValue: false,
|
|
10
|
+
});
|
|
11
|
+
if (isCancel(enable)) return null;
|
|
12
|
+
if (!enable) return { exts: [], customReaders: undefined };
|
|
13
|
+
|
|
14
|
+
// --------------------------------------------------
|
|
15
|
+
// Select built-in formats and/or custom reader
|
|
16
|
+
// --------------------------------------------------
|
|
17
|
+
const selected = await multiselect<FormatOption>({
|
|
18
|
+
message: "Select message readers",
|
|
19
|
+
options: [
|
|
20
|
+
{ value: "md", label: "Markdown (.md)" },
|
|
21
|
+
{ value: "yaml", label: "YAML (.yaml/.yml)" },
|
|
22
|
+
{ value: "toml", label: "TOML (.toml)" },
|
|
23
|
+
{ value: "json5", label: "JSON5 (.json5)" },
|
|
24
|
+
{ value: "custom", label: "Custom reader" },
|
|
25
|
+
],
|
|
26
|
+
required: false,
|
|
27
|
+
});
|
|
28
|
+
if (isCancel(selected)) return null;
|
|
29
|
+
|
|
30
|
+
// Extract built-in extensions
|
|
31
|
+
const exts = selected.filter((v): v is ExtraExt => v !== "custom");
|
|
32
|
+
|
|
33
|
+
let customReaders: Record<string, string> | undefined;
|
|
34
|
+
|
|
35
|
+
// --------------------------------------------------
|
|
36
|
+
// Custom reader mappings (ext=path)
|
|
37
|
+
// --------------------------------------------------
|
|
38
|
+
if (selected.includes("custom")) {
|
|
39
|
+
const mapping = await text({
|
|
40
|
+
message: "Custom reader mappings (ext=path, comma separated)",
|
|
41
|
+
placeholder: "md=./reader-md.ts",
|
|
42
|
+
});
|
|
43
|
+
if (isCancel(mapping)) return null;
|
|
44
|
+
|
|
45
|
+
customReaders = Object.fromEntries(
|
|
46
|
+
mapping
|
|
47
|
+
.split(",")
|
|
48
|
+
.map((pair) => pair.trim())
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
.map((pair) => {
|
|
51
|
+
const [ext, path] = pair.split("=", 2);
|
|
52
|
+
if (!ext || !path) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Invalid custom reader entry: "${pair}". Expected <ext=path>.`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return [ext, path];
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { exts, customReaders };
|
|
63
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { select, isCancel, confirm, text, note } from "@clack/prompts";
|
|
2
|
+
|
|
3
|
+
// ------------------------------------------------------------------
|
|
4
|
+
// Mode
|
|
5
|
+
// ------------------------------------------------------------------
|
|
6
|
+
export async function promptMode(): Promise<"default" | "custom" | null> {
|
|
7
|
+
const mode = await select({
|
|
8
|
+
message: "Mode?",
|
|
9
|
+
options: [
|
|
10
|
+
{ value: "default", label: "Default (recommended)" },
|
|
11
|
+
{ value: "custom", label: "Custom configuration" },
|
|
12
|
+
],
|
|
13
|
+
});
|
|
14
|
+
if (isCancel(mode)) return null;
|
|
15
|
+
return mode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ------------------------------------------------------------------
|
|
19
|
+
// Format
|
|
20
|
+
// ------------------------------------------------------------------
|
|
21
|
+
export async function promptFormat(): Promise<"human" | "json" | null> {
|
|
22
|
+
const format = await select({
|
|
23
|
+
message: "Select output format",
|
|
24
|
+
options: [
|
|
25
|
+
{ value: "human", label: "Human-readable" },
|
|
26
|
+
{ value: "json", label: "JSON (machine-readable)" },
|
|
27
|
+
],
|
|
28
|
+
initialValue: "human",
|
|
29
|
+
});
|
|
30
|
+
if (isCancel(format)) return null;
|
|
31
|
+
return format as "human" | "json";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ------------------------------------------------------------------
|
|
35
|
+
// Output
|
|
36
|
+
// ------------------------------------------------------------------
|
|
37
|
+
export async function promptOutput(): Promise<string | undefined | null> {
|
|
38
|
+
const writeToFile = await confirm({
|
|
39
|
+
message: "Write output to a file?",
|
|
40
|
+
initialValue: false,
|
|
41
|
+
});
|
|
42
|
+
if (isCancel(writeToFile)) return null;
|
|
43
|
+
|
|
44
|
+
if (writeToFile) {
|
|
45
|
+
const output = await text({
|
|
46
|
+
message: "Output file path",
|
|
47
|
+
placeholder: "check-report.json",
|
|
48
|
+
defaultValue: "check-report.json",
|
|
49
|
+
});
|
|
50
|
+
if (isCancel(output)) return null;
|
|
51
|
+
return output || undefined;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ------------------------------------------------------------------
|
|
57
|
+
// Debug
|
|
58
|
+
// ------------------------------------------------------------------
|
|
59
|
+
export async function promptDebug(): Promise<boolean | null> {
|
|
60
|
+
const debug = await confirm({
|
|
61
|
+
message: "Enable debug mode?",
|
|
62
|
+
initialValue: false,
|
|
63
|
+
});
|
|
64
|
+
if (isCancel(debug)) return null;
|
|
65
|
+
return debug;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ------------------------------------------------------------------
|
|
69
|
+
// Summary
|
|
70
|
+
// ------------------------------------------------------------------
|
|
71
|
+
export function printOptionsSummary(
|
|
72
|
+
title: string,
|
|
73
|
+
lines: Array<[string, string]>,
|
|
74
|
+
) {
|
|
75
|
+
note(lines.map(([k, v]) => `${k}: ${v}`).join("\n"), title);
|
|
76
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* eslint-disable unicorn/no-process-exit */
|
|
2
|
+
import { outro, select, isCancel, intro } from "@clack/prompts";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import { features } from "../../constants";
|
|
5
|
+
import { check, discover, generate, validate } from "../../features";
|
|
6
|
+
import { bold, italic } from "../../render";
|
|
7
|
+
import { version } from "../version";
|
|
8
|
+
import { promptCheck } from "./prompts/prompt-check";
|
|
9
|
+
import { promptDiscover } from "./prompts/prompt-discover";
|
|
10
|
+
import { promptGenerate } from "./prompts/prompt-generate";
|
|
11
|
+
import { promptValidate } from "./prompts/prompt-validate";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Run a single CLI action with its corresponding prompt and handler.
|
|
15
|
+
*/
|
|
16
|
+
async function runAction<T>(
|
|
17
|
+
prompt: () => Promise<T | null>,
|
|
18
|
+
action: (options: T) => Promise<void>,
|
|
19
|
+
) {
|
|
20
|
+
const options = await prompt();
|
|
21
|
+
if (!options) {
|
|
22
|
+
outro(pc.dim("Cancelled"));
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
await action(options);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Entry point for the interactive CLI menu.
|
|
30
|
+
*/
|
|
31
|
+
export async function run() {
|
|
32
|
+
intro(italic(bold("The Intor CLI.")));
|
|
33
|
+
|
|
34
|
+
const action = await select({
|
|
35
|
+
message: "Select an action",
|
|
36
|
+
options: [
|
|
37
|
+
...Object.values(features).map(({ name, title }) => ({
|
|
38
|
+
value: name,
|
|
39
|
+
label: title,
|
|
40
|
+
})),
|
|
41
|
+
{ value: "exit", label: "Exit" },
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (isCancel(action) || action === "exit") {
|
|
46
|
+
outro("Exited");
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
switch (action) {
|
|
51
|
+
case "discover": {
|
|
52
|
+
await runAction(promptDiscover, discover);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case "check": {
|
|
56
|
+
await runAction(promptCheck, check);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "generate": {
|
|
60
|
+
await runAction(promptGenerate, (options) =>
|
|
61
|
+
generate({ ...options, toolVersion: version }),
|
|
62
|
+
);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case "validate": {
|
|
66
|
+
await runAction(promptValidate, validate);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
outro(pc.green("Completed"));
|
|
72
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const features = {
|
|
2
|
+
discover: { name: "discover", title: "Discover intor configs" },
|
|
3
|
+
generate: { name: "generate", title: "Generate types & schemas" },
|
|
4
|
+
check: { name: "check", title: "Check translation usages" },
|
|
5
|
+
validate: { name: "validate", title: "Validate messages" },
|
|
6
|
+
} as const;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { GeneratedSchema, SchemaEntry } from "../types";
|
|
2
|
+
|
|
3
|
+
export function buildSchema(
|
|
4
|
+
entries: SchemaEntry[],
|
|
5
|
+
toolVersion?: string,
|
|
6
|
+
): GeneratedSchema {
|
|
7
|
+
return {
|
|
8
|
+
version: 1,
|
|
9
|
+
toolVersion,
|
|
10
|
+
generatedAt: new Date().toISOString(),
|
|
11
|
+
entries,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GeneratedSchema } from "../types";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { DEFAULT_OUT_DIR, DEFAULT_SCHEMA_FILE_NAME } from "
|
|
4
|
+
import { DEFAULT_OUT_DIR, DEFAULT_SCHEMA_FILE_NAME } from "../../constants";
|
|
5
5
|
|
|
6
|
-
export async function readSchema(): Promise<
|
|
6
|
+
export async function readSchema(): Promise<GeneratedSchema> {
|
|
7
7
|
const filePath = path.join(DEFAULT_OUT_DIR, DEFAULT_SCHEMA_FILE_NAME);
|
|
8
8
|
|
|
9
9
|
let raw: string;
|
|
@@ -24,7 +24,7 @@ export async function readSchema(): Promise<Schema> {
|
|
|
24
24
|
// Parse JSON
|
|
25
25
|
// ------------------------------------------------------------------
|
|
26
26
|
try {
|
|
27
|
-
return JSON.parse(raw) as
|
|
27
|
+
return JSON.parse(raw) as GeneratedSchema;
|
|
28
28
|
} catch {
|
|
29
29
|
throw new Error(
|
|
30
30
|
`Invalid JSON format in Intor schema file at "${filePath}".`,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GeneratedSchema } from "../types";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DEFAULT_OUT_DIR, DEFAULT_SCHEMA_FILE_NAME } from "../../constants";
|
|
4
|
+
import { ensureAndWrite } from "../ensure-and-write";
|
|
5
|
+
|
|
6
|
+
export async function writeSchema(
|
|
7
|
+
generatedSchema: GeneratedSchema,
|
|
8
|
+
): Promise<string> {
|
|
9
|
+
const filePath = path.join(DEFAULT_OUT_DIR, DEFAULT_SCHEMA_FILE_NAME);
|
|
10
|
+
return await ensureAndWrite(
|
|
11
|
+
filePath,
|
|
12
|
+
JSON.stringify(generatedSchema, null, 2),
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SchemaEntry } from "../../types";
|
|
2
2
|
import { appendHeader, appendConfigBlock, appendFooter } from "./output";
|
|
3
3
|
import { normalizeRichInferNode } from "./utils/normalize-rich-infer-node";
|
|
4
4
|
import { renderInferNode } from "./utils/render-infer-node";
|
|
@@ -8,20 +8,19 @@ const GENERATED_INTERFACE_NAME = "IntorGeneratedTypes";
|
|
|
8
8
|
/**
|
|
9
9
|
* Builds the global TypeScript declaration from inferred typegen inputs.
|
|
10
10
|
*/
|
|
11
|
-
export function buildTypes(
|
|
11
|
+
export function buildTypes(schemaEntries: SchemaEntry[]): string {
|
|
12
12
|
const lines: string[] = [];
|
|
13
13
|
|
|
14
14
|
// Global declaration header
|
|
15
15
|
appendHeader(lines, GENERATED_INTERFACE_NAME);
|
|
16
16
|
|
|
17
17
|
// Per-config processing
|
|
18
|
-
for (const [index,
|
|
19
|
-
const localesType =
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
);
|
|
18
|
+
for (const [index, entry] of schemaEntries.entries()) {
|
|
19
|
+
const localesType = entry.locales.map((l) => `"${l}"`).join(" | ");
|
|
20
|
+
|
|
21
|
+
const messagesType = renderInferNode(entry.shapes.messages);
|
|
22
|
+
const replacementsType = renderInferNode(entry.shapes.replacements);
|
|
23
|
+
const richType = renderInferNode(normalizeRichInferNode(entry.shapes.rich));
|
|
25
24
|
|
|
26
25
|
if (index === 0) {
|
|
27
26
|
appendConfigBlock(lines, {
|
|
@@ -34,7 +33,7 @@ export function buildTypes(inputs: BuildInput[]): string {
|
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
appendConfigBlock(lines, {
|
|
37
|
-
id: `"${
|
|
36
|
+
id: `"${entry.id}"`,
|
|
38
37
|
locales: localesType,
|
|
39
38
|
messages: messagesType,
|
|
40
39
|
replacements: replacementsType,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { DEFAULT_OUT_DIR, DEFAULT_TYPES_FILE_NAME } from "../../constants";
|
|
3
|
+
import { ensureAndWrite } from "../ensure-and-write";
|
|
4
|
+
|
|
5
|
+
export async function writeTypes(generatedTypes: string): Promise<string> {
|
|
6
|
+
const filePath = path.join(DEFAULT_OUT_DIR, DEFAULT_TYPES_FILE_NAME);
|
|
7
|
+
return await ensureAndWrite(filePath, generatedTypes);
|
|
8
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { InferredShapes } from "../infer-shape";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Versioned schema artifact generated from inferred semantic shapes.
|
|
5
|
+
*/
|
|
6
|
+
export interface GeneratedSchema {
|
|
7
|
+
version: number;
|
|
8
|
+
toolVersion?: string;
|
|
9
|
+
generatedAt: string;
|
|
10
|
+
entries: SchemaEntry[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A schema entry representing the inferred result of a single Intor config.
|
|
15
|
+
*/
|
|
16
|
+
export interface SchemaEntry {
|
|
17
|
+
id: string;
|
|
18
|
+
locales: readonly string[];
|
|
19
|
+
shapes: InferredShapes;
|
|
20
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReaderOptions } from "./types";
|
|
2
2
|
import type { IntorResolvedConfig, MessageObject } from "intor";
|
|
3
3
|
import { collectRuntimeMessages } from "./collect-runtime-messages";
|
|
4
4
|
|
|
5
5
|
export async function collectOtherLocaleMessages(
|
|
6
6
|
config: IntorResolvedConfig,
|
|
7
|
-
exts
|
|
8
|
-
customReaders?: Record<string, string>,
|
|
7
|
+
{ exts, customReaders }: ReaderOptions,
|
|
9
8
|
): Promise<Record<string, MessageObject>> {
|
|
10
9
|
const { supportedLocales, defaultLocale } = config;
|
|
11
10
|
|
|
@@ -14,12 +13,11 @@ export async function collectOtherLocaleMessages(
|
|
|
14
13
|
for (const locale of supportedLocales) {
|
|
15
14
|
if (locale === defaultLocale) continue;
|
|
16
15
|
|
|
17
|
-
const { messages } = await collectRuntimeMessages(
|
|
18
|
-
config,
|
|
19
|
-
locale,
|
|
16
|
+
const { messages } = await collectRuntimeMessages(config, locale, {
|
|
20
17
|
exts,
|
|
21
18
|
customReaders,
|
|
22
|
-
);
|
|
19
|
+
});
|
|
20
|
+
|
|
23
21
|
result[locale] = messages[locale];
|
|
24
22
|
}
|
|
25
23
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
ReaderOptions,
|
|
3
|
+
CollectRuntimeMessagesResult,
|
|
4
|
+
MergeOverrides,
|
|
5
|
+
} from "./types";
|
|
3
6
|
import {
|
|
4
7
|
mergeMessages,
|
|
5
8
|
resolveLoaderOptions,
|
|
@@ -19,8 +22,7 @@ import { resolveMessagesReader } from "./resolve-messages-reader";
|
|
|
19
22
|
export async function collectRuntimeMessages(
|
|
20
23
|
config: IntorResolvedConfig,
|
|
21
24
|
locale: string,
|
|
22
|
-
exts
|
|
23
|
-
customReaders?: Record<string, string>, // {ext, customReaderFilePath}
|
|
25
|
+
{ exts = [], customReaders }: ReaderOptions,
|
|
24
26
|
): Promise<CollectRuntimeMessagesResult> {
|
|
25
27
|
// ----------------------------------------------------------------------
|
|
26
28
|
// Resolve readers
|
|
@@ -74,7 +76,7 @@ export async function collectRuntimeMessages(
|
|
|
74
76
|
onEvent: (event) => {
|
|
75
77
|
overrides.push({
|
|
76
78
|
...event,
|
|
77
|
-
layer: "
|
|
79
|
+
layer: "clientOverServer",
|
|
78
80
|
locale,
|
|
79
81
|
configId: config.id,
|
|
80
82
|
});
|
|
@@ -88,7 +90,7 @@ export async function collectRuntimeMessages(
|
|
|
88
90
|
onEvent: (event) => {
|
|
89
91
|
overrides.push({
|
|
90
92
|
...event,
|
|
91
|
-
layer: "
|
|
93
|
+
layer: "runtimeOverStatic",
|
|
92
94
|
locale,
|
|
93
95
|
configId: config.id,
|
|
94
96
|
});
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import type { ExtraExt } from "../constants";
|
|
1
2
|
import type { DeepMergeOverrideEvent, LocaleMessages } from "intor";
|
|
2
3
|
|
|
4
|
+
export interface ReaderOptions {
|
|
5
|
+
exts?: Array<ExtraExt>;
|
|
6
|
+
customReaders?: Record<string, string>; // {ext, customReaderFilePath}
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
export interface CollectRuntimeMessagesResult {
|
|
4
10
|
messages: LocaleMessages;
|
|
5
11
|
overrides: MergeOverrides[];
|
|
6
12
|
}
|
|
7
13
|
|
|
8
14
|
export interface MergeOverrides extends DeepMergeOverrideEvent {
|
|
9
|
-
layer: "
|
|
15
|
+
layer: "clientOverServer" | "runtimeOverStatic";
|
|
10
16
|
locale: string;
|
|
11
17
|
configId: string;
|
|
12
18
|
}
|