Skip to content

Commit 6835e89

Browse files
committed
fix(errors): return errors
1 parent 6f0eb9b commit 6835e89

File tree

5 files changed

+92
-49
lines changed

5 files changed

+92
-49
lines changed

packages/validate-icu-locales/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ We can parse JSON, TS and JS if theses files use default export.
1515

1616

1717
```
18-
@scaleway/validate-icu-locales "../**/en.json"
18+
validate-icu "../**/en.json"
1919
```
2020

2121
If there is an error on a local, the CLI will throw an Error and print all errors.
@@ -27,22 +27,22 @@ export default from: ../src/__tests__/locales/en-1.js is not an object
2727
{
2828
errors: [
2929
{
30-
err: [SyntaxError],
30+
message: 'EXPECT_ARGUMENT_CLOSING_BRACE',
3131
value: '{count, plural, =0 {Minute} =1 {Minute} other {Minutes',
3232
key: 'units.minutes.label',
33-
filePath: '../src/__tests__/locales/en.js'
33+
filePath: '../../packages/validate-icu-locales/src/__tests__/locales/en.js'
3434
},
3535
{
36-
err: [SyntaxError],
36+
message: 'EXPECT_ARGUMENT_CLOSING_BRACE',
3737
value: '{count, plural, =0 {Minute} =1 {Minute} other {Minutes',
3838
key: 'units.minutes.label',
39-
filePath: '../src/__tests__/locales/en.json'
39+
filePath: '../../packages/validate-icu-locales/src/__tests__/locales/en.json'
4040
},
4141
{
42-
err: [SyntaxError],
42+
message: 'EXPECT_ARGUMENT_CLOSING_BRACE',
4343
value: '{count, plural, =0 {Minute} =1 {Minute} other {Minutes',
4444
key: 'units.minutes.label',
45-
filePath: '../src/__tests__/locales/en.ts'
45+
filePath: '../../packages/validate-icu-locales/src/__tests__/locales/en.ts'
4646
}
4747
]
4848
}

packages/validate-icu-locales/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"cli"
99
],
1010
"type": "module",
11-
"bin": "dist/index.js",
11+
"bin": {
12+
"validate-icu": "dist/index.js"
13+
},
1214
"publishConfig": {
1315
"access": "public"
1416
},
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const toto = {
2+
// error on this one missing bracket
3+
4+
'units.minutes.label':
5+
'{count, plural, =0 {Minute} =1 {Minute} other {Minutes',
6+
'units.hours.label': '{count, plural, =0 {Hour} =1 {Hour} other {Hours}}',
7+
'units.days.label': '{count, plural, =0 {Day} =1 {Day} other {Days}}',
8+
'units.months.label': '{count, plural, =0 {Month} =1 {Month} other {Months}}',
9+
} as const
Lines changed: 72 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env node
22

33
import { parse } from '@formatjs/icu-messageformat-parser'
4-
import { info } from 'console'
4+
import { ParserError } from '@formatjs/icu-messageformat-parser/error'
55
import { readFile } from 'fs/promises'
66
import { globby } from 'globby'
77
import { importFromString } from 'module-from-string'
@@ -10,27 +10,51 @@ const args = process.argv.slice(2)
1010
const pattern = args[0]
1111
const { error, table } = console
1212

13-
let isICUError = false
14-
15-
const findICUError = (locales: { [key: string]: string }, filePath: string) => {
16-
Object.keys(locales).forEach(key => {
17-
const value = locales[key]
18-
19-
try {
20-
parse(value)
21-
} catch (err) {
22-
isICUError = true
23-
error({
24-
err,
25-
value,
26-
key,
27-
filePath,
28-
})
29-
}
30-
})
13+
type Locales = Record<string, string>
14+
type ErrorICU = {
15+
message: ParserError['message']
16+
value: string
17+
key: string
18+
filePath: string
19+
}
20+
21+
type ErrorsICU = (ErrorICU | undefined)[]
22+
23+
const isObject = (obj: unknown) => obj === Object(obj)
24+
25+
const findICUErrors = (
26+
locales: { [key: string]: string },
27+
filePath: string,
28+
): ErrorsICU => {
29+
const keys = Object.keys(locales)
30+
31+
const errors = keys
32+
.map(key => {
33+
const value = locales[key]
34+
35+
try {
36+
parse(value)
37+
38+
return undefined
39+
} catch (err) {
40+
const { message } = err as ParserError
41+
42+
return {
43+
message,
44+
value,
45+
key,
46+
filePath,
47+
}
48+
}
49+
})
50+
.filter(x => x)
51+
52+
return errors
3153
}
3254

33-
const readFiles = async (files: string[]) => {
55+
const readFiles = async (files: string[]): Promise<ErrorsICU> => {
56+
const errors = []
57+
3458
for await (const file of files) {
3559
const extension = file.split('.').pop()
3660

@@ -39,9 +63,10 @@ const readFiles = async (files: string[]) => {
3963
const data = await readFile(file)
4064
const jsonFile = data.toString()
4165

42-
const locales = JSON.parse(jsonFile) as Record<string, string>
66+
const locales = JSON.parse(jsonFile) as Locales
4367

44-
findICUError(locales, file)
68+
const ICUErrors = findICUErrors(locales, file)
69+
errors.push(...ICUErrors)
4570
} catch (err) {
4671
error({ file, err })
4772
}
@@ -52,37 +77,44 @@ const readFiles = async (files: string[]) => {
5277
const data = await readFile(file)
5378
const javascriptFile = data.toString()
5479

55-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
56-
const d: { default: Record<string, string> } = await importFromString(
57-
javascriptFile,
58-
{ transformOptions: { loader: 'ts' } },
59-
)
60-
61-
if (typeof d.default === 'object') {
62-
findICUError(d.default, file)
80+
const mod: unknown = await importFromString(javascriptFile, {
81+
transformOptions: { loader: 'ts' },
82+
})
83+
84+
if (isObject(mod)) {
85+
if ('default' in Object(mod)) {
86+
const { default: locales } = mod as { default: Locales }
87+
const ICUErrors = findICUErrors(locales, file)
88+
errors.push(...ICUErrors)
89+
} else {
90+
error('export default from: ', file, ' is not an object')
91+
}
6392
} else {
64-
error('export default from: ', file, ' is not an object')
93+
error(file, ' is not an object')
6594
}
6695
} catch (err) {
6796
error({ err, file })
6897
}
6998
}
7099
}
100+
101+
return errors
71102
}
72103

73104
const files = await globby(pattern)
74105

75-
if (files.length > 0) {
76-
table(files)
77-
await readFiles(files)
78-
}
79-
80106
if (files.length === 0) {
81-
info('There is no files matching this pattern', pattern)
107+
error('There is no files matching this pattern', pattern)
108+
process.exit(1)
82109
}
83110

84-
if (isICUError) {
111+
table(files)
112+
113+
const errors = await readFiles(files)
114+
115+
if (errors.length > 0) {
116+
error({
117+
errors,
118+
})
85119
process.exit(1)
86-
} else {
87-
process.exit(0)
88120
}

rollup.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default async () => {
7474
const doesAlsoTargetBrowser = 'browser' in pkg
7575

7676
return [
77-
getConfig(pkg, false),
77+
getConfig(pkg),
7878
doesAlsoTargetBrowser && getConfig(pkg, true),
7979
{
8080
input: './src/index.ts',

0 commit comments

Comments
 (0)