@formatjs/cli 5.1.8 → 5.1.10

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.
Files changed (44) hide show
  1. package/bin/formatjs +164756 -0
  2. package/package.json +2 -2
  3. package/BUILD +0 -22
  4. package/CHANGELOG.md +0 -1144
  5. package/integration-tests/BUILD +0 -81
  6. package/integration-tests/compile/__snapshots__/integration.test.ts.snap +0 -1154
  7. package/integration-tests/compile/glob/en.json +0 -14
  8. package/integration-tests/compile/glob/en2.json +0 -14
  9. package/integration-tests/compile/glob-conflict/en.json +0 -14
  10. package/integration-tests/compile/glob-conflict/en2.json +0 -14
  11. package/integration-tests/compile/integration.test.ts +0 -213
  12. package/integration-tests/compile/lang/empty.json +0 -1
  13. package/integration-tests/compile/lang/en-crowdin.json +0 -14
  14. package/integration-tests/compile/lang/en-format.json +0 -14
  15. package/integration-tests/compile/lang/en-lokalise.json +0 -14
  16. package/integration-tests/compile/lang/en-simple.json +0 -5
  17. package/integration-tests/compile/lang/en-smartling.json +0 -24
  18. package/integration-tests/compile/lang/en-transifex.json +0 -14
  19. package/integration-tests/compile/lang/en.json +0 -22
  20. package/integration-tests/compile/lang/malformed-messages.json +0 -14
  21. package/integration-tests/compile_folder/__snapshots__/integration.test.ts.snap +0 -44
  22. package/integration-tests/compile_folder/integration.test.ts +0 -33
  23. package/integration-tests/compile_folder/lang/empty.json +0 -1
  24. package/integration-tests/compile_folder/lang/en.json +0 -14
  25. package/integration-tests/compile_folder/lang/en2.json +0 -14
  26. package/integration-tests/extract/__snapshots__/integration.test.ts.snap +0 -941
  27. package/integration-tests/extract/defineMessages/actual.ignore.js +0 -22
  28. package/integration-tests/extract/defineMessages/actual.js +0 -65
  29. package/integration-tests/extract/defineMessages/bad.json +0 -1
  30. package/integration-tests/extract/duplicated/file1.tsx +0 -10
  31. package/integration-tests/extract/duplicated/file2.tsx +0 -10
  32. package/integration-tests/extract/integration.test.ts +0 -256
  33. package/integration-tests/extract/nonDuplicated/file1.tsx +0 -14
  34. package/integration-tests/extract/nonDuplicated/file2.tsx +0 -14
  35. package/integration-tests/extract/typescript/actual.ignore.tsx +0 -21
  36. package/integration-tests/extract/typescript/actual.tsx +0 -104
  37. package/integration-tests/extract/typescript/err.tsx +0 -1
  38. package/integration-tests/extract/typescript/pragma.tsx +0 -77
  39. package/integration-tests/extract/typescript/ts47.tsx +0 -6
  40. package/integration-tests/extract-vue/__snapshots__/integration.test.ts.snap +0 -17
  41. package/integration-tests/extract-vue/comp.vue +0 -22
  42. package/integration-tests/extract-vue/integration.test.ts +0 -14
  43. package/integration-tests/formatter.js +0 -18
  44. package/integration-tests/package.json +0 -5
@@ -1,22 +0,0 @@
1
- // @react-intl project:foo
2
- import React, {Component} from 'react'
3
- import {defineMessages, FormattedMessage} from 'react-intl'
4
-
5
- const msgs = defineMessages({
6
- test: {
7
- id: 'ignore',
8
- defaultMessage: 'ignore',
9
- },
10
- })
11
-
12
- export default class Foo extends Component {
13
- render() {
14
- return (
15
- <div>
16
- <h1>
17
- <FormattedMessage {...msgs.test} />
18
- </h1>
19
- </div>
20
- )
21
- }
22
- }
@@ -1,65 +0,0 @@
1
- import React, {Component} from 'react'
2
- import {defineMessages, FormattedMessage} from 'react-intl'
3
-
4
- const msgs = defineMessages({
5
- header: {
6
- id: 'foo.bar.baz',
7
- defaultMessage: 'Hello World!',
8
- description: 'The default message',
9
- },
10
- content: {
11
- id: 'foo.bar.biff',
12
- defaultMessage: 'Hello Nurse!',
13
- description: 'Another message',
14
- },
15
- kittens: {
16
- id: 'app.home.kittens',
17
- description: 'Counts kittens',
18
- defaultMessage: '{count, plural, =0 {😭} one {# kitten} other {# kittens}}',
19
- },
20
- trailingWhitespace: {
21
- id: 'trailing.ws',
22
- description: 'Whitespace',
23
- defaultMessage: ' Some whitespace ',
24
- },
25
- escaped: {
26
- id: 'escaped.apostrophe',
27
- description: 'Escaped apostrophe',
28
- defaultMessage: "A quoted value ''{value}'",
29
- },
30
- noId: {
31
- description: 'no ID',
32
- defaultMessage: 'No ID',
33
- },
34
- duplicateAsNoId: {
35
- description: 'no ID',
36
- defaultMessage: 'No ID',
37
- },
38
- complex: {
39
- defaultMessage: 'I have {count, plural, one{a dog} other{many dogs}}',
40
- },
41
- })
42
-
43
- export default class Foo extends Component {
44
- render() {
45
- const msg = msgs?.header
46
- return (
47
- <div>
48
- <h1>
49
- <FormattedMessage {...msg} />
50
- </h1>
51
- <p>
52
- <FormattedMessage {...msgs.content} />
53
- </p>
54
- <p>
55
- <FormattedMessage {...msgs.kittens} />
56
- <FormattedMessage
57
- id="inline-msg"
58
- defaultMessage="inline message"
59
- description="inline description"
60
- />
61
- </p>
62
- </div>
63
- )
64
- }
65
- }
@@ -1,10 +0,0 @@
1
- import React from 'react'
2
- import {FormattedMessage} from 'react-intl'
3
-
4
- export function Foo() {
5
- return (
6
- <p>
7
- <FormattedMessage id="foo" defaultMessage="Foo" />
8
- </p>
9
- )
10
- }
@@ -1,10 +0,0 @@
1
- import React from 'react'
2
- import {FormattedMessage} from 'react-intl'
3
-
4
- export function Bar() {
5
- return (
6
- <p>
7
- <FormattedMessage id="foo" defaultMessage="Bar" />
8
- </p>
9
- )
10
- }
@@ -1,256 +0,0 @@
1
- import {resolve, join} from 'path'
2
- import {promisify} from 'util'
3
- import {exec as nodeExec} from 'child_process'
4
- import {readJSON, mkdirp} from 'fs-extra'
5
- import _rimraf from 'rimraf'
6
- const exec = promisify(nodeExec)
7
- const rimraf = promisify(_rimraf)
8
-
9
- const BIN_PATH = require.resolve('@formatjs/cli/bin/formatjs')
10
- const ARTIFACT_PATH = resolve(__dirname, 'test_artifacts')
11
-
12
- beforeEach(async () => {
13
- await mkdirp(join(__dirname, 'test_artifacts'))
14
- await rimraf(ARTIFACT_PATH)
15
- })
16
-
17
- test('basic case: help', async () => {
18
- await expect(exec(`${BIN_PATH} extract --help`)).resolves.toMatchSnapshot()
19
- }, 20000)
20
-
21
- test('basic case: defineMessages -> stdout', async () => {
22
- await expect(
23
- exec(
24
- `${BIN_PATH} extract --throws ${join(
25
- __dirname,
26
- 'defineMessages/actual.js'
27
- )}`
28
- )
29
- ).resolves.toMatchSnapshot()
30
- }, 20000)
31
-
32
- test('flatten defineMessages -> stdout', async () => {
33
- await expect(
34
- exec(
35
- `${BIN_PATH} extract --flatten --throws ${join(
36
- __dirname,
37
- 'defineMessages/actual.js'
38
- )}`
39
- )
40
- ).resolves.toMatchSnapshot()
41
- }, 20000)
42
-
43
- test('bad json', async () => {
44
- await expect(
45
- exec(
46
- `${BIN_PATH} extract --throws ${join(__dirname, 'defineMessages/*.js*')}`
47
- )
48
- ).rejects.toThrowError('Error processing file')
49
- }, 20000)
50
-
51
- test('[glob] basic case: defineMessages -> stdout', async () => {
52
- await expect(
53
- exec(`${BIN_PATH} extract ${join(__dirname, 'defineMessages/*.js')}`)
54
- ).resolves.toMatchSnapshot()
55
- }, 20000)
56
-
57
- test('basic case: defineMessages -> out-file', async () => {
58
- process.chdir(__dirname)
59
- await expect(
60
- exec(
61
- `${BIN_PATH} extract defineMessages/actual.js --out-file ${ARTIFACT_PATH}/defineMessages/actual.json`
62
- )
63
- ).resolves.toMatchSnapshot()
64
-
65
- expect(
66
- await readJSON(join(ARTIFACT_PATH, 'defineMessages/actual.json'))
67
- ).toMatchSnapshot()
68
- }, 20000)
69
-
70
- test('basic case: defineMessages -> out-file with location', async () => {
71
- process.chdir(__dirname)
72
- await expect(
73
- exec(
74
- `${BIN_PATH} extract defineMessages/actual.js --extract-source-location --out-file ${ARTIFACT_PATH}/defineMessages/actual.json`
75
- )
76
- ).resolves.toMatchSnapshot()
77
-
78
- expect(
79
- await readJSON(join(ARTIFACT_PATH, 'defineMessages/actual.json'))
80
- ).toMatchSnapshot()
81
- }, 20000)
82
-
83
- test('typescript -> stdout', async () => {
84
- await expect(
85
- exec(`${BIN_PATH} extract ${join(__dirname, 'typescript/actual.tsx')}`)
86
- ).resolves.toMatchSnapshot()
87
- }, 20000)
88
-
89
- test('pragma', async () => {
90
- await expect(
91
- exec(
92
- `${BIN_PATH} extract --pragma intl ${join(
93
- __dirname,
94
- 'typescript/pragma.tsx'
95
- )}`
96
- )
97
- ).resolves.toMatchSnapshot()
98
- }, 20000)
99
-
100
- test('typescript -> stdout with formatter', async () => {
101
- await expect(
102
- exec(
103
- `${BIN_PATH} extract ${join(
104
- __dirname,
105
- 'typescript/actual.tsx'
106
- )} --format ${join(__dirname, '../formatter.js')}`
107
- )
108
- ).resolves.toMatchSnapshot()
109
- }, 20000)
110
-
111
- test('typescript -> stdout with transifex', async () => {
112
- await expect(
113
- exec(
114
- `${BIN_PATH} extract ${join(
115
- __dirname,
116
- 'typescript/actual.tsx'
117
- )} --format transifex`
118
- )
119
- ).resolves.toMatchSnapshot()
120
- }, 20000)
121
-
122
- test('typescript -> stdout with simple', async () => {
123
- await expect(
124
- exec(
125
- `${BIN_PATH} extract ${join(
126
- __dirname,
127
- 'typescript/actual.tsx'
128
- )} --format simple`
129
- )
130
- ).resolves.toMatchSnapshot()
131
- }, 20000)
132
-
133
- test('typescript -> stdout with lokalise', async () => {
134
- await expect(
135
- exec(
136
- `${BIN_PATH} extract ${join(
137
- __dirname,
138
- 'typescript/actual.tsx'
139
- )} --format lokalise`
140
- )
141
- ).resolves.toMatchSnapshot()
142
- }, 20000)
143
-
144
- test('typescript -> stdout with crowdin', async () => {
145
- await expect(
146
- exec(
147
- `${BIN_PATH} extract ${join(
148
- __dirname,
149
- 'typescript/actual.tsx'
150
- )} --format crowdin`
151
- )
152
- ).resolves.toMatchSnapshot()
153
- }, 20000)
154
-
155
- test('typescript -> stdout with smartling', async () => {
156
- await expect(
157
- exec(
158
- `${BIN_PATH} extract ${join(
159
- __dirname,
160
- 'typescript/actual.tsx'
161
- )} --format smartling`
162
- )
163
- ).resolves.toMatchSnapshot()
164
- }, 20000)
165
-
166
- const ignore = "--ignore '*.ignore.*'"
167
-
168
- test('ignore -> stdout TS', async () => {
169
- await expect(
170
- exec(
171
- `${BIN_PATH} extract --throws ${join(
172
- __dirname,
173
- 'typescript/actual.tsx'
174
- )} ${ignore}`
175
- )
176
- ).resolves.toMatchSnapshot()
177
- }, 20000)
178
-
179
- test('ignore -> stdout JS', async () => {
180
- const jsResult = await exec(
181
- `${BIN_PATH} extract '${join(__dirname, 'defineMessages/*.js')}' ${ignore}`
182
- )
183
- expect(JSON.parse(jsResult.stdout)).toMatchSnapshot()
184
- expect(jsResult.stderr).toBe('')
185
- }, 20000)
186
-
187
- test('duplicated descriptor ids shows warning', async () => {
188
- const {stderr, stdout} = await exec(
189
- `${BIN_PATH} extract '${join(__dirname, 'duplicated/*.tsx')}'`
190
- )
191
- expect(JSON.parse(stdout)).toMatchSnapshot()
192
- expect(stderr).toContain('Duplicate message id: "foo"')
193
- }, 20000)
194
-
195
- test('duplicated descriptor ids throws', async () => {
196
- expect(async () => {
197
- await exec(
198
- `${BIN_PATH} extract --throws '${join(__dirname, 'duplicated/*.tsx')}'`
199
- )
200
- }).rejects.toThrowError('Duplicate message id: "foo"')
201
- }, 20000)
202
-
203
- test('non duplicated descriptors does not throw', async () => {
204
- await expect(
205
- exec(
206
- `${BIN_PATH} extract --throws '${join(
207
- __dirname,
208
- 'nonDuplicated/*.tsx'
209
- )}' --out-file ${ARTIFACT_PATH}/nonDuplicated/actual.json`
210
- )
211
- ).resolves.toMatchSnapshot()
212
-
213
- expect(
214
- await readJSON(join(ARTIFACT_PATH, 'nonDuplicated/actual.json'))
215
- ).toMatchSnapshot()
216
- }, 20000)
217
-
218
- test('invalid syntax should throw', async () => {
219
- expect(async () => {
220
- await exec(
221
- `${BIN_PATH} extract --throws '${join(__dirname, 'typescript/err.tsx')}'`
222
- )
223
- }).rejects.toThrowError('TS1005')
224
- }, 20000)
225
-
226
- test('whitespace and newlines should be preserved', async () => {
227
- process.chdir(__dirname)
228
- await expect(
229
- exec(
230
- `${BIN_PATH} extract '${join(
231
- __dirname,
232
- 'typescript/actual.tsx'
233
- )}' --out-file ${ARTIFACT_PATH}/defineMessages/actual.json`
234
- )
235
- ).resolves.toMatchSnapshot()
236
-
237
- expect(
238
- await readJSON(join(ARTIFACT_PATH, 'defineMessages/actual.json'))
239
- ).toMatchSnapshot()
240
- }, 20000)
241
-
242
- // See: https://github.com/formatjs/formatjs/issues/3630
243
- test('TypeScript 4.7 syntax', async () => {
244
- await expect(
245
- exec(
246
- `${BIN_PATH} extract --throws '${join(
247
- __dirname,
248
- 'typescript/ts47.tsx'
249
- )}' --out-file ${ARTIFACT_PATH}/typescript/ts47.json`
250
- )
251
- ).resolves.toMatchSnapshot()
252
-
253
- expect(
254
- await readJSON(join(ARTIFACT_PATH, 'typescript/ts47.json'))
255
- ).toMatchSnapshot()
256
- })
@@ -1,14 +0,0 @@
1
- import React from 'react'
2
- import {FormattedMessage} from 'react-intl'
3
-
4
- export function Foo() {
5
- return (
6
- <p>
7
- <FormattedMessage
8
- id="foo"
9
- defaultMessage="Foo"
10
- description={{maxCharacterCount: 2, text: 'Bar'}}
11
- />
12
- </p>
13
- )
14
- }
@@ -1,14 +0,0 @@
1
- import React from 'react'
2
- import {FormattedMessage} from 'react-intl'
3
-
4
- export function Bar() {
5
- return (
6
- <p>
7
- <FormattedMessage
8
- id="foo"
9
- defaultMessage="Foo"
10
- description={{text: 'Bar', maxCharacterCount: 2}}
11
- />
12
- </p>
13
- )
14
- }
@@ -1,21 +0,0 @@
1
- import React, {Component} from 'react'
2
- import {defineMessages, FormattedMessage} from 'react-intl'
3
-
4
- const msgs = defineMessages({
5
- test: {
6
- id: 'ignore',
7
- defaultMessage: 'ignore',
8
- },
9
- })
10
-
11
- export default class Foo extends Component {
12
- render() {
13
- return (
14
- <div>
15
- <h1>
16
- <FormattedMessage {...msgs.test} />
17
- </h1>
18
- </div>
19
- )
20
- }
21
- }
@@ -1,104 +0,0 @@
1
- import React, {Component} from 'react'
2
- import {defineMessages, FormattedMessage} from 'react-intl'
3
-
4
- export enum TestEnum {
5
- FOO = 'foo',
6
- BAR = 'bar',
7
- }
8
-
9
- const msgs = defineMessages({
10
- header: {
11
- id: 'foo.bar.baz',
12
- defaultMessage: 'Hello World!',
13
- description: 'The default message',
14
- },
15
- content: {
16
- id: 'foo.bar.biff',
17
- defaultMessage: 'Hello Nurse!',
18
- description: 'Another message',
19
- },
20
- kittens: {
21
- id: 'app.home.kittens',
22
- description: 'Counts kittens',
23
- defaultMessage: '{count, plural, =0 {😭} one {# kitten} other {# kittens}}',
24
- },
25
- trailingWhitespace: {
26
- id: 'trailing.ws',
27
- description: 'Whitespace',
28
- defaultMessage: ' Some whitespace ',
29
- },
30
- newline: {
31
- id: 'newline',
32
- description: 'this is \
33
- a \
34
- description',
35
- defaultMessage: 'this is \
36
- a message',
37
- },
38
- linebreak: {
39
- id: 'linebreak',
40
- description: 'this is\na\ndescription',
41
- defaultMessage: 'this is\na message',
42
- },
43
- templateLinebreak: {
44
- id: 'templateLinebreak',
45
- description: `this is
46
- a
47
- description`,
48
- defaultMessage: `this is
49
- a message`,
50
- },
51
- escaped: {
52
- id: 'escaped.apostrophe',
53
- description: 'Escaped apostrophe',
54
- defaultMessage: "A quoted value ''{value}'",
55
- },
56
- noId: {
57
- description: 'no ID',
58
- defaultMessage: 'No ID',
59
- },
60
- duplicateAsNoId: {
61
- description: 'no ID',
62
- defaultMessage: 'No ID',
63
- },
64
- noDesc: {
65
- defaultMessage: 'No Desc',
66
- },
67
- } as const)
68
-
69
- async function foo() {
70
- await import('../../package.json')
71
- }
72
-
73
- export default class Foo extends Component<{}, {}> {
74
- async componentDidMount() {
75
- await foo()
76
- }
77
- render() {
78
- return (
79
- <div>
80
- <h1>
81
- <FormattedMessage {...msgs.header} />
82
- </h1>
83
- <p>
84
- <FormattedMessage {...msgs.content} />
85
- </p>
86
- <p>
87
- <FormattedMessage {...msgs.kittens} />
88
- <FormattedMessage
89
- id="inline"
90
- defaultMessage="formatted message"
91
- description="foo"
92
- />
93
- <FormattedMessage
94
- id="inline.linebreak"
95
- defaultMessage="formatted message
96
- with linebreak"
97
- description="foo
98
- bar"
99
- />
100
- </p>
101
- </div>
102
- )
103
- }
104
- }
@@ -1 +0,0 @@
1
- import from 'foo'
@@ -1,77 +0,0 @@
1
- // @intl foo:bar baz:1
2
- import React, {Component} from 'react'
3
- import {defineMessages, FormattedMessage} from 'react-intl'
4
-
5
- export enum TestEnum {
6
- FOO = 'foo',
7
- BAR = 'bar',
8
- }
9
-
10
- const msgs = defineMessages({
11
- header: {
12
- id: 'foo.bar.baz',
13
- defaultMessage: 'Hello World!',
14
- description: 'The default message',
15
- },
16
- content: {
17
- id: 'foo.bar.biff',
18
- defaultMessage: 'Hello Nurse!',
19
- description: 'Another message',
20
- },
21
- kittens: {
22
- id: 'app.home.kittens',
23
- description: 'Counts kittens',
24
- defaultMessage: '{count, plural, =0 {😭} one {# kitten} other {# kittens}}',
25
- },
26
- trailingWhitespace: {
27
- id: 'trailing.ws',
28
- description: 'Whitespace',
29
- defaultMessage: ' Some whitespace ',
30
- },
31
- escaped: {
32
- id: 'escaped.apostrophe',
33
- description: 'Escaped apostrophe',
34
- defaultMessage: "A quoted value ''{value}'",
35
- },
36
- noId: {
37
- description: 'no ID',
38
- defaultMessage: 'No ID',
39
- },
40
- duplicateAsNoId: {
41
- description: 'no ID',
42
- defaultMessage: 'No ID',
43
- },
44
- noDesc: {
45
- defaultMessage: 'No Desc',
46
- },
47
- } as const)
48
-
49
- async function foo() {
50
- await import('../../package.json')
51
- }
52
-
53
- export default class Foo extends Component<{}, {}> {
54
- async componentDidMount() {
55
- await foo()
56
- }
57
- render() {
58
- return (
59
- <div>
60
- <h1>
61
- <FormattedMessage {...msgs.header} />
62
- </h1>
63
- <p>
64
- <FormattedMessage {...msgs.content} />
65
- </p>
66
- <p>
67
- <FormattedMessage {...msgs.kittens} />
68
- <FormattedMessage
69
- id="inline"
70
- defaultMessage="formatted message"
71
- description="foo"
72
- />
73
- </p>
74
- </div>
75
- )
76
- }
77
- }
@@ -1,6 +0,0 @@
1
- // See: https://github.com/formatjs/formatjs/issues/3630
2
- export type GetMetaType<State> = State extends {
3
- meta: infer Meta extends string
4
- }
5
- ? Meta
6
- : never;
@@ -1,17 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`vue 1`] = `
4
- Object {
5
- "3mGZ30": Object {
6
- "defaultMessage": "in script",
7
- "description": "in script desc",
8
- },
9
- "7MCO2v": Object {
10
- "defaultMessage": "in template",
11
- "description": "in template desc",
12
- },
13
- "S3wEt4": Object {
14
- "defaultMessage": "script setup",
15
- },
16
- }
17
- `;
@@ -1,22 +0,0 @@
1
- <template>
2
- <p>
3
- {{
4
- $formatMessage({
5
- defaultMessage: 'in template',
6
- description: 'in template desc',
7
- })
8
- }}
9
- </p>
10
- </template>
11
-
12
- <script>
13
- intl.formatMessage({
14
- defaultMessage: 'in script',
15
- description: 'in script desc',
16
- })
17
- </script>
18
-
19
- <script lang="ts" setup>
20
- const intl = useIntl()
21
- console.log(intl.formatMessage({defaultMessage: 'script setup'}))
22
- </script>
@@ -1,14 +0,0 @@
1
- import {join} from 'path'
2
- import {promisify} from 'util'
3
- import {exec as nodeExec} from 'child_process'
4
- const exec = promisify(nodeExec)
5
-
6
- const BIN_PATH = require.resolve('@formatjs/cli/bin/formatjs')
7
-
8
- test('vue', async () => {
9
- const {stdout} = await exec(
10
- `${BIN_PATH} extract '${join(__dirname, '*.vue')}'`
11
- )
12
-
13
- expect(JSON.parse(stdout)).toMatchSnapshot()
14
- }, 20000)
@@ -1,18 +0,0 @@
1
- exports.format = function (msgs) {
2
- const results = {}
3
- for (const [id, msg] of Object.entries(msgs)) {
4
- results[id] = {
5
- string: msg.defaultMessage,
6
- comment: msg.description,
7
- }
8
- }
9
- return results
10
- }
11
-
12
- exports.compile = function (msgs) {
13
- const results = {}
14
- for (const [id, msg] of Object.entries(msgs)) {
15
- results[id] = msg.string
16
- }
17
- return results
18
- }
@@ -1,5 +0,0 @@
1
- {
2
- "devDependencies": {
3
- "@formatjs/cli": "workspace:*"
4
- }
5
- }