@lingual/i18n-check 0.8.1 → 0.8.3

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.
@@ -1,3 +1,4 @@
1
+ import { CheckResult, InvalidTranslationsResult } from './types';
1
2
  export type StandardReporter = {
2
3
  file: string;
3
4
  key: string;
@@ -6,10 +7,7 @@ export type StandardReporter = {
6
7
  export declare const CheckOptions: string[];
7
8
  export type Context = (typeof CheckOptions)[number];
8
9
  export declare const contextMapping: Record<Context, string>;
9
- export declare const standardReporter: (result: StandardReporter[], flatten?: boolean) => string;
10
- export declare const summaryReporter: (result: {
11
- file: string;
12
- total: number;
13
- }[]) => string;
14
- export declare const createTable: (input: unknown[]) => string;
15
- export declare const createVerticalTable: (input: unknown[]) => string;
10
+ export declare function formatSummaryTable<T>(result: Record<string, T[]>): string;
11
+ export declare function formatTable(rowGroups: string[][][], lineSep?: string): string;
12
+ export declare function formatCheckResultTable(result: CheckResult): string;
13
+ export declare function formatInvalidTranslationsResultTable(result: InvalidTranslationsResult): string;
@@ -1,92 +1,78 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createVerticalTable = exports.createTable = exports.summaryReporter = exports.standardReporter = exports.contextMapping = exports.CheckOptions = void 0;
4
- const console_1 = require("console");
5
- const stream_1 = require("stream");
3
+ exports.contextMapping = exports.CheckOptions = void 0;
4
+ exports.formatSummaryTable = formatSummaryTable;
5
+ exports.formatTable = formatTable;
6
+ exports.formatCheckResultTable = formatCheckResultTable;
7
+ exports.formatInvalidTranslationsResultTable = formatInvalidTranslationsResultTable;
6
8
  exports.CheckOptions = [
7
- "invalidKeys",
8
- "missingKeys",
9
- "unused",
10
- "undefined",
9
+ 'invalidKeys',
10
+ 'missingKeys',
11
+ 'unused',
12
+ 'undefined',
11
13
  ];
12
14
  exports.contextMapping = {
13
- invalidKeys: "invalid",
14
- missingKeys: "missing",
15
- unused: "unused",
16
- undefined: "undefined",
15
+ invalidKeys: 'invalid',
16
+ missingKeys: 'missing',
17
+ unused: 'unused',
18
+ undefined: 'undefined',
17
19
  };
18
- const standardReporter = (result, flatten = false) => {
19
- if (flatten) {
20
- const data = result.reduce((acc, row) => {
21
- Object.entries(row).forEach(([info, result]) => {
22
- acc.push({ info, result });
23
- });
24
- return acc;
25
- }, []);
26
- return (0, exports.createVerticalTable)(data);
20
+ function formatSummaryTable(result) {
21
+ return formatTable(getSummaryRows(result));
22
+ }
23
+ const getSummaryRows = (checkResult) => {
24
+ const rows = [];
25
+ for (const [file, keys] of Object.entries(checkResult)) {
26
+ rows.push([truncate(file), String(keys.length)]);
27
27
  }
28
- return (0, exports.createTable)(result);
28
+ return [[['file', 'total']], rows];
29
29
  };
30
- exports.standardReporter = standardReporter;
31
- const summaryReporter = (result) => {
32
- return (0, exports.createTable)(result.map(({ file, total }) => ({ file, total })));
33
- };
34
- exports.summaryReporter = summaryReporter;
35
- const createTable = (input) => {
36
- // https://stackoverflow.com/a/67859384
37
- const ts = new stream_1.Transform({
38
- transform(chunk, enc, cb) {
39
- cb(null, chunk);
40
- },
41
- });
42
- const logger = new console_1.Console({ stdout: ts });
43
- logger.table(input);
44
- const table = (ts.read() || "").toString();
45
- // https://stackoverflow.com/a/69874540
46
- let output = "";
47
- const lines = table.split(/[\r\n]+/);
48
- for (let line of lines) {
49
- output += `${line
50
- .replace(/[^┬]*┬/, "┌")
51
- .replace(/^├─*┼/, "├")
52
- .replace(/│[^│]*/, "")
53
- .replace(/^└─*┴/, "└")
54
- .replace(/'/g, " ")}\n`;
55
- }
56
- return output.replace(/\n\n$/, "");
57
- };
58
- exports.createTable = createTable;
59
- const createVerticalTable = (input) => {
60
- // https://stackoverflow.com/a/67859384
61
- const ts = new stream_1.Transform({
62
- transform(chunk, enc, cb) {
63
- cb(null, chunk);
64
- },
65
- });
66
- const logger = new console_1.Console({ stdout: ts });
67
- logger.table(input);
68
- const table = (ts.read() || "").toString();
69
- // https://stackoverflow.com/a/69874540
70
- let output = "";
71
- let firstLine = "";
72
- let index = 0;
73
- const lines = table.split(/[\r\n]+/);
74
- for (let line of lines) {
75
- const transformedLine = `${line
76
- .replace(/[^┬]*┬/, "┌")
77
- .replace(/^├─*┼/, "├")
78
- .replace(/│[^│]*/, "")
79
- .replace(/^└─*┴/, "└")
80
- .replace(/'/g, " ")}\n`;
81
- output += transformedLine;
82
- if (index === 2) {
83
- firstLine = transformedLine;
30
+ function formatTable(rowGroups, lineSep = '\n') {
31
+ // +2 for whitespace padding left and right
32
+ const padding = 2;
33
+ const colWidths = [];
34
+ for (const rows of rowGroups) {
35
+ for (const row of rows) {
36
+ for (let index = 0; index < row.length; ++index) {
37
+ colWidths[index] = Math.max(colWidths[index] ?? 0, row[index].length + padding);
38
+ }
84
39
  }
85
- if (index > 3 && (index + 1) % 3 === 0 && index !== lines.length - 3) {
86
- output += firstLine;
40
+ }
41
+ const lines = [];
42
+ lines.push(formatSeparatorRow(colWidths, '┌┬┐'));
43
+ for (const rows of rowGroups) {
44
+ for (const row of rows) {
45
+ lines.push(formatRow(row, colWidths));
87
46
  }
88
- index++;
47
+ lines.push(formatSeparatorRow(colWidths, '├┼┤'));
89
48
  }
90
- return output.replace(/\n\n$/, "");
91
- };
92
- exports.createVerticalTable = createVerticalTable;
49
+ lines[lines.length - 1] = formatSeparatorRow(colWidths, '└┴┘');
50
+ return lines.join(lineSep);
51
+ }
52
+ function formatSeparatorRow(widths, [left, middle, right]) {
53
+ return (left + widths.map((width) => ''.padEnd(width, '─')).join(middle) + right);
54
+ }
55
+ function formatRow(values, widths) {
56
+ return (`│` +
57
+ values
58
+ .map((val, index) => ` ${val} `.padEnd(widths[index], ' '))
59
+ .join('│') +
60
+ `│`);
61
+ }
62
+ const truncate = (chars, len = 80) => chars.length > 80 ? `${chars.substring(0, len)}...` : chars;
63
+ function formatCheckResultTable(result) {
64
+ return formatTable([
65
+ [['file', 'key']],
66
+ Object.entries(result).flatMap(([file, keys]) => keys.map((key) => [truncate(file), truncate(key)])),
67
+ ]);
68
+ }
69
+ function formatInvalidTranslationsResultTable(result) {
70
+ return formatTable([
71
+ [['info', 'result']],
72
+ ...Object.entries(result).flatMap(([file, errors]) => errors.map(({ key, msg }) => [
73
+ ['file', truncate(file)],
74
+ ['key', truncate(key)],
75
+ ['msg', truncate(msg, 120)],
76
+ ])),
77
+ ]);
78
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const errorReporters_1 = require("./errorReporters");
4
+ describe('formatTable', () => {
5
+ test('single col and row', () => {
6
+ expect((0, errorReporters_1.formatTable)([[['lorem ipsum']]])).toEqual(`
7
+ ┌─────────────┐
8
+ │ lorem ipsum │
9
+ └─────────────┘
10
+ `.trim());
11
+ });
12
+ test('single col and two rows', () => {
13
+ expect((0, errorReporters_1.formatTable)([[['lorem ipsum'], ['foo bar']]])).toEqual(`
14
+ ┌─────────────┐
15
+ │ lorem ipsum │
16
+ │ foo bar │
17
+ └─────────────┘
18
+ `.trim());
19
+ });
20
+ test('with two columns and two row groups', () => {
21
+ expect((0, errorReporters_1.formatTable)([
22
+ [['col1', 'col2']],
23
+ [
24
+ ['lorem ipsum dolor', 'foobar'],
25
+ ['baz', 'more text'],
26
+ ],
27
+ ])).toEqual(`
28
+ ┌───────────────────┬───────────┐
29
+ │ col1 │ col2 │
30
+ ├───────────────────┼───────────┤
31
+ │ lorem ipsum dolor │ foobar │
32
+ │ baz │ more text │
33
+ └───────────────────┴───────────┘
34
+ `.trim());
35
+ });
36
+ test('with two columns and three row groups', () => {
37
+ expect((0, errorReporters_1.formatTable)([
38
+ [['one', 'two']],
39
+ [
40
+ ['lorem ipsum dolor', 'foobar'],
41
+ ['baz', 'more text'],
42
+ ],
43
+ [['hello world', 'here is more text for testing']],
44
+ ])).toEqual(`
45
+ ┌───────────────────┬───────────────────────────────┐
46
+ │ one │ two │
47
+ ├───────────────────┼───────────────────────────────┤
48
+ │ lorem ipsum dolor │ foobar │
49
+ │ baz │ more text │
50
+ ├───────────────────┼───────────────────────────────┤
51
+ │ hello world │ here is more text for testing │
52
+ └───────────────────┴───────────────────────────────┘
53
+ `.trim());
54
+ });
55
+ });
56
+ describe('formatCheckResultTable', () => {
57
+ test('with one file and two keys', () => {
58
+ expect((0, errorReporters_1.formatCheckResultTable)({
59
+ 'some/file.json': ['key.one', 'key.two'],
60
+ })).toEqual(`
61
+ ┌────────────────┬─────────┐
62
+ │ file │ key │
63
+ ├────────────────┼─────────┤
64
+ │ some/file.json │ key.one │
65
+ │ some/file.json │ key.two │
66
+ └────────────────┴─────────┘
67
+ `.trim());
68
+ });
69
+ test('with two files and three keys', () => {
70
+ expect((0, errorReporters_1.formatCheckResultTable)({
71
+ 'some/de.json': ['key.one', 'key.two'],
72
+ 'some/en.json': ['key.three'],
73
+ })).toEqual(`
74
+ ┌──────────────┬───────────┐
75
+ │ file │ key │
76
+ ├──────────────┼───────────┤
77
+ │ some/de.json │ key.one │
78
+ │ some/de.json │ key.two │
79
+ │ some/en.json │ key.three │
80
+ └──────────────┴───────────┘
81
+ `.trim());
82
+ });
83
+ });
84
+ describe('formatInvalidTranslationsResultTable', () => {
85
+ test('with one file and one key', () => {
86
+ expect((0, errorReporters_1.formatInvalidTranslationsResultTable)({
87
+ 'some/en.json': [{ key: 'key.one', msg: 'key one error msg' }],
88
+ })).toEqual(`
89
+ ┌──────┬───────────────────┐
90
+ │ info │ result │
91
+ ├──────┼───────────────────┤
92
+ │ file │ some/en.json │
93
+ │ key │ key.one │
94
+ │ msg │ key one error msg │
95
+ └──────┴───────────────────┘
96
+ `.trim());
97
+ });
98
+ test('with two files and three keys', () => {
99
+ expect((0, errorReporters_1.formatInvalidTranslationsResultTable)({
100
+ 'some/en-US.json': [
101
+ { key: 'key.one', msg: 'key one error msg' },
102
+ { key: 'key.two', msg: 'another msg' },
103
+ ],
104
+ 'some/de.json': [{ key: 'key.three', msg: 'key three msg' }],
105
+ })).toEqual(`
106
+ ┌──────┬───────────────────┐
107
+ │ info │ result │
108
+ ├──────┼───────────────────┤
109
+ │ file │ some/en-US.json │
110
+ │ key │ key.one │
111
+ │ msg │ key one error msg │
112
+ ├──────┼───────────────────┤
113
+ │ file │ some/en-US.json │
114
+ │ key │ key.two │
115
+ │ msg │ another msg │
116
+ ├──────┼───────────────────┤
117
+ │ file │ some/de.json │
118
+ │ key │ key.three │
119
+ │ msg │ key three msg │
120
+ └──────┴───────────────────┘
121
+ `.trim());
122
+ });
123
+ });
124
+ describe('formatSummaryTable', () => {
125
+ test('with CheckResult with single file and key', () => {
126
+ expect((0, errorReporters_1.formatSummaryTable)({
127
+ 'some/file.json': ['key.one'],
128
+ })).toEqual(`
129
+ ┌────────────────┬───────┐
130
+ │ file │ total │
131
+ ├────────────────┼───────┤
132
+ │ some/file.json │ 1 │
133
+ └────────────────┴───────┘
134
+ `.trim());
135
+ });
136
+ test('with CheckResult with two files and three keys', () => {
137
+ expect((0, errorReporters_1.formatSummaryTable)({
138
+ 'some/de.json': ['key.one', 'key.two'],
139
+ 'some/en.json': ['key.three'],
140
+ })).toEqual(`
141
+ ┌──────────────┬───────┐
142
+ │ file │ total │
143
+ ├──────────────┼───────┤
144
+ │ some/de.json │ 2 │
145
+ │ some/en.json │ 1 │
146
+ └──────────────┴───────┘
147
+ `.trim());
148
+ });
149
+ test('with InvalidTranslationsResult with two files and three keys', () => {
150
+ expect((0, errorReporters_1.formatSummaryTable)({
151
+ 'some/en-US.json': [
152
+ { key: 'key.one', msg: 'key one error msg' },
153
+ { key: 'key.two', msg: 'another msg' },
154
+ ],
155
+ 'some/de.json': [{ key: 'key.three', msg: 'key three msg' }],
156
+ })).toEqual(`
157
+ ┌─────────────────┬───────┐
158
+ │ file │ total │
159
+ ├─────────────────┼───────┤
160
+ │ some/en-US.json │ 2 │
161
+ │ some/de.json │ 1 │
162
+ └─────────────────┴───────┘
163
+ `.trim());
164
+ });
165
+ });
package/dist/index.d.ts CHANGED
@@ -1,14 +1,14 @@
1
- import { CheckResult, Translation, TranslationFile } from "./types";
2
- import { Context } from "./errorReporters";
1
+ import { CheckResult, InvalidTranslationsResult, Translation, TranslationFile } from './types';
2
+ import { Context } from './errorReporters';
3
3
  export type Options = {
4
- format?: "icu" | "i18next" | "react-intl" | "next-intl";
4
+ format?: 'icu' | 'i18next' | 'react-intl' | 'next-intl';
5
5
  checks?: Context[];
6
6
  };
7
- export declare const checkInvalidTranslations: (source: Translation, targets: Record<string, Translation>, options?: Options) => CheckResult;
7
+ export declare const checkInvalidTranslations: (source: Translation, targets: Record<string, Translation>, options?: Options) => InvalidTranslationsResult;
8
8
  export declare const checkMissingTranslations: (source: Translation, targets: Record<string, Translation>) => CheckResult;
9
9
  export declare const checkTranslations: (source: TranslationFile[], targets: TranslationFile[], options?: Options) => {
10
10
  missingKeys: CheckResult | undefined;
11
- invalidKeys: CheckResult | undefined;
11
+ invalidKeys: InvalidTranslationsResult | undefined;
12
12
  };
13
- export declare const checkUnusedKeys: (translationFiles: TranslationFile[], filesToParse: string[], options?: Options, componentFunctions?: never[]) => Promise<CheckResult | undefined>;
14
- export declare const checkUndefinedKeys: (source: TranslationFile[], filesToParse: string[], options?: Options, componentFunctions?: never[]) => Promise<CheckResult | undefined>;
13
+ export declare const checkUnusedKeys: (translationFiles: TranslationFile[], filesToParse: string[], options?: Options, componentFunctions?: string[]) => Promise<CheckResult | undefined>;
14
+ export declare const checkUndefinedKeys: (source: TranslationFile[], filesToParse: string[], options?: Options, componentFunctions?: string[]) => Promise<CheckResult | undefined>;