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
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import type { MissingRequirements } from "./collect-missing-requirements";
|
|
1
2
|
import type { InferNode } from "../../../core";
|
|
2
|
-
import type { ValidationResult } from "../validate-locale-messages";
|
|
3
3
|
import type { MessageObject } from "intor";
|
|
4
4
|
|
|
5
|
-
export function
|
|
5
|
+
export function collectMissingMessages(
|
|
6
6
|
schema: InferNode,
|
|
7
7
|
messages: MessageObject,
|
|
8
8
|
path: string,
|
|
9
|
-
result:
|
|
9
|
+
result: MissingRequirements,
|
|
10
10
|
) {
|
|
11
11
|
// Only object schemas define required message keys
|
|
12
12
|
if (schema.kind !== "object") return;
|
|
@@ -19,13 +19,13 @@ export function validateMessagesSchema(
|
|
|
19
19
|
|
|
20
20
|
// Schema requires this key, but message does not provide it
|
|
21
21
|
if (value === undefined) {
|
|
22
|
-
result.
|
|
22
|
+
result.missingMessages.push(nextPath);
|
|
23
23
|
continue;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Recurse into nested message objects
|
|
27
27
|
if (typeof value === "object" && value !== null) {
|
|
28
|
-
|
|
28
|
+
collectMissingMessages(
|
|
29
29
|
expected,
|
|
30
30
|
value as MessageObject,
|
|
31
31
|
nextPath,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import type { MissingRequirements } from "./collect-missing-requirements";
|
|
1
2
|
import type { InferNode } from "../../../core";
|
|
2
|
-
import type { ValidationResult } from "../validate-locale-messages";
|
|
3
3
|
import type { MessageObject } from "intor";
|
|
4
4
|
import { extractInterpolationNames } from "../../../core";
|
|
5
5
|
|
|
6
|
-
export function
|
|
6
|
+
export function collectMissingReplacements(
|
|
7
7
|
schema: InferNode,
|
|
8
8
|
messages: MessageObject,
|
|
9
9
|
path: string,
|
|
10
|
-
result:
|
|
10
|
+
result: MissingRequirements,
|
|
11
11
|
) {
|
|
12
12
|
// Only object schemas define required message keys
|
|
13
13
|
if (schema.kind !== "object") return;
|
|
@@ -40,7 +40,7 @@ export function validateReplacementsSchema(
|
|
|
40
40
|
|
|
41
41
|
// Recurse into nested message objects
|
|
42
42
|
if (typeof value === "object" && value !== null) {
|
|
43
|
-
|
|
43
|
+
collectMissingReplacements(
|
|
44
44
|
expected,
|
|
45
45
|
value as MessageObject,
|
|
46
46
|
nextPath,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { InferredShapes } from "../../../core";
|
|
2
|
+
import type { MessageObject } from "intor";
|
|
3
|
+
import { collectMissingMessages } from "./collect-missing-messages";
|
|
4
|
+
import { collectMissingReplacements } from "./collect-missing-replacements";
|
|
5
|
+
import { collectMissingRich } from "./collect-missing-rich";
|
|
6
|
+
|
|
7
|
+
export interface MissingRequirements {
|
|
8
|
+
missingMessages: string[];
|
|
9
|
+
missingReplacements: Array<{ key: string; name: string }>;
|
|
10
|
+
missingRich: Array<{ key: string; tag: string }>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Collect missing translation requirements by comparing
|
|
15
|
+
* inferred semantic schemas with locale messages.
|
|
16
|
+
*/
|
|
17
|
+
export function collectMissingRequirements(
|
|
18
|
+
schemas: InferredShapes,
|
|
19
|
+
localeMessages: MessageObject,
|
|
20
|
+
): MissingRequirements {
|
|
21
|
+
const missingRequirements: MissingRequirements = {
|
|
22
|
+
missingMessages: [],
|
|
23
|
+
missingReplacements: [],
|
|
24
|
+
missingRich: [],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
collectMissingMessages(
|
|
28
|
+
schemas.messages,
|
|
29
|
+
localeMessages,
|
|
30
|
+
"",
|
|
31
|
+
missingRequirements,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
collectMissingReplacements(
|
|
35
|
+
schemas.replacements,
|
|
36
|
+
localeMessages,
|
|
37
|
+
"",
|
|
38
|
+
missingRequirements,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
collectMissingRich(schemas.rich, localeMessages, "", missingRequirements);
|
|
42
|
+
|
|
43
|
+
return missingRequirements;
|
|
44
|
+
}
|
package/src/features/validate/{rich/validate-rich-schema.ts → missing/collect-missing-rich.ts}
RENAMED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import type { MissingRequirements } from "./collect-missing-requirements";
|
|
1
2
|
import type { InferNode } from "../../../core";
|
|
2
|
-
import type { ValidationResult } from "../validate-locale-messages";
|
|
3
3
|
import { tokenize, type MessageObject, type Token } from "intor";
|
|
4
4
|
|
|
5
|
-
export function
|
|
5
|
+
export function collectMissingRich(
|
|
6
6
|
schema: InferNode,
|
|
7
7
|
messages: MessageObject,
|
|
8
8
|
path: string,
|
|
9
|
-
result:
|
|
9
|
+
result: MissingRequirements,
|
|
10
10
|
) {
|
|
11
11
|
// Only object schemas define expected rich tags
|
|
12
12
|
if (schema.kind !== "object") return;
|
|
@@ -34,7 +34,7 @@ export function validateRichSchema(
|
|
|
34
34
|
// Report any schema-required rich tags missing in the message
|
|
35
35
|
for (const tag of Object.keys(expected.properties)) {
|
|
36
36
|
if (actualTags.has(tag)) continue;
|
|
37
|
-
result.
|
|
37
|
+
result.missingRich.push({ key: nextPath, tag });
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
continue;
|
|
@@ -42,7 +42,7 @@ export function validateRichSchema(
|
|
|
42
42
|
|
|
43
43
|
// Recurse into nested message objects
|
|
44
44
|
if (typeof value === "object" && value !== null) {
|
|
45
|
-
|
|
45
|
+
collectMissingRich(expected, value as MessageObject, nextPath, result);
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { MissingRequirementsByLocale } from "./types";
|
|
2
|
+
import { createLogger } from "../../logger";
|
|
3
|
+
import { cyan, br, yellow } from "../../render";
|
|
4
|
+
import { renderLocaleBlocks } from "./render-locale-blocks";
|
|
5
|
+
|
|
6
|
+
// { [locale: string]: MissingRequirements; } → { string; MissingRequirements; }[]
|
|
7
|
+
function normalizeMissingByLocale(
|
|
8
|
+
missingByLocale: MissingRequirementsByLocale,
|
|
9
|
+
) {
|
|
10
|
+
return Object.entries(missingByLocale)
|
|
11
|
+
.map(([locale, missing]) => ({ locale, missing }))
|
|
12
|
+
.filter(
|
|
13
|
+
({ missing }) =>
|
|
14
|
+
missing.missingMessages.length > 0 ||
|
|
15
|
+
missing.missingReplacements.length > 0 ||
|
|
16
|
+
missing.missingRich.length > 0,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function renderConfigSummary(
|
|
21
|
+
configId: string,
|
|
22
|
+
missingByLocale: MissingRequirementsByLocale,
|
|
23
|
+
enabled = true,
|
|
24
|
+
) {
|
|
25
|
+
if (!enabled) return;
|
|
26
|
+
const logger = createLogger();
|
|
27
|
+
br();
|
|
28
|
+
|
|
29
|
+
const entries = normalizeMissingByLocale(missingByLocale);
|
|
30
|
+
|
|
31
|
+
if (entries.length === 0) {
|
|
32
|
+
logger.ok(`${cyan(configId)}: no problems found`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Log header
|
|
37
|
+
logger.header(
|
|
38
|
+
`${cyan(configId)}: ${yellow(entries.length)} problem locale(s)`,
|
|
39
|
+
{ lineBreakAfter: 1 },
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Render locale blocks
|
|
43
|
+
renderLocaleBlocks(entries);
|
|
44
|
+
|
|
45
|
+
// Log footer
|
|
46
|
+
logger.footer("", { lineBreakBefore: 1 });
|
|
47
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { MissingRequirements } from "./missing/collect-missing-requirements";
|
|
2
|
+
import { createLogger } from "../../logger";
|
|
3
|
+
import { dim, italic, gray } from "../../render";
|
|
4
|
+
|
|
5
|
+
export function renderLocaleBlocks(
|
|
6
|
+
entries: { locale: string; missing: MissingRequirements }[],
|
|
7
|
+
) {
|
|
8
|
+
const logger = createLogger();
|
|
9
|
+
const prefix = dim("│ ");
|
|
10
|
+
|
|
11
|
+
for (let i = 0; i < entries.length; i++) {
|
|
12
|
+
const { locale, missing } = entries[i];
|
|
13
|
+
const isLastLocale = i === entries.length - 1;
|
|
14
|
+
|
|
15
|
+
const { missingMessages, missingReplacements, missingRich } = missing;
|
|
16
|
+
|
|
17
|
+
logger.header(italic(locale), { prefix });
|
|
18
|
+
logger.log("", { prefix });
|
|
19
|
+
|
|
20
|
+
let hasPrintedSection = false;
|
|
21
|
+
|
|
22
|
+
// messages
|
|
23
|
+
if (missingMessages.length > 0) {
|
|
24
|
+
logger.log(gray("Missing messages:"), { prefix });
|
|
25
|
+
for (const message of missingMessages) {
|
|
26
|
+
logger.log(` - ${message}`, { prefix });
|
|
27
|
+
}
|
|
28
|
+
hasPrintedSection = true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// replacements
|
|
32
|
+
if (missingReplacements.length > 0) {
|
|
33
|
+
if (hasPrintedSection) logger.log("", { prefix });
|
|
34
|
+
logger.log(gray("Missing replacements:"), { prefix });
|
|
35
|
+
for (const { key, name } of missingReplacements) {
|
|
36
|
+
logger.log(` - ${key}: ${name}`, { prefix });
|
|
37
|
+
}
|
|
38
|
+
hasPrintedSection = true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// rich
|
|
42
|
+
if (missingRich.length > 0) {
|
|
43
|
+
if (hasPrintedSection) logger.log("", { prefix });
|
|
44
|
+
logger.log(gray("Missing rich tags:"), { prefix });
|
|
45
|
+
for (const { key, tag } of missingRich) {
|
|
46
|
+
logger.log(` - ${key}: ${tag}`, { prefix });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
logger.log("", { prefix });
|
|
51
|
+
logger.footer("", { prefix });
|
|
52
|
+
|
|
53
|
+
// locale ↔ locale spacing
|
|
54
|
+
if (!isLastLocale) logger.log();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReaderOptions } from "../../core";
|
|
2
|
+
import type { MissingRequirements } from "./missing/collect-missing-requirements";
|
|
3
|
+
|
|
4
|
+
export type MissingRequirementsByLocale = {
|
|
5
|
+
[locale: string]: MissingRequirements;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type MissingReport = { [configId: string]: MissingRequirementsByLocale };
|
|
9
|
+
|
|
10
|
+
export interface ValidateOptions extends ReaderOptions {
|
|
11
|
+
format?: "human" | "json";
|
|
12
|
+
output?: string;
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
}
|
|
@@ -1,83 +1,78 @@
|
|
|
1
1
|
/* eslint-disable unicorn/no-process-exit */
|
|
2
|
-
import
|
|
2
|
+
import type {
|
|
3
|
+
MissingReport,
|
|
4
|
+
MissingRequirementsByLocale,
|
|
5
|
+
ValidateOptions,
|
|
6
|
+
} from "./types";
|
|
7
|
+
import { features } from "../../constants";
|
|
8
|
+
import { discoverConfigs, readSchema } from "../../core";
|
|
3
9
|
import { collectOtherLocaleMessages } from "../../core";
|
|
4
|
-
import {
|
|
5
|
-
import { spinner } from "../spinner";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
type ValidationResult,
|
|
10
|
-
} from "./validate-locale-messages";
|
|
11
|
-
|
|
12
|
-
export interface ValidateOptions {
|
|
13
|
-
exts?: Array<ExtraExt>;
|
|
14
|
-
customReaders?: Record<string, string>;
|
|
15
|
-
debug?: boolean;
|
|
16
|
-
}
|
|
10
|
+
import { renderMissingConfigSchema, renderTitle } from "../../render";
|
|
11
|
+
import { spinner } from "../shared/spinner";
|
|
12
|
+
import { writeJsonReport } from "../shared/write-json-report";
|
|
13
|
+
import { collectMissingRequirements } from "./missing/collect-missing-requirements";
|
|
14
|
+
import { renderConfigSummary } from "./render-config-summary";
|
|
17
15
|
|
|
18
16
|
export async function validate({
|
|
19
17
|
exts = [],
|
|
20
18
|
customReaders,
|
|
19
|
+
format = "human",
|
|
20
|
+
output,
|
|
21
21
|
debug,
|
|
22
22
|
}: ValidateOptions) {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const isHuman = format === "human";
|
|
24
|
+
renderTitle(features.validate.title, isHuman);
|
|
25
25
|
|
|
26
26
|
// -----------------------------------------------------------------------
|
|
27
27
|
// Discover configs from the current workspace
|
|
28
28
|
// -----------------------------------------------------------------------
|
|
29
29
|
const configEntries = await discoverConfigs(debug);
|
|
30
|
-
if (configEntries.length === 0)
|
|
31
|
-
spinner.stop();
|
|
32
|
-
throw new Error("No Intor config found.");
|
|
33
|
-
}
|
|
30
|
+
if (configEntries.length === 0) throw new Error("No Intor config found.");
|
|
34
31
|
|
|
35
32
|
try {
|
|
36
|
-
//
|
|
33
|
+
// ---------------------------------------------------------------------
|
|
37
34
|
// Read generated schema
|
|
38
|
-
//
|
|
35
|
+
// ---------------------------------------------------------------------
|
|
39
36
|
const schema = await readSchema();
|
|
40
|
-
|
|
41
|
-
const resultsByConfig: Record<
|
|
42
|
-
string,
|
|
43
|
-
Record<string, ValidationResult>
|
|
44
|
-
> = {};
|
|
37
|
+
const report: MissingReport = {};
|
|
45
38
|
|
|
46
39
|
// Per-config processing
|
|
47
40
|
for (const { config } of configEntries) {
|
|
48
|
-
const schemaConfig = schema.
|
|
41
|
+
const schemaConfig = schema.entries.find((c) => c.id === config.id);
|
|
49
42
|
if (!schemaConfig) {
|
|
50
|
-
|
|
51
|
-
printMissingSchema(config.id);
|
|
52
|
-
spinner.start();
|
|
43
|
+
renderMissingConfigSchema(config.id, isHuman);
|
|
53
44
|
continue;
|
|
54
45
|
}
|
|
46
|
+
const { shapes } = schemaConfig;
|
|
55
47
|
|
|
48
|
+
// -------------------------------------------------------------------
|
|
56
49
|
// Load all non-default locale messages
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
// -------------------------------------------------------------------
|
|
51
|
+
if (isHuman) spinner.start();
|
|
52
|
+
const localeMessages = await collectOtherLocaleMessages(config, {
|
|
59
53
|
exts,
|
|
60
54
|
customReaders,
|
|
61
|
-
);
|
|
55
|
+
});
|
|
56
|
+
if (isHuman) spinner.stop();
|
|
62
57
|
|
|
63
|
-
|
|
58
|
+
// -------------------------------------------------------------------
|
|
59
|
+
// Collect missing requirements per locale
|
|
60
|
+
// -------------------------------------------------------------------
|
|
61
|
+
const missingByLocale: MissingRequirementsByLocale = {};
|
|
64
62
|
for (const locale of config.supportedLocales) {
|
|
65
63
|
if (locale === config.defaultLocale) continue;
|
|
66
64
|
const messages = localeMessages[locale];
|
|
67
65
|
if (!messages) continue;
|
|
68
|
-
|
|
69
|
-
schemaConfig.schemas,
|
|
70
|
-
messages,
|
|
71
|
-
);
|
|
66
|
+
missingByLocale[locale] = collectMissingRequirements(shapes, messages);
|
|
72
67
|
}
|
|
73
68
|
|
|
74
|
-
|
|
69
|
+
report[config.id] = missingByLocale;
|
|
70
|
+
renderConfigSummary(config.id, missingByLocale, isHuman);
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
printSummary(resultsByConfig);
|
|
73
|
+
if (format === "json") await writeJsonReport(report, output);
|
|
79
74
|
} catch (error) {
|
|
80
|
-
spinner.stop();
|
|
75
|
+
if (isHuman) spinner.stop();
|
|
81
76
|
console.error(error instanceof Error ? error.message : String(error));
|
|
82
77
|
process.exit(1);
|
|
83
78
|
}
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { dim, green, red, yellow } from "./render";
|
|
2
|
+
|
|
3
|
+
type Logger = {
|
|
4
|
+
header: (message?: string, options?: LogOptions) => void;
|
|
5
|
+
log: (message?: string, options?: LogOptions) => void;
|
|
6
|
+
footer: (message?: string, options?: LogOptions) => void;
|
|
7
|
+
process: (tag: ProcessTag, message: string) => void;
|
|
8
|
+
ok: (message?: string) => void;
|
|
9
|
+
error: (message?: string) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const noop = () => {};
|
|
13
|
+
const noopLogger: Logger = {
|
|
14
|
+
header: noop,
|
|
15
|
+
log: noop,
|
|
16
|
+
footer: noop,
|
|
17
|
+
process: noop,
|
|
18
|
+
ok: noop,
|
|
19
|
+
error: noop,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type LogOptions = {
|
|
23
|
+
prefix?: string;
|
|
24
|
+
kind?: "process";
|
|
25
|
+
lineBreakBefore?: number;
|
|
26
|
+
lineBreakAfter?: number;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const SPACER = dim("│ ");
|
|
30
|
+
const log = console.log;
|
|
31
|
+
|
|
32
|
+
export function createLogger(enabled = true): Logger {
|
|
33
|
+
if (!enabled) return noopLogger;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
header: (message = "", options: LogOptions = {}) => {
|
|
37
|
+
const prefix = options.prefix ?? "";
|
|
38
|
+
const symbol = options.kind === "process" ? dim("○ ") : "";
|
|
39
|
+
const before = options.lineBreakBefore ?? 0;
|
|
40
|
+
const after = options.lineBreakAfter ?? 0;
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < before; i++) log(SPACER);
|
|
43
|
+
log(prefix + dim("┌─ ") + symbol + message);
|
|
44
|
+
for (let i = 0; i < after; i++) log(SPACER);
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
log: (message = "", options: LogOptions = {}) => {
|
|
48
|
+
const prefix = options.prefix ?? "";
|
|
49
|
+
log(prefix + SPACER + message);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
footer: (message = "", options: LogOptions = {}) => {
|
|
53
|
+
const prefix = options.prefix ?? "";
|
|
54
|
+
const symbol = options.kind === "process" ? dim("● ") : "";
|
|
55
|
+
const before = options.lineBreakBefore ?? 0;
|
|
56
|
+
const after = options.lineBreakAfter ?? 0;
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < before; i++) log(SPACER);
|
|
59
|
+
log(prefix + dim("└─ ") + symbol + message);
|
|
60
|
+
for (let i = 0; i < after; i++) log(SPACER);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
process: (tag: ProcessTag, message: string) => {
|
|
64
|
+
log(...formatProcessLog(tag, message));
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
ok: (message = "") => {
|
|
68
|
+
log(dim("╶─ ") + green("✔ ") + message);
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
error: (message = "") => {
|
|
72
|
+
log(dim("╶─ ") + red("✖ ") + message);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Format process log
|
|
78
|
+
type ProcessTag = "ok" | "warn" | "skip" | "load" | string;
|
|
79
|
+
type TagStyle = {
|
|
80
|
+
color?: (s: string) => string;
|
|
81
|
+
dimMessage?: boolean;
|
|
82
|
+
};
|
|
83
|
+
const TAG_STYLES: Record<string, TagStyle> = {
|
|
84
|
+
ok: { color: green },
|
|
85
|
+
warn: { color: yellow },
|
|
86
|
+
skip: { color: dim, dimMessage: true },
|
|
87
|
+
load: {},
|
|
88
|
+
};
|
|
89
|
+
const formatProcessLog = (tag: ProcessTag, message: string) => {
|
|
90
|
+
const style = TAG_STYLES[tag] ?? {};
|
|
91
|
+
const paddedTag = tag.padEnd(4, " ");
|
|
92
|
+
const finalTag = style.color ? style.color(paddedTag) : paddedTag;
|
|
93
|
+
const finalMessage = style.dimMessage ? dim(message) : message;
|
|
94
|
+
return [SPACER + dim("│ ") + finalTag + dim(" │ ") + finalMessage];
|
|
95
|
+
};
|
package/src/render.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ConfigEntry } from "./core";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import { toRelativePath } from "./features/shared/to-relative-path";
|
|
4
|
+
import { createLogger } from "./logger";
|
|
5
|
+
|
|
6
|
+
// Formatting utilities
|
|
7
|
+
export const dim = pc.dim;
|
|
8
|
+
export const cyan = pc.cyan;
|
|
9
|
+
export const green = pc.green;
|
|
10
|
+
export const bold = pc.bold;
|
|
11
|
+
export const italic = pc.italic;
|
|
12
|
+
export const gray = pc.gray;
|
|
13
|
+
export const yellow = pc.yellow;
|
|
14
|
+
export const red = pc.red;
|
|
15
|
+
export const bgBlack = pc.bgBlack;
|
|
16
|
+
|
|
17
|
+
// Layout helpers
|
|
18
|
+
export function br(count = 1, enabled = true) {
|
|
19
|
+
if (!enabled) return;
|
|
20
|
+
for (let i = 0; i < count; i++) console.log();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Render feature title
|
|
24
|
+
export function renderTitle(title = "", enabled = true) {
|
|
25
|
+
if (!enabled) return;
|
|
26
|
+
console.log(" " + bgBlack(` ${title} `));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Render discovered Intor configs
|
|
30
|
+
export function renderConfigs(configEntries: ConfigEntry[], enabled = true) {
|
|
31
|
+
if (!enabled) return;
|
|
32
|
+
const logger = createLogger();
|
|
33
|
+
|
|
34
|
+
// Log header
|
|
35
|
+
logger.header(`Discovered ${yellow(configEntries.length)} Intor config(s):`, {
|
|
36
|
+
lineBreakAfter: 1,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
for (const { filePath, config } of configEntries) {
|
|
40
|
+
logger.log(`${cyan(config.id)} ${dim(`⚲ ${toRelativePath(filePath)}`)}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Log footer
|
|
44
|
+
logger.footer("", { lineBreakBefore: 1 });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Render schema-not-found message for config
|
|
48
|
+
export function renderMissingConfigSchema(configId: string, enabled = true) {
|
|
49
|
+
if (!enabled) return;
|
|
50
|
+
const logger = createLogger();
|
|
51
|
+
|
|
52
|
+
br();
|
|
53
|
+
logger.error(
|
|
54
|
+
cyan(configId) +
|
|
55
|
+
`: schema not found, run ${italic("intor generate")} and retry`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { BuildInput, Schema } from "../types";
|
|
2
|
-
|
|
3
|
-
export function buildSchemas(inputs: BuildInput[]): Schema {
|
|
4
|
-
return {
|
|
5
|
-
version: 1,
|
|
6
|
-
generatedAt: new Date().toISOString(),
|
|
7
|
-
configs: inputs.map((input) => ({
|
|
8
|
-
id: input.id,
|
|
9
|
-
locales: input.locales,
|
|
10
|
-
schemas: input.schemas,
|
|
11
|
-
})),
|
|
12
|
-
};
|
|
13
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { buildSchemas } from "./build-schemas";
|
package/src/build/index.ts
DELETED
package/src/build/types.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { InferredSchemas } from "../core";
|
|
2
|
-
|
|
3
|
-
export interface BuildInput {
|
|
4
|
-
id: string;
|
|
5
|
-
locales: readonly string[];
|
|
6
|
-
schemas: InferredSchemas;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// Schema
|
|
10
|
-
export interface Schema {
|
|
11
|
-
version: number;
|
|
12
|
-
generatedAt: string;
|
|
13
|
-
configs: SchemaConfig[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface SchemaConfig {
|
|
17
|
-
id: string;
|
|
18
|
-
locales: readonly string[];
|
|
19
|
-
schemas: InferredSchemas;
|
|
20
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/* eslint-disable unicorn/no-array-sort */
|
|
2
|
-
import type { MessageObject } from "intor";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { DEFAULT_OUT_DIR, DEFAULT_MESSAGES_SNAPSHOT_DIR } from "../constants";
|
|
5
|
-
import { ensureAndWrite } from "./ensure-and-write";
|
|
6
|
-
|
|
7
|
-
export async function writeMessagesSnapshot(
|
|
8
|
-
localeMessages: Record<string, MessageObject>,
|
|
9
|
-
): Promise<string[]> {
|
|
10
|
-
const filePaths: string[] = [];
|
|
11
|
-
|
|
12
|
-
for (const locale of Object.keys(localeMessages).sort()) {
|
|
13
|
-
const messages = localeMessages[locale];
|
|
14
|
-
|
|
15
|
-
const filePath = path.join(
|
|
16
|
-
DEFAULT_OUT_DIR,
|
|
17
|
-
DEFAULT_MESSAGES_SNAPSHOT_DIR,
|
|
18
|
-
`${locale}.json`,
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
await ensureAndWrite(filePath, JSON.stringify(messages, null, 2));
|
|
22
|
-
|
|
23
|
-
filePaths.push(filePath);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return filePaths;
|
|
27
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Schema } from "../../build";
|
|
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(schema: Schema): Promise<string> {
|
|
7
|
-
const filePath = path.join(DEFAULT_OUT_DIR, DEFAULT_SCHEMA_FILE_NAME);
|
|
8
|
-
return await ensureAndWrite(filePath, JSON.stringify(schema, null, 2));
|
|
9
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
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(types: string): Promise<string> {
|
|
6
|
-
const filePath = path.join(DEFAULT_OUT_DIR, DEFAULT_TYPES_FILE_NAME);
|
|
7
|
-
return await ensureAndWrite(filePath, types);
|
|
8
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { InferredSchemas } from "./types";
|
|
2
|
-
import type { MessageObject } from "intor";
|
|
3
|
-
import { inferMessagesSchema } from "./messages";
|
|
4
|
-
import { inferReplacementsSchema } from "./replacements";
|
|
5
|
-
import { inferRichSchema } from "./rich";
|
|
6
|
-
import { stripInternalKeys } from "./utils/strip-internal-keys";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Infer all semantic schemas from messages.
|
|
10
|
-
*/
|
|
11
|
-
export function inferSchemas(messages: MessageObject): InferredSchemas {
|
|
12
|
-
const schemas = {
|
|
13
|
-
messagesSchema: inferMessagesSchema(messages),
|
|
14
|
-
replacementsSchema: inferReplacementsSchema(messages),
|
|
15
|
-
richSchema: inferRichSchema(messages),
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
stripInternalKeys(schemas);
|
|
19
|
-
return schemas;
|
|
20
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { inferMessagesSchema } from "./infer-messages-schema";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { inferReplacementsSchema } from "./infer-replacements-schema";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { inferRichSchema } from "./infer-rich-schema";
|