`
12 |
13 | Example: `['en', 'ja']`
14 |
15 | #### pattern
16 |
17 | Type: `string`
18 |
19 | File path with glob.
20 |
21 | #### options
22 |
23 | Additional options.
24 |
25 | #### defaultLocale
26 |
27 | Type: `string`
Default: `en`
28 |
29 | Set default locale for your app.
30 |
31 | ##### cwd
32 |
33 | Type: `string`
Default: `.`
34 |
35 | **You most likely don't need this.**
36 |
37 | Change run path.
38 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/__snapshots__/test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`babel plugin execution order 1`] = `
4 | Object {
5 | "en": Object {
6 | "src.extract-react-intl.test.pluginOrdering.test": "auto",
7 | },
8 | }
9 | `;
10 |
11 | exports[`extract from file 1`] = `
12 | Object {
13 | "en": Object {
14 | "components.App.1248161314": "Submit button",
15 | "components.App.hello": "hello",
16 | "components.App.world": "world",
17 | "components/Greeting/welcome": "
18 | Welcome {name}, you have received {unreadCount, plural,
19 | =0 {no new messages}
20 | one {{formattedUnreadCount} new message}
21 | other {{formattedUnreadCount} new messages}
22 | } since {formattedLastLoginTime}.
23 | ",
24 | },
25 | "ja": Object {
26 | "components.App.1248161314": "",
27 | "components.App.hello": "",
28 | "components.App.world": "",
29 | "components/Greeting/welcome": "",
30 | },
31 | }
32 | `;
33 |
34 | exports[`extract from file with descriptions 1`] = `
35 | Object {
36 | "en": Object {
37 | "components.App.hello": Object {
38 | "description": "hello message description",
39 | "message": "hello",
40 | },
41 | "components.App.world": Object {
42 | "description": "world message description",
43 | "message": "world",
44 | },
45 | "components/Greeting/welcome": Object {
46 | "description": "Welcome message description",
47 | "message": "
48 | Welcome {name}, you have received {unreadCount, plural,
49 | =0 {no new messages}
50 | one {{formattedUnreadCount} new message}
51 | other {{formattedUnreadCount} new messages}
52 | } since {formattedLastLoginTime}.
53 | ",
54 | },
55 | },
56 | "ja": Object {
57 | "components.App.hello": Object {
58 | "description": "hello message description",
59 | "message": "",
60 | },
61 | "components.App.world": Object {
62 | "description": "world message description",
63 | "message": "",
64 | },
65 | "components/Greeting/welcome": Object {
66 | "description": "Welcome message description",
67 | "message": "",
68 | },
69 | },
70 | }
71 | `;
72 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/fixtures/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "browsers": [
8 | "last 2 versions",
9 | "safari >= 7"
10 | ]
11 | }
12 | }
13 | ],
14 | "@babel/preset-react",
15 | "@babel/preset-flow"
16 | ],
17 | "plugins": [
18 | "@babel/plugin-proposal-class-properties"
19 | ],
20 | "env": {
21 | "react-intl": {
22 | "plugins": [
23 | [
24 | "react-intl-auto",
25 | {
26 | "removePrefix": "src.extract-react-intl.test.fixtures"
27 | }
28 | ]
29 | ]
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/fixtures/components/App/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react'
3 | import { FormattedMessage, injectIntl } from 'react-intl'
4 | import Greeting from '../Greeting'
5 | import messages from './messages'
6 |
7 | injectIntl(({ intl }) => {
8 | const label = intl.formatMessage({ defaultMessage: "Submit button" })
9 |
10 | return
11 | });
12 |
13 |
14 | export default class App extends Component {
15 | render() {
16 | const user = {
17 | name: 'Eric',
18 | unreadCount: 4,
19 | lastLoginTime: Date.now() - 1000 * 60 * 60 * 24
20 | }
21 |
22 | return (
23 |
24 |
25 |
26 |
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/fixtures/components/App/messages.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { defineMessages } from 'react-intl'
3 |
4 | export default defineMessages({
5 | // hello message description
6 | hello: 'hello',
7 | // world message description
8 | world: 'world'
9 | })
10 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/fixtures/components/Greeting/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react'
3 | import {
4 | FormattedMessage,
5 | FormattedNumber,
6 | FormattedRelative
7 | } from 'react-intl'
8 | import messages from './messages'
9 |
10 | type Props = {
11 | user: {
12 | name: string,
13 | unreadCount: number,
14 | lastLoginTime: number
15 | }
16 | }
17 |
18 | export default class Greeting extends Component {
19 | props: Props
20 |
21 | render() {
22 | const { user } = this.props
23 |
24 | return (
25 |
26 | {user.name},
30 | unreadCount: user.unreadCount,
31 | formattedUnreadCount: (
32 |
33 |
34 |
35 | ),
36 | formattedLastLoginTime: (
37 |
38 | )
39 | }}
40 | />
41 |
42 | )
43 | }
44 | }
45 |
46 | function defaultMessage() {
47 | return 'hello'
48 | }
49 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/fixtures/components/Greeting/messages.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { defineMessages } from 'react-intl'
3 |
4 | export default defineMessages({
5 | // Welcome message description
6 | welcome: {
7 | id: 'components/Greeting/welcome',
8 | defaultMessage: `
9 | Welcome {name}, you have received {unreadCount, plural,
10 | =0 {no new messages}
11 | one {{formattedUnreadCount} new message}
12 | other {{formattedUnreadCount} new messages}
13 | } since {formattedLastLoginTime}.
14 | `
15 | }
16 | })
17 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/fixtures/components/LanguageProvider/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react'
3 | import { addLocaleData, IntlProvider } from 'react-intl'
4 | import enLocaleData from 'react-intl/locale-data/en'
5 | import jaLocaleData from 'react-intl/locale-data/ja'
6 |
7 | import enMessages from '../../translations/en.json'
8 | import jaMessages from '../../translations/ja.json'
9 |
10 | addLocaleData(enLocaleData)
11 | addLocaleData(jaLocaleData)
12 |
13 | const messages = {
14 | en: enMessages,
15 | ja: jaMessages
16 | }
17 |
18 | export default class LanguageProvider extends Component {
19 | props: { children?: React$Element<*> }
20 | state: { locale: string } = { locale: 'en' }
21 |
22 | render() {
23 | const { locale } = this.state
24 | return (
25 |
33 | )
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/fixtures/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/fixtures/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from 'react'
3 | import ReactDOM from 'react-dom'
4 | import App from './components/App'
5 | import LanguageProvider from './components/LanguageProvider'
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | )
13 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/pluginOrdering/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-flow"],
3 | "plugins": ["react-intl-auto"]
4 | }
5 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/pluginOrdering/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | test: 'auto'
5 | })
6 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/resolution/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-flow"],
3 | "plugins": ["react-intl"]
4 | }
5 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/resolution/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | test: {
5 | id: 'test',
6 | defaultMessage: 'test'
7 | }
8 | })
9 |
--------------------------------------------------------------------------------
/src/extract-react-intl/test/test.ts:
--------------------------------------------------------------------------------
1 | import m from '..'
2 |
3 | const pattern = 'src/extract-react-intl/test/fixtures/**/*.js'
4 | const locales = ['en', 'ja']
5 |
6 | test('extract from file', async () => {
7 | process.env.BABEL_ENV = 'react-intl'
8 | const x = await m(locales, pattern, {
9 | defaultLocale: 'en',
10 | cwd: `${__dirname}/fixtures`,
11 | extractFromFormatMessageCall: true
12 | })
13 | expect(x).toMatchSnapshot()
14 | })
15 |
16 | // TODO: fix
17 | test.skip('babelrc path resolution', async () => {
18 | const x = await m(['en'], './extract-react-intl/test/resolution/**/*.js', {
19 | defaultLocale: 'en',
20 | cwd: `${__dirname}/resolution`
21 | })
22 | expect(x).toMatchSnapshot()
23 | })
24 |
25 | test('babel plugin execution order', async () => {
26 | const x = await m(
27 | ['en'],
28 | 'src/extract-react-intl/test/pluginOrdering/**/*.js',
29 | { defaultLocale: 'en', cwd: `${__dirname}/pluginOrdering` }
30 | )
31 | expect(x).toMatchSnapshot()
32 | })
33 |
34 | test('error', async () => {
35 | expect.assertions(1)
36 | await m(locales, 'notfound', {
37 | defaultLocale: 'en',
38 | cwd: `${__dirname}/fixtures`
39 | }).catch((error) => {
40 | expect(error.message).toMatch('File not found')
41 | })
42 | })
43 |
44 | test('extract from file with descriptions', async () => {
45 | process.env.BABEL_ENV = 'react-intl'
46 | const x = await m(locales, pattern, {
47 | defaultLocale: 'en',
48 | cwd: './test/fixtures',
49 | withDescriptions: true
50 | })
51 | expect(x).toMatchSnapshot()
52 | })
53 |
--------------------------------------------------------------------------------
/src/global.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'read-babelrc-up' {
2 | function sync(opts: {
3 | cwd: string
4 | }): { path: string; babel: import('@babel/core').TransformOptions }
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import fs from 'fs'
3 | import mkdirp from 'mkdirp'
4 | import pick from 'lodash.pick'
5 | import yaml from 'js-yaml'
6 | import pify from 'pify'
7 | import { flatten, unflatten } from 'flat'
8 | import loadJsonFile from 'load-json-file'
9 | import writeJsonFile from 'write-json-file'
10 | import sortKeys from 'sort-keys'
11 | import _extractReactIntl from './extract-react-intl'
12 |
13 | const writeJson = (outputPath: string, obj: object) => {
14 | return writeJsonFile(`${outputPath}.json`, obj, { indent: 2 })
15 | }
16 |
17 | const writeYaml = (outputPath: string, obj: object) => {
18 | return pify(fs.writeFile)(`${outputPath}.yml`, yaml.safeDump(obj), 'utf8')
19 | }
20 |
21 | const isJson = (ext: string) => ext === 'json'
22 |
23 | function loadLocaleFiles(locales: string[], buildDir: string, ext: string) {
24 | const oldLocaleMaps: Record> = {}
25 |
26 | try {
27 | mkdirp.sync(buildDir)
28 | } catch (error) {}
29 |
30 | for (const locale of locales) {
31 | const file = path.resolve(buildDir, `${locale}.${ext}`)
32 | // Initialize json file
33 | try {
34 | const output = isJson(ext) ? JSON.stringify({}) : yaml.safeDump({})
35 | fs.writeFileSync(file, output, { flag: 'wx' })
36 | } catch (error) {
37 | if (error.code !== 'EEXIST') {
38 | throw error
39 | }
40 | }
41 |
42 | let messages = isJson(ext)
43 | ? loadJsonFile.sync(file)
44 | : yaml.safeLoad(fs.readFileSync(file, 'utf8'), { json: true })
45 |
46 | messages = flatten(messages)
47 |
48 | oldLocaleMaps[locale] = {}
49 | for (const messageKey of Object.keys(messages)) {
50 | const message = messages[messageKey]
51 | if (message && typeof message === 'string' && message !== '') {
52 | oldLocaleMaps[locale][messageKey] = messages[messageKey]
53 | }
54 | }
55 | }
56 |
57 | return oldLocaleMaps
58 | }
59 |
60 | type Opts = {
61 | [key: string]: unknown
62 | defaultLocale: string
63 | format?: string
64 | flat?: boolean
65 | overwriteDefault?: boolean
66 | }
67 |
68 | // eslint-disable-next-line max-lines-per-function
69 | const extractMessage = async (
70 | locales: string[],
71 | pattern: string,
72 | buildDir: string,
73 | {
74 | format = 'json',
75 | flat = isJson(format),
76 | defaultLocale = 'en',
77 | overwriteDefault = true,
78 | ...opts
79 | }: Opts = {
80 | defaultLocale: 'en'
81 | }
82 | ) => {
83 | if (!Array.isArray(locales)) {
84 | throw new TypeError(`Expected a Array, got ${typeof locales}`)
85 | }
86 |
87 | if (typeof pattern !== 'string') {
88 | throw new TypeError(`Expected a string, got ${typeof pattern}`)
89 | }
90 |
91 | if (typeof buildDir !== 'string') {
92 | throw new TypeError(`Expected a string, got ${typeof buildDir}`)
93 | }
94 |
95 | const ext = isJson(format) ? 'json' : 'yml'
96 |
97 | const oldLocaleMaps = loadLocaleFiles(locales, buildDir, ext)
98 |
99 | const extractorOptions = {
100 | defaultLocale,
101 | withDescriptions: false,
102 | cwd: process.cwd(),
103 | extractFromFormatMessageCall: true,
104 | ...opts
105 | }
106 |
107 | const newLocaleMaps = await _extractReactIntl(
108 | locales,
109 | pattern,
110 | extractorOptions
111 | )
112 |
113 | return Promise.all(
114 | locales.map((locale) => {
115 | // If the default locale, overwrite the origin file
116 | let localeMap =
117 | locale === defaultLocale && overwriteDefault
118 | ? // Create a clone so we can use only current valid messages below
119 | { ...oldLocaleMaps[locale], ...newLocaleMaps[locale] }
120 | : { ...newLocaleMaps[locale], ...oldLocaleMaps[locale] }
121 | // Only keep existing keys
122 | localeMap = pick(localeMap, Object.keys(newLocaleMaps[locale]))
123 |
124 | const fomattedLocaleMap: object = flat
125 | ? sortKeys(localeMap, { deep: true })
126 | : sortKeys(unflatten(localeMap, { object: true }), { deep: true })
127 |
128 | const fn = isJson(format) ? writeJson : writeYaml
129 |
130 | return fn(path.resolve(buildDir, locale), fomattedLocaleMap)
131 | })
132 | )
133 | }
134 |
135 | extractMessage.extractReactIntl = _extractReactIntl
136 |
137 | export default extractMessage
138 |
139 | // For CommonJS default export support
140 | module.exports = extractMessage
141 | module.exports.default = extractMessage
142 |
--------------------------------------------------------------------------------
/src/test/fixtures/custom/a/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from '../i18n'
2 |
3 | export default defineMessages({
4 | hello: {
5 | id: 'a.custom.hello',
6 | defaultMessage: 'hello'
7 | },
8 | world: {
9 | id: 'a.custom.world',
10 | defaultMessage: 'world'
11 | }
12 | })
13 |
--------------------------------------------------------------------------------
/src/test/fixtures/custom/b/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from '../i18n'
2 |
3 | export default defineMessages({
4 | hello: {
5 | id: 'b.custom.message',
6 | defaultMessage: 'Message'
7 | }
8 | })
9 |
--------------------------------------------------------------------------------
/src/test/fixtures/custom/i18n.js:
--------------------------------------------------------------------------------
1 | export { defineMessages } from 'react-intl'
2 |
--------------------------------------------------------------------------------
/src/test/fixtures/default/a/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useIntl } from 'react-intl'
3 |
4 | export const SubmitButton = () => {
5 | const intl = useIntl()
6 | const label = intl.formatMessage({
7 | id: 'a.submit',
8 | defaultMessage: 'Submit Button'
9 | })
10 | return
11 | }
12 |
--------------------------------------------------------------------------------
/src/test/fixtures/default/a/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | hello: {
5 | id: 'a.hello',
6 | defaultMessage: 'hello'
7 | },
8 | world: {
9 | id: 'a.world',
10 | defaultMessage: 'world'
11 | }
12 | })
13 |
--------------------------------------------------------------------------------
/src/test/fixtures/default/b/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | hello: {
5 | id: 'b.hello',
6 | defaultMessage: 'hello'
7 | },
8 | world: {
9 | id: 'b.world',
10 | defaultMessage: 'world'
11 | }
12 | })
13 |
--------------------------------------------------------------------------------
/src/test/fixtures/removed/a/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | hello: {
5 | id: 'a.hello',
6 | defaultMessage: 'hello'
7 | }
8 | })
9 |
--------------------------------------------------------------------------------
/src/test/fixtures/removed/b/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | hello: {
5 | id: 'b.hello',
6 | defaultMessage: 'hello'
7 | },
8 | world: {
9 | id: 'b.world',
10 | defaultMessage: 'world'
11 | }
12 | })
13 |
--------------------------------------------------------------------------------
/src/test/fixtures/unsorted/a/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | hello: {
5 | id: 'a.hello',
6 | defaultMessage: 'hello'
7 | },
8 | helloZ: {
9 | id: 'z.hello',
10 | defaultMessage: 'hello'
11 | },
12 | helloC: {
13 | id: 'c.hello',
14 | defaultMessage: 'hello'
15 | },
16 | world: {
17 | id: 'a.world',
18 | defaultMessage: 'world'
19 | }
20 | })
21 |
--------------------------------------------------------------------------------
/src/test/fixtures/unsorted/b/messages.js:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | hello: {
5 | id: 'b.hello',
6 | defaultMessage: 'hello'
7 | },
8 | helloY: {
9 | id: 'y.hello',
10 | defaultMessage: 'hello'
11 | },
12 | world: {
13 | id: 'b.world',
14 | defaultMessage: 'world'
15 | }
16 | })
17 |
--------------------------------------------------------------------------------
/src/test/json/__snapshots__/test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`export json - nest 1`] = `
4 | Object {
5 | "a": Object {
6 | "hello": "hello",
7 | "submit": "Submit Button",
8 | "world": "world",
9 | },
10 | "b": Object {
11 | "hello": "hello",
12 | "world": "world",
13 | },
14 | }
15 | `;
16 |
17 | exports[`export json - nest 2`] = `
18 | Object {
19 | "a": Object {
20 | "hello": "",
21 | "submit": "",
22 | "world": "",
23 | },
24 | "b": Object {
25 | "hello": "",
26 | "world": "",
27 | },
28 | }
29 | `;
30 |
31 | exports[`export json 1`] = `
32 | Object {
33 | "a.hello": "hello",
34 | "a.submit": "Submit Button",
35 | "a.world": "world",
36 | "b.hello": "hello",
37 | "b.world": "world",
38 | }
39 | `;
40 |
41 | exports[`export json 2`] = `
42 | Object {
43 | "a.hello": "",
44 | "a.submit": "",
45 | "a.world": "",
46 | "b.hello": "",
47 | "b.world": "",
48 | }
49 | `;
50 |
51 | exports[`export json with removed messages 1`] = `
52 | Object {
53 | "a.hello": "hello",
54 | "a.submit": "Submit Button",
55 | "a.world": "world",
56 | "b.hello": "hello",
57 | "b.world": "world",
58 | }
59 | `;
60 |
61 | exports[`export json with removed messages 2`] = `
62 | Object {
63 | "a.hello": "",
64 | "a.submit": "",
65 | "a.world": "",
66 | "b.hello": "",
67 | "b.world": "",
68 | }
69 | `;
70 |
71 | exports[`export json with removed messages 3`] = `
72 | Object {
73 | "a.hello": "hello",
74 | "b.hello": "hello",
75 | "b.world": "world",
76 | }
77 | `;
78 |
79 | exports[`export json with removed messages 4`] = `
80 | Object {
81 | "a.hello": "",
82 | "b.hello": "",
83 | "b.world": "",
84 | }
85 | `;
86 |
87 | exports[`export using custom module 1`] = `
88 | Object {
89 | "a.custom.hello": "hello",
90 | "a.custom.world": "world",
91 | "b.custom.message": "Message",
92 | }
93 | `;
94 |
95 | exports[`export using custom module 2`] = `
96 | Object {
97 | "a.custom.hello": "",
98 | "a.custom.world": "",
99 | "b.custom.message": "",
100 | }
101 | `;
102 |
103 | exports[`sort keys 1`] = `
104 | Array [
105 | "a.hello",
106 | "a.world",
107 | "b.hello",
108 | "b.world",
109 | "c.hello",
110 | "y.hello",
111 | "z.hello",
112 | ]
113 | `;
114 |
115 | exports[`sort keys 2`] = `
116 | Array [
117 | "a.hello",
118 | "a.world",
119 | "b.hello",
120 | "b.world",
121 | "c.hello",
122 | "y.hello",
123 | "z.hello",
124 | ]
125 | `;
126 |
127 | exports[`with overwriteDefault 1`] = `
128 | Object {
129 | "a.custom.hello": "hello",
130 | "a.custom.world": "world",
131 | "b.custom.message": "Default Message",
132 | }
133 | `;
134 |
135 | exports[`with overwriteDefault 2`] = `
136 | Object {
137 | "a.custom.hello": "",
138 | "a.custom.world": "",
139 | "b.custom.message": "",
140 | }
141 | `;
142 |
--------------------------------------------------------------------------------
/src/test/json/test.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import path from 'path'
3 | import tempy from 'tempy'
4 | import m from '../..'
5 |
6 | test('export json', async () => {
7 | const tmp = tempy.directory()
8 | await m(['en', 'ja'], 'src/test/fixtures/default/**/*.js', tmp)
9 | const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
10 | const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
11 | expect(en).toMatchSnapshot()
12 | expect(ja).toMatchSnapshot()
13 | })
14 |
15 | test('export json with removed messages', async () => {
16 | const tmp = tempy.directory()
17 | await m(['en', 'ja'], 'src/test/fixtures/default/**/*.js', tmp)
18 | const enBefore = JSON.parse(
19 | fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8')
20 | )
21 | const jaBefore = JSON.parse(
22 | fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8')
23 | )
24 | expect(enBefore).toMatchSnapshot()
25 | expect(jaBefore).toMatchSnapshot()
26 | await m(['en', 'ja'], 'src/test/fixtures/removed/**/*.js', tmp)
27 | const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
28 | const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
29 | expect(en).toMatchSnapshot()
30 | expect(ja).toMatchSnapshot()
31 | })
32 |
33 | test('export json - nest', async () => {
34 | const tmp = tempy.directory()
35 | await m(['en', 'ja'], 'src/test/fixtures/default/**/*.js', tmp, {
36 | defaultLocale: 'en',
37 | flat: false
38 | })
39 | const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
40 | const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
41 | expect(en).toMatchSnapshot()
42 | expect(ja).toMatchSnapshot()
43 | })
44 |
45 | test('sort keys', async () => {
46 | const tmp = tempy.directory()
47 | const enPath = path.resolve(tmp, 'en.json')
48 | const jaPath = path.resolve(tmp, 'ja.json')
49 |
50 | await m(['en', 'ja'], 'src/test/fixtures/unsorted/**/*.js', tmp)
51 | const en = JSON.parse(fs.readFileSync(enPath, 'utf8'))
52 | const ja = JSON.parse(fs.readFileSync(jaPath, 'utf8'))
53 |
54 | expect(Object.keys(en)).toMatchSnapshot()
55 | expect(Object.keys(ja)).toMatchSnapshot()
56 | })
57 |
58 | test('export using custom module', async () => {
59 | const tmp = tempy.directory()
60 | await m(['en', 'ja'], 'src/test/fixtures/custom/**/*.js', tmp, {
61 | defaultLocale: 'en',
62 | moduleSourceName: '../i18n'
63 | })
64 | const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
65 | const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
66 | expect(en).toMatchSnapshot()
67 | expect(ja).toMatchSnapshot()
68 | })
69 |
70 | test('with overwriteDefault', async () => {
71 | const tmp = tempy.directory()
72 | fs.writeFileSync(
73 | path.resolve(tmp, 'en.json'),
74 | JSON.stringify(
75 | {
76 | 'a.custom.hello': 'hello',
77 | 'a.custom.world': 'world',
78 | 'b.custom.message': 'Default Message'
79 | },
80 | null,
81 | 2
82 | ),
83 | 'utf8'
84 | )
85 | await m(['en', 'ja'], 'src/test/fixtures/custom/**/*.js', tmp, {
86 | defaultLocale: 'en',
87 | moduleSourceName: '../i18n',
88 | overwriteDefault: false
89 | })
90 | const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
91 | const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
92 | expect(en).toMatchSnapshot()
93 | expect(ja).toMatchSnapshot()
94 | })
95 |
--------------------------------------------------------------------------------
/src/test/test.ts:
--------------------------------------------------------------------------------
1 | import m from '..'
2 |
3 | test('errors', async () => {
4 | // @ts-ignore
5 | await expect(m('hello')).rejects.toThrow('Expected a Array')
6 | // @ts-ignore
7 | await expect(m(['en', 'ja'], 2)).rejects.toThrow('Expected a string')
8 | // @ts-ignore
9 | await expect(m(['en', 'ja'], 'app/', 2)).rejects.toThrow('Expected a string')
10 | })
11 |
--------------------------------------------------------------------------------
/src/test/yaml/__snapshots__/test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`export yaml - flat 1`] = `
4 | Object {
5 | "a.hello": "hello",
6 | "a.submit": "Submit Button",
7 | "a.world": "world",
8 | "b.hello": "hello",
9 | "b.world": "world",
10 | }
11 | `;
12 |
13 | exports[`export yaml - flat 2`] = `
14 | Object {
15 | "a.hello": "",
16 | "a.submit": "",
17 | "a.world": "",
18 | "b.hello": "",
19 | "b.world": "",
20 | }
21 | `;
22 |
23 | exports[`export yaml 1`] = `
24 | Object {
25 | "a": Object {
26 | "hello": "hello",
27 | "submit": "Submit Button",
28 | "world": "world",
29 | },
30 | "b": Object {
31 | "hello": "hello",
32 | "world": "world",
33 | },
34 | }
35 | `;
36 |
37 | exports[`export yaml 2`] = `
38 | Object {
39 | "a": Object {
40 | "hello": "",
41 | "submit": "",
42 | "world": "",
43 | },
44 | "b": Object {
45 | "hello": "",
46 | "world": "",
47 | },
48 | }
49 | `;
50 |
51 | exports[`exsit yaml 1`] = `
52 | Object {
53 | "a": Object {
54 | "hello": "hello",
55 | "submit": "Submit Button",
56 | "world": "world",
57 | },
58 | "b": Object {
59 | "hello": "hello",
60 | "world": "world",
61 | },
62 | }
63 | `;
64 |
65 | exports[`exsit yaml 2`] = `
66 | Object {
67 | "a": Object {
68 | "hello": "hello2",
69 | "submit": "",
70 | "world": "",
71 | },
72 | "b": Object {
73 | "hello": "",
74 | "world": "",
75 | },
76 | }
77 | `;
78 |
--------------------------------------------------------------------------------
/src/test/yaml/test.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import path from 'path'
3 | // eslint-disable-next-line import/no-extraneous-dependencies
4 | import tempy from 'tempy'
5 | // eslint-disable-next-line import/no-extraneous-dependencies
6 | import tempWrite from 'temp-write'
7 | import yaml from 'js-yaml'
8 | import m from '../..'
9 |
10 | const fixturesPath = 'src/test/fixtures/default/**/*.js'
11 |
12 | const yamlLoad = (tmp: string, file = '') =>
13 | yaml.safeLoad(fs.readFileSync(path.resolve(tmp, file), 'utf8'))
14 |
15 | const defaultLocale = 'en'
16 |
17 | test('export yaml', async () => {
18 | const tmp = tempy.directory()
19 | await m(['en', 'ja'], fixturesPath, tmp, { defaultLocale, format: 'yaml' })
20 |
21 | expect(yamlLoad(tmp, 'en.yml')).toMatchSnapshot()
22 | expect(yamlLoad(tmp, 'ja.yml')).toMatchSnapshot()
23 | })
24 |
25 | test('export yaml - flat', async () => {
26 | const tmp = tempy.directory()
27 | await m(['en', 'ja'], fixturesPath, tmp, {
28 | defaultLocale,
29 | format: 'yaml',
30 | flat: true
31 | })
32 |
33 | expect(yamlLoad(tmp, 'en.yml')).toMatchSnapshot()
34 | expect(yamlLoad(tmp, 'ja.yml')).toMatchSnapshot()
35 | })
36 |
37 | test('exsit yaml', async () => {
38 | const x = { a: { hello: 'hello2' } }
39 |
40 | const tmpEn = tempWrite.sync(yaml.safeDump(x), 'en.yml')
41 | await m(['en'], fixturesPath, path.dirname(tmpEn), {
42 | defaultLocale,
43 | format: 'yaml'
44 | })
45 | expect(yaml.safeLoad(fs.readFileSync(tmpEn, 'utf8'))).toMatchSnapshot()
46 |
47 | const tmpJa = tempWrite.sync(yaml.safeDump(x), 'ja.yml')
48 | await m(['ja'], fixturesPath, path.dirname(tmpJa), {
49 | defaultLocale,
50 | format: 'yaml'
51 | })
52 |
53 | expect(yaml.safeLoad(fs.readFileSync(tmpJa, 'utf8'))).toMatchSnapshot()
54 | })
55 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@akameco/tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": ["src"]
7 | }
8 |
--------------------------------------------------------------------------------