intor-cli 0.0.9 → 0.0.11
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/README.md +24 -8
- package/package.json +4 -4
- package/src/build/build-schemas/build-schemas.ts +2 -2
- package/src/build/build-types/build-types.ts +4 -1
- package/src/build/build-types/utils/normalize-rich-infer-node.ts +40 -0
- package/src/build/build-types/utils/render-infer-node.ts +4 -2
- package/src/build/index.ts +1 -1
- package/src/build/types.ts +4 -3
- package/src/cli/commands/check.ts +9 -26
- package/src/cli/commands/generate.ts +2 -13
- package/src/cli/commands/utils/normalize-reader-options.ts +46 -0
- package/src/cli/commands/validate.ts +50 -0
- package/src/cli/index.ts +2 -0
- package/src/core/collect-messages/collect-other-locale-messages.ts +27 -0
- package/src/core/collect-messages/index.ts +1 -0
- package/src/core/constants/generated.ts +5 -0
- package/src/core/constants/index.ts +4 -3
- package/src/core/extract-usages/index.ts +1 -1
- package/src/core/generated/ensure-and-write.ts +11 -0
- package/src/core/generated/index.ts +6 -0
- package/src/core/generated/read-schema.ts +33 -0
- package/src/core/generated/write-messages-snapshot.ts +27 -0
- package/src/core/generated/write-schema.ts +9 -0
- package/src/core/generated/write-types.ts +8 -0
- package/src/core/index.ts +36 -9
- package/src/core/infer-schema/index.ts +2 -1
- package/src/core/infer-schema/infer-schemas.ts +5 -1
- package/src/core/infer-schema/messages/infer-messages-schema.ts +1 -1
- package/src/core/infer-schema/replacements/infer-replacements-schema.ts +1 -1
- package/src/core/infer-schema/rich/infer-rich-schema.ts +1 -1
- package/src/core/infer-schema/utils/infer-object.ts +8 -6
- package/src/core/infer-schema/utils/should-skip-key.ts +15 -0
- package/src/core/infer-schema/utils/strip-internal-keys.ts +30 -0
- package/src/features/check/check.ts +7 -15
- package/src/{core → features/check}/diagnostics/collect.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/enforce-missing-replacements.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/enforce-missing-rich.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/key/empty.ts +1 -1
- package/src/{core → features/check}/diagnostics/rules/key/not-found.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/pre-key/not-found.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/replacement/missing.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/replacement/not-allowed.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/replacement/unused.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/rich/missing.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/rich/not-allowed.ts +1 -2
- package/src/{core → features/check}/diagnostics/rules/rich/unused.ts +1 -2
- package/src/{core → features/check}/diagnostics/types.ts +1 -1
- package/src/{core → features/check}/diagnostics/utils/get-schema-node-at-path.ts +1 -1
- package/src/{core → features/check}/diagnostics/utils/index-usages-by-key.ts +1 -1
- package/src/features/check/print-summary.ts +15 -15
- package/src/features/generate/generate.ts +16 -8
- package/src/features/generate/print-configs.ts +3 -3
- package/src/features/generate/print-overrides.ts +44 -26
- package/src/features/generate/print-summary.ts +21 -13
- package/src/features/index.ts +1 -0
- package/src/features/print.ts +43 -0
- package/src/features/validate/index.ts +1 -0
- package/src/features/validate/messages/index.ts +1 -0
- package/src/features/validate/messages/validate-messages-schema.ts +36 -0
- package/src/features/validate/print-summary.ts +65 -0
- package/src/features/validate/replacements/index.ts +1 -0
- package/src/features/validate/replacements/validate-replacements-schema.ts +51 -0
- package/src/features/validate/rich/index.ts +1 -0
- package/src/features/validate/rich/validate-rich-schema.ts +48 -0
- package/src/features/validate/validate-locale-messages.ts +38 -0
- package/src/features/validate/validate.ts +84 -0
- package/src/core/constants/generated-files.ts +0 -3
- package/src/core/read-generated-schema.ts +0 -42
- package/src/core/write-generated-files.ts +0 -51
- package/src/features/print-title.ts +0 -7
- /package/src/{core → features/check}/diagnostics/group.ts +0 -0
- /package/src/{core → features/check}/diagnostics/index.ts +0 -0
- /package/src/{core → features/check}/diagnostics/messages.ts +0 -0
- /package/src/{core → features/check}/diagnostics/rules/key/index.ts +0 -0
- /package/src/{core → features/check}/diagnostics/rules/pre-key/index.ts +0 -0
- /package/src/{core → features/check}/diagnostics/rules/replacement/index.ts +0 -0
- /package/src/{core → features/check}/diagnostics/rules/rich/index.ts +0 -0
- /package/src/{core → features/check}/diagnostics/utils/resolve-key-path.ts +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { InferNode } from "../types";
|
|
2
2
|
import type { MessageObject, MessageValue } from "intor";
|
|
3
|
+
import { shouldSkipKey } from "./should-skip-key";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Infer an object-like semantic node by aggregating inferred children.
|
|
@@ -8,13 +9,17 @@ import type { MessageObject, MessageValue } from "intor";
|
|
|
8
9
|
* - Prunes branches without semantic meaning
|
|
9
10
|
* - Returns `none` if no children remain
|
|
10
11
|
*/
|
|
12
|
+
|
|
11
13
|
export function inferObject(
|
|
12
14
|
value: MessageObject,
|
|
13
15
|
inferChild: (value: MessageValue) => InferNode,
|
|
16
|
+
mode: "messages" | "replacements" | "rich",
|
|
14
17
|
): InferNode {
|
|
15
18
|
const properties: Record<string, InferNode> = {};
|
|
16
19
|
|
|
17
20
|
for (const [key, val] of Object.entries(value)) {
|
|
21
|
+
if (shouldSkipKey(key, mode)) continue;
|
|
22
|
+
|
|
18
23
|
const child = inferChild(val);
|
|
19
24
|
|
|
20
25
|
// Skip branches without semantic meaning
|
|
@@ -23,10 +28,7 @@ export function inferObject(
|
|
|
23
28
|
properties[key] = child;
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return { kind: "object", properties };
|
|
31
|
+
return Object.keys(properties).length === 0
|
|
32
|
+
? { kind: "none" }
|
|
33
|
+
: { kind: "object", properties };
|
|
32
34
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determine whether a message object key should be ignored during schema inference.
|
|
3
|
+
*
|
|
4
|
+
* - Internal Intor metadata keys (`__intor_*`) are never part of semantic schemas
|
|
5
|
+
* - Markdown payload (`content`) is only meaningful in Messages schema,
|
|
6
|
+
* and must be excluded from Replacements / Rich inference
|
|
7
|
+
*/
|
|
8
|
+
export function shouldSkipKey(
|
|
9
|
+
key: string,
|
|
10
|
+
mode: "messages" | "replacements" | "rich",
|
|
11
|
+
) {
|
|
12
|
+
if (key.startsWith("__intor_")) return true;
|
|
13
|
+
if (mode !== "messages" && key === "content") return true;
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { INTOR_PREFIX } from "intor";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Remove internal Intor keys from inferred schema objects.
|
|
5
|
+
*
|
|
6
|
+
* This runs after schema inference to avoid leaking
|
|
7
|
+
* internal metadata into generated types.
|
|
8
|
+
*/
|
|
9
|
+
export function stripInternalKeys(target: unknown): void {
|
|
10
|
+
if (!target || typeof target !== "object") return;
|
|
11
|
+
|
|
12
|
+
// Handle arrays explicitly
|
|
13
|
+
if (Array.isArray(target)) {
|
|
14
|
+
for (const item of target) {
|
|
15
|
+
stripInternalKeys(item);
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const record = target as Record<string, unknown>;
|
|
21
|
+
|
|
22
|
+
for (const key of Object.keys(record)) {
|
|
23
|
+
if (key.startsWith(INTOR_PREFIX)) {
|
|
24
|
+
delete record[key];
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
stripInternalKeys(record[key]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
/* eslint-disable unicorn/no-process-exit */
|
|
2
|
-
import type {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
readGeneratedSchema,
|
|
6
|
-
extractUsages,
|
|
7
|
-
collectDiagnostics,
|
|
8
|
-
groupDiagnostics,
|
|
9
|
-
} from "../../core";
|
|
10
|
-
import { printTitle } from "../../features/print-title";
|
|
2
|
+
import type { ExtractUsagesOptions } from "../../core";
|
|
3
|
+
import { extractUsages, readSchema } from "../../core";
|
|
4
|
+
import { printTitle } from "../print";
|
|
11
5
|
import { spinner } from "../spinner";
|
|
12
6
|
import { dedupePreKeyUsages } from "./dedupe-pre-key-usages";
|
|
7
|
+
import { collectDiagnostics, groupDiagnostics } from "./diagnostics";
|
|
13
8
|
import { printSummary } from "./print-summary";
|
|
14
9
|
|
|
15
10
|
function resolveConfigKey(
|
|
@@ -21,18 +16,15 @@ function resolveConfigKey(
|
|
|
21
16
|
return usageConfigKey;
|
|
22
17
|
}
|
|
23
18
|
|
|
24
|
-
export async function check(
|
|
25
|
-
|
|
26
|
-
extractOptions?: ExtractUsagesOptions,
|
|
27
|
-
) {
|
|
28
|
-
printTitle("Checking intor diagnostics");
|
|
19
|
+
export async function check(extractOptions?: ExtractUsagesOptions) {
|
|
20
|
+
printTitle("Checking intor usages");
|
|
29
21
|
spinner.start();
|
|
30
22
|
|
|
31
23
|
try {
|
|
32
24
|
// -----------------------------------------------------------------------
|
|
33
25
|
// Read generated schema
|
|
34
26
|
// -----------------------------------------------------------------------
|
|
35
|
-
const generatedSchema = await
|
|
27
|
+
const generatedSchema = await readSchema();
|
|
36
28
|
|
|
37
29
|
// -----------------------------------------------------------------------
|
|
38
30
|
// Extract usages
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Diagnostic } from "./types";
|
|
2
|
-
import type { ExtractedUsages } from "
|
|
3
|
-
import type { InferredSchemas } from "../infer-schema/types";
|
|
2
|
+
import type { ExtractedUsages, InferredSchemas } from "../../../core";
|
|
4
3
|
import { enforceMissingReplacements } from "./rules/enforce-missing-replacements";
|
|
5
4
|
import { enforceMissingRich } from "./rules/enforce-missing-rich";
|
|
6
5
|
import { keyEmpty, keyNotFound } from "./rules/key";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { KeyUsage, ReplacementUsage } from "
|
|
2
|
-
import type { InferNode } from "../../infer-schema";
|
|
1
|
+
import type { KeyUsage, ReplacementUsage, InferNode } from "../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../utils/get-schema-node-at-path";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { KeyUsage, RichUsage } from "
|
|
2
|
-
import type { InferNode } from "../../infer-schema";
|
|
1
|
+
import type { KeyUsage, RichUsage, InferNode } from "../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../utils/get-schema-node-at-path";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { KeyUsage } from "
|
|
2
|
-
import type { InferNode } from "../../../infer-schema";
|
|
1
|
+
import type { KeyUsage, InferNode } from "../../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../../utils/get-schema-node-at-path";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { PreKeyUsage } from "
|
|
2
|
-
import type { InferNode } from "../../../infer-schema";
|
|
1
|
+
import type { PreKeyUsage, InferNode } from "../../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../../utils/get-schema-node-at-path";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { ReplacementUsage } from "
|
|
2
|
-
import type { InferNode } from "../../../infer-schema";
|
|
1
|
+
import type { ReplacementUsage, InferNode } from "../../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../../utils/get-schema-node-at-path";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { ReplacementUsage } from "
|
|
2
|
-
import type { InferNode } from "../../../infer-schema";
|
|
1
|
+
import type { ReplacementUsage, InferNode } from "../../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../../utils/get-schema-node-at-path";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { ReplacementUsage } from "
|
|
2
|
-
import type { InferNode } from "../../../infer-schema";
|
|
1
|
+
import type { ReplacementUsage, InferNode } from "../../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../../utils/get-schema-node-at-path";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { RichUsage } from "
|
|
2
|
-
import type { InferNode } from "../../../infer-schema";
|
|
1
|
+
import type { RichUsage, InferNode } from "../../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../../utils/get-schema-node-at-path";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { RichUsage } from "
|
|
2
|
-
import type { InferNode } from "../../../infer-schema";
|
|
1
|
+
import type { RichUsage, InferNode } from "../../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../../utils/get-schema-node-at-path";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { RichUsage } from "
|
|
2
|
-
import type { InferNode } from "../../../infer-schema";
|
|
1
|
+
import type { RichUsage, InferNode } from "../../../../../core";
|
|
3
2
|
import type { Diagnostic } from "../../types";
|
|
4
3
|
import { DIAGNOSTIC_MESSAGES } from "../../messages";
|
|
5
4
|
import { getSchemaNodeAtPath } from "../../utils/get-schema-node-at-path";
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import type { DiagnosticGroup } from "
|
|
2
|
-
import
|
|
1
|
+
import type { DiagnosticGroup } from "./diagnostics/types";
|
|
2
|
+
import { br, dim, printList, cyan, print, gray } from "../print";
|
|
3
3
|
|
|
4
4
|
export function printSummary(configId: string, grouped: DiagnosticGroup[]) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
print(`${dim("Config:")} ${cyan(configId)}`);
|
|
6
|
+
br();
|
|
7
7
|
|
|
8
|
-
//
|
|
8
|
+
// No issues
|
|
9
9
|
if (grouped.length === 0) {
|
|
10
|
-
|
|
10
|
+
print(dim("✔ All usages are valid\n"), 1);
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
//
|
|
14
|
+
// Problems
|
|
15
15
|
for (const group of grouped) {
|
|
16
16
|
const { factory, method, messageKey, problems, file, lines } = group;
|
|
17
17
|
|
|
18
|
-
const header = `${messageKey}
|
|
18
|
+
const header = `${messageKey} (${method ?? factory})`;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
print(header, 1);
|
|
21
|
+
printList(
|
|
22
|
+
null,
|
|
23
|
+
problems.map((p) => gray(p)),
|
|
24
|
+
1,
|
|
25
|
+
);
|
|
26
|
+
print(dim(` ⚲ ${file}:${lines.join(",")}\n`), 1);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/* eslint-disable unicorn/no-process-exit */
|
|
2
2
|
import type { ExtraExt } from "../../core";
|
|
3
|
+
import path from "node:path";
|
|
3
4
|
import { buildTypes, buildSchemas, type BuildInput } from "../../build";
|
|
4
5
|
import {
|
|
5
6
|
discoverConfigs,
|
|
6
7
|
collectRuntimeMessages,
|
|
7
8
|
inferSchemas,
|
|
8
|
-
|
|
9
|
+
writeTypes,
|
|
10
|
+
writeSchema,
|
|
11
|
+
DEFAULT_OUT_DIR,
|
|
9
12
|
} from "../../core";
|
|
10
|
-
import { printTitle } from "../print
|
|
13
|
+
import { cyan, dim, print, printTitle } from "../print";
|
|
11
14
|
import { spinner } from "../spinner";
|
|
12
|
-
import { printConfigs } from "./print-configs";
|
|
13
15
|
import { printOverrides } from "./print-overrides";
|
|
14
16
|
import { printSummary } from "./print-summary";
|
|
15
17
|
|
|
@@ -35,7 +37,7 @@ export async function generate({
|
|
|
35
37
|
const configEntries = await discoverConfigs(debug);
|
|
36
38
|
if (configEntries.length === 0) {
|
|
37
39
|
spinner.stop();
|
|
38
|
-
throw new Error("No Intor config found
|
|
40
|
+
throw new Error("No Intor config found.");
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
// -----------------------------------------------------------------------
|
|
@@ -46,13 +48,18 @@ export async function generate({
|
|
|
46
48
|
// Runtime mode - Per-config processing
|
|
47
49
|
for (const { config, filePath } of configEntries) {
|
|
48
50
|
const { id, supportedLocales: locales } = config;
|
|
49
|
-
|
|
51
|
+
|
|
52
|
+
spinner.stop();
|
|
53
|
+
print(`${dim("Config:")} ${cyan(config.id)} ${dim(`⚲ ${filePath}`)}`);
|
|
54
|
+
spinner.start();
|
|
55
|
+
|
|
50
56
|
const { messages, overrides } = await collectRuntimeMessages(
|
|
51
57
|
config,
|
|
52
58
|
config.defaultLocale,
|
|
53
59
|
exts,
|
|
54
60
|
customReaders,
|
|
55
61
|
);
|
|
62
|
+
|
|
56
63
|
printOverrides(overrides);
|
|
57
64
|
const schemas = inferSchemas(messages[config.defaultLocale]);
|
|
58
65
|
buildInputs.push({ id, locales, schemas });
|
|
@@ -64,11 +71,12 @@ export async function generate({
|
|
|
64
71
|
const types = buildTypes(buildInputs);
|
|
65
72
|
const schema = buildSchemas(buildInputs);
|
|
66
73
|
|
|
67
|
-
// Write
|
|
68
|
-
|
|
74
|
+
// Write files
|
|
75
|
+
await writeTypes(types);
|
|
76
|
+
await writeSchema(schema);
|
|
69
77
|
|
|
70
78
|
spinner.stop();
|
|
71
|
-
printSummary(
|
|
79
|
+
printSummary(path.resolve(DEFAULT_OUT_DIR), performance.now() - start);
|
|
72
80
|
} catch (error) {
|
|
73
81
|
spinner.stop();
|
|
74
82
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { cyan, dim, print } from "../print";
|
|
2
2
|
import { spinner } from "../spinner";
|
|
3
3
|
|
|
4
|
-
export function printConfigs(
|
|
4
|
+
export function printConfigs(configId: string, filePath: string) {
|
|
5
5
|
spinner.stop();
|
|
6
|
-
|
|
6
|
+
print(`${dim("Config:")} ${cyan(configId)} ⚲ ${filePath}`);
|
|
7
7
|
spinner.start();
|
|
8
8
|
}
|
|
@@ -1,44 +1,62 @@
|
|
|
1
1
|
import type { MergeOverrides } from "../../core/collect-messages/types";
|
|
2
|
-
import
|
|
2
|
+
import { dim, gray, print, br } from "../print";
|
|
3
3
|
import { spinner } from "../spinner";
|
|
4
4
|
|
|
5
5
|
export function printOverrides(overrides: MergeOverrides[]) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
// Group overrides by layer
|
|
7
|
+
const grouped = groupOverrides(overrides);
|
|
8
|
+
|
|
9
|
+
const hasAny =
|
|
10
|
+
grouped.client_over_server.length > 0 ||
|
|
11
|
+
grouped.runtime_over_static.length > 0;
|
|
12
|
+
if (!hasAny) return;
|
|
12
13
|
|
|
13
14
|
spinner.stop();
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
// Print title
|
|
17
|
+
print(dim("Overrides:"), 1);
|
|
16
18
|
|
|
17
|
-
// Print
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
// Print layer group
|
|
20
|
+
printLayerGroup("client > server", grouped.client_over_server);
|
|
21
|
+
printLayerGroup("runtime > static", grouped.runtime_over_static);
|
|
22
|
+
|
|
23
|
+
br();
|
|
24
|
+
spinner.start();
|
|
25
|
+
}
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
function groupOverrides(overrides: MergeOverrides[]) {
|
|
28
|
+
const groups: Record<MergeOverrides["layer"], MergeOverrides[]> = {
|
|
29
|
+
client_over_server: [],
|
|
30
|
+
runtime_over_static: [],
|
|
31
|
+
};
|
|
32
|
+
for (const o of overrides) {
|
|
33
|
+
if (o.kind !== "override") continue;
|
|
34
|
+
if (typeof o.prev !== "string" && typeof o.next !== "string") continue;
|
|
35
|
+
groups[o.layer].push(o);
|
|
25
36
|
}
|
|
37
|
+
return groups;
|
|
38
|
+
}
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
function printLayerGroup(label: string, items: MergeOverrides[]) {
|
|
41
|
+
if (items.length === 0) return;
|
|
42
|
+
print(dim(label), 2);
|
|
43
|
+
for (const { path, prev, next } of items) {
|
|
44
|
+
print(`- ${path} ${formatDiff(prev, next)}`, 3);
|
|
45
|
+
}
|
|
29
46
|
}
|
|
30
47
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
const MAX_PREVIEW_LENGTH = 16;
|
|
49
|
+
function truncate(value: unknown, max = MAX_PREVIEW_LENGTH): string {
|
|
50
|
+
if (typeof value !== "string") return "";
|
|
51
|
+
if (value.length <= max) return value;
|
|
52
|
+
return value.slice(0, max) + "…";
|
|
34
53
|
}
|
|
35
54
|
|
|
36
|
-
function formatDiff(prev: unknown, next: unknown)
|
|
37
|
-
if (typeof prev !== "string" && typeof next !== "string") return;
|
|
55
|
+
function formatDiff(prev: unknown, next: unknown) {
|
|
38
56
|
return (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
dim("| Prev: ") +
|
|
58
|
+
gray(truncate(prev)) +
|
|
59
|
+
dim(" → Next: ") +
|
|
60
|
+
gray(truncate(next))
|
|
43
61
|
);
|
|
44
62
|
}
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { bold, br, dim, gray, green, italic, print } from "../print";
|
|
2
2
|
|
|
3
|
-
const label = (text: string) =>
|
|
3
|
+
const label = (text: string) => dim(text.padEnd(18));
|
|
4
4
|
|
|
5
5
|
export function printSummary(outDir: string, ms: number) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
br();
|
|
7
|
+
|
|
8
|
+
print(green(bold("✔ intor generate completed")));
|
|
9
|
+
|
|
10
|
+
br();
|
|
11
|
+
|
|
12
|
+
print(label("Output directory: ") + gray(outDir));
|
|
13
|
+
print(label("Time elapsed: ") + gray(`${(ms / 1000).toFixed(2)}s`));
|
|
14
|
+
|
|
15
|
+
br();
|
|
16
|
+
|
|
17
|
+
print(
|
|
18
|
+
italic(
|
|
19
|
+
dim("💡 Remember to include ") +
|
|
20
|
+
gray(".intor/**/*.d.ts") +
|
|
21
|
+
dim(" in your tsconfig.json "),
|
|
22
|
+
),
|
|
16
23
|
);
|
|
17
|
-
|
|
24
|
+
|
|
25
|
+
br();
|
|
18
26
|
}
|
package/src/features/index.ts
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
|
|
3
|
+
export const dim = pc.dim;
|
|
4
|
+
export const cyan = pc.cyan;
|
|
5
|
+
export const green = pc.green;
|
|
6
|
+
export const bold = pc.bold;
|
|
7
|
+
export const italic = pc.italic;
|
|
8
|
+
export const gray = pc.gray;
|
|
9
|
+
|
|
10
|
+
const INDENT = " ";
|
|
11
|
+
|
|
12
|
+
export function print(text = "", indentLevel = 0) {
|
|
13
|
+
if (!text) return;
|
|
14
|
+
console.log(INDENT.repeat(indentLevel) + text);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function br(count = 1) {
|
|
18
|
+
for (let i = 0; i < count; i++) console.log();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function printTitle(title: string) {
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(pc.bgBlack(` • ${title} `));
|
|
24
|
+
console.log();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function printList(
|
|
28
|
+
title: string | null,
|
|
29
|
+
items: readonly string[],
|
|
30
|
+
indentLevel = 0,
|
|
31
|
+
) {
|
|
32
|
+
if (items.length === 0) return;
|
|
33
|
+
if (title) print(title, indentLevel);
|
|
34
|
+
for (const value of items) {
|
|
35
|
+
print(`- ${value}`, indentLevel + 1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function printMissingSchema(configId: string) {
|
|
40
|
+
print(`${dim("Config:")} ${cyan(configId)}`);
|
|
41
|
+
print(dim("✖ Missing schema: run `intor generate` and retry"), 1);
|
|
42
|
+
br();
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validate } from "./validate";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validateMessagesSchema } from "./validate-messages-schema";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { InferNode } from "../../../core";
|
|
2
|
+
import type { ValidationResult } from "../validate-locale-messages";
|
|
3
|
+
import type { MessageObject } from "intor";
|
|
4
|
+
|
|
5
|
+
export function validateMessagesSchema(
|
|
6
|
+
schema: InferNode,
|
|
7
|
+
messages: MessageObject,
|
|
8
|
+
path: string,
|
|
9
|
+
result: ValidationResult,
|
|
10
|
+
) {
|
|
11
|
+
// Only object schemas define required message keys
|
|
12
|
+
if (schema.kind !== "object") return;
|
|
13
|
+
|
|
14
|
+
// Traverse schema-defined keys and validate message presence
|
|
15
|
+
for (const key of Object.keys(schema.properties)) {
|
|
16
|
+
const nextPath = path ? `${path}.${key}` : key;
|
|
17
|
+
const expected = schema.properties[key];
|
|
18
|
+
const value = messages[key];
|
|
19
|
+
|
|
20
|
+
// Schema requires this key, but message does not provide it
|
|
21
|
+
if (value === undefined) {
|
|
22
|
+
result.missingKeys.push(nextPath);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Recurse into nested message objects
|
|
27
|
+
if (typeof value === "object" && value !== null) {
|
|
28
|
+
validateMessagesSchema(
|
|
29
|
+
expected,
|
|
30
|
+
value as MessageObject,
|
|
31
|
+
nextPath,
|
|
32
|
+
result,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|