master 5e8f220414fe cached
66 files
61.9 KB
19.8k tokens
12 symbols
1 requests
Download .txt
Repository: akameco/extract-react-intl-messages
Branch: master
Commit: 5e8f220414fe
Files: 66
Total size: 61.9 KB

Directory structure:
gitextract_9ar36m_8/

├── .all-contributorsrc
├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .prettierignore
├── .prettierrc
├── CODE_OF_CONDUCT.md
├── eslint.config.mjs
├── example/
│   ├── basic/
│   │   ├── babel.config.js
│   │   ├── i18n/
│   │   │   ├── .keep
│   │   │   ├── en.json
│   │   │   └── ja.json
│   │   ├── package.json
│   │   └── src/
│   │       ├── App.jsx
│   │       └── messages.js
│   └── with-typescript/
│       ├── babel.config.js
│       ├── i18n/
│       │   ├── en.json
│       │   └── ja.json
│       ├── package.json
│       └── src/
│           ├── App.tsx
│           └── messages.ts
├── jest.config.js
├── license
├── package.json
├── readme.md
├── src/
│   ├── cli.ts
│   ├── extract-react-intl/
│   │   ├── index.ts
│   │   ├── readme.md
│   │   └── test/
│   │       ├── __snapshots__/
│   │       │   └── test.ts.snap
│   │       ├── fixtures/
│   │       │   ├── .babelrc
│   │       │   ├── components/
│   │       │   │   ├── App/
│   │       │   │   │   ├── index.js
│   │       │   │   │   └── messages.js
│   │       │   │   ├── Greeting/
│   │       │   │   │   ├── index.js
│   │       │   │   │   └── messages.js
│   │       │   │   └── LanguageProvider/
│   │       │   │       └── index.js
│   │       │   ├── index.html
│   │       │   └── index.js
│   │       ├── pluginOrdering/
│   │       │   ├── .babelrc
│   │       │   └── messages.js
│   │       ├── resolution/
│   │       │   ├── .babelrc
│   │       │   └── messages.js
│   │       └── test.ts
│   ├── global.d.ts
│   ├── index.ts
│   └── test/
│       ├── fixtures/
│       │   ├── custom/
│       │   │   ├── a/
│       │   │   │   └── messages.js
│       │   │   ├── b/
│       │   │   │   └── messages.js
│       │   │   └── i18n.js
│       │   ├── default/
│       │   │   ├── a/
│       │   │   │   ├── App.js
│       │   │   │   └── messages.js
│       │   │   └── b/
│       │   │       └── messages.js
│       │   ├── removed/
│       │   │   ├── a/
│       │   │   │   └── messages.js
│       │   │   └── b/
│       │   │       └── messages.js
│       │   └── unsorted/
│       │       ├── a/
│       │       │   └── messages.js
│       │       └── b/
│       │           └── messages.js
│       ├── json/
│       │   ├── __snapshots__/
│       │   │   └── test.ts.snap
│       │   └── test.ts
│       ├── test.ts
│       └── yaml/
│           ├── __snapshots__/
│           │   └── test.ts.snap
│           └── test.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .all-contributorsrc
================================================
{
  "projectName": "extract-react-intl-messages",
  "projectOwner": "akameco",
  "files": ["readme.md"],
  "imageSize": 100,
  "commit": true,
  "contributors": [
    {
      "login": "akameco",
      "name": "akameco",
      "avatar_url": "https://avatars2.githubusercontent.com/u/4002137?v=4",
      "profile": "http://akameco.github.io",
      "contributions": ["code", "test", "doc", "infra"]
    },
    {
      "login": "hoantran-it",
      "name": "Hoan Tran",
      "avatar_url": "https://avatars3.githubusercontent.com/u/13161875?v=4",
      "profile": "http://hoantran.info",
      "contributions": ["code", "test"]
    },
    {
      "login": "giantpinkwalrus",
      "name": "giantpinkwalrus",
      "avatar_url": "https://avatars1.githubusercontent.com/u/3383240?v=4",
      "profile": "https://github.com/giantpinkwalrus",
      "contributions": ["code"]
    },
    {
      "login": "enrique-ramirez",
      "name": "enrique-ramirez",
      "avatar_url": "https://avatars3.githubusercontent.com/u/1190640?v=4",
      "profile": "https://github.com/enrique-ramirez",
      "contributions": ["doc"]
    },
    {
      "login": "hoschi",
      "name": "Stefan Gojan",
      "avatar_url": "https://avatars2.githubusercontent.com/u/163128?v=4",
      "profile": "http://stefan-gojan.de",
      "contributions": ["bug", "code", "test"]
    },
    {
      "login": "solomon23",
      "name": "Solomon English",
      "avatar_url": "https://avatars1.githubusercontent.com/u/857744?v=4",
      "profile": "https://lithe.net",
      "contributions": ["code"]
    },
    {
      "login": "Filson14",
      "name": "Filip \"Filson\" Pasternak",
      "avatar_url": "https://avatars1.githubusercontent.com/u/4540538?v=4",
      "profile": "https://github.com/Filson14",
      "contributions": ["code"]
    },
    {
      "login": "nodaguti",
      "name": "nodaguti",
      "avatar_url": "https://avatars0.githubusercontent.com/u/27622?v=4",
      "profile": "http://about.me/nodaguti",
      "contributions": ["code", "test"]
    },
    {
      "login": "fix-fix",
      "name": "fix-fix",
      "avatar_url": "https://avatars1.githubusercontent.com/u/11943024?v=4",
      "profile": "https://github.com/fix-fix",
      "contributions": ["code"]
    },
    {
      "login": "bradbarrow",
      "name": "bradbarrow",
      "avatar_url": "https://avatars3.githubusercontent.com/u/1264276?v=4",
      "profile": "http://bradbarrow.com",
      "contributions": ["bug", "code", "test"]
    },
    {
      "login": "gmaclennan",
      "name": "Gregor MacLennan",
      "avatar_url": "https://avatars1.githubusercontent.com/u/290457?v=4",
      "profile": "http://ddem.us/",
      "contributions": ["code"]
    },
    {
      "login": "zarv1k",
      "name": "Dmitry Zarva",
      "avatar_url": "https://avatars1.githubusercontent.com/u/6296643?v=4",
      "profile": "https://github.com/zarv1k",
      "contributions": ["code"]
    },
    {
      "login": "panpanc",
      "name": "Michael Pan",
      "avatar_url": "https://avatars2.githubusercontent.com/u/29132669?v=4",
      "profile": "https://github.com/panpanc",
      "contributions": ["example"]
    },
    {
      "login": "testower",
      "name": "Tom Erik Støwer",
      "avatar_url": "https://avatars2.githubusercontent.com/u/231492?v=4",
      "profile": "https://github.com/testower",
      "contributions": ["code"]
    },
    {
      "login": "lensbart",
      "name": "Bart Lens",
      "avatar_url": "https://avatars0.githubusercontent.com/u/20876627?v=4",
      "profile": "https://nextbook.io",
      "contributions": ["code"]
    },
    {
      "login": "revskill10",
      "name": "Truong Hoang Dung",
      "avatar_url": "https://avatars3.githubusercontent.com/u/1390196?v=4",
      "profile": "https://github.com/revskill10",
      "contributions": ["example"]
    },
    {
      "login": "Nestoro",
      "name": "Nestoro",
      "avatar_url": "https://avatars.githubusercontent.com/u/13397845?v=4",
      "profile": "https://github.com/Nestoro",
      "contributions": ["code"]
    },
    {
      "login": "lightnet328",
      "name": "Yutaro Kido",
      "avatar_url": "https://avatars.githubusercontent.com/u/2351326?v=4",
      "profile": "https://lightnet328.com/",
      "contributions": ["code"]
    }
  ],
  "repoType": "github",
  "commitConvention": "none",
  "repoHost": "https://github.com",
  "skipCi": true,
  "contributorsPerLine": 7
}



================================================
FILE: .babelrc
================================================
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "current"
        }
      }
    ],
    "@babel/preset-react"
  ]
}


================================================
FILE: .editorconfig
================================================
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true


================================================
FILE: .eslintrc
================================================
{
  "extends": ["precure/auto"],
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "off"
  }
}


================================================
FILE: .gitattributes
================================================
* text=auto
*.js text eol=lf


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
<!--
Thanks for your interest in the project.
I appreciate bugs filed and PRs submitted!
I'll probably ask you to submit the fix (after giving some direction).

English/日本語(日本語で入力して大丈夫です。日本語の方が迅速です)
-->

- version:
- `node` version:
- `npm` (or `yarn`) version:

**Do you want to request a *feature* or report a *bug*?:**

**What is the current behavior?:**

**What is the expected behavior?:**

**Suggested solution:**


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Thanks for your interest in the project. I appreciate bugs filed and PRs submitted!
English/日本語(日本語で入力して大丈夫です。日本語の方が迅速です)
-->

<!-- What changes are being made? (What feature/bug is being fixed here?) / 何が変更されていますか?-->
**What**:


<!-- Why are these changes necessary? / なぜその変更をする必要がありましたか?-->
**Why**:


<!-- How were these changes implemented? / これらの変更をどのように実装しましたか?-->
**How**:


**Checklist**:
<!-- add "N/A" to the end of each line that's irrelevant to your changes to check an item, place an "x" in the box like so: "- [x] Documentation" -->

* [ ] Documentation
* [ ] Tests
* [ ] Ready to be merged <!-- In your opinion, is this ready to be merged as soon as it's reviewed? -->

<!-- feel free to add additional comments. -->


================================================
FILE: .github/workflows/test.yml
================================================
name: test

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        # LTS (20.x) and latest (22.x) only
        node-version: [20.x, 22.x]
    steps:
      - uses: actions/checkout@v2
      - name: Setup node
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node }}
      - run: npm install
      - run: npm run lint
      - run: npm run test


================================================
FILE: .gitignore
================================================
# Dependencies
node_modules/

# Build outputs
lib/
dist/
compiled/

# Cache files
.eslintcache
.test-cache

# OS files
.DS_Store
Thumbs.db

# IDE files
.vscode/
.idea/
*.swp
*.swo

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage/
.nyc_output/


================================================
FILE: .husky/pre-commit
================================================
npx lint-staged


================================================
FILE: .prettierignore
================================================
**/test/fixtures/**
.github
dist
package.json


================================================
FILE: .prettierrc
================================================
{
  "semi": false,
  "singleQuote": true,
  "trailingComma": "none"
}


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at akameco.t@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: eslint.config.mjs
================================================
// ESLint v9+ config
import js from '@eslint/js'
import tseslint from 'typescript-eslint'

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    files: ['src/**/*.ts'],
    languageOptions: {
      parser: tseslint.parser,
      parserOptions: {
        ecmaVersion: 2022,
        sourceType: 'module'
      }
    }
  },
  {
    ignores: ['dist/', 'node_modules/', 'compiled/']
  }
]


================================================
FILE: example/basic/babel.config.js
================================================
module.exports = function (api) {
  api.cache(true)

  return {
    presets: ['react-app']
  }
}


================================================
FILE: example/basic/i18n/.keep
================================================


================================================
FILE: example/basic/i18n/en.json
================================================
{
  "App": {
    "hello": "Hello Button",
    "submit": "Submit Button"
  },
  "a": {
    "hello": "hello",
    "world": "world"
  }
}


================================================
FILE: example/basic/i18n/ja.json
================================================
{
  "App": {
    "hello": "",
    "submit": ""
  },
  "a": {
    "hello": "",
    "world": ""
  }
}


================================================
FILE: example/basic/package.json
================================================
{
  "name": "basic",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "i18n": "NODE_ENV=development extract-messages -l=en,ja -o i18n -d en 'src/**/*.{js,jsx}'"
  },
  "dependencies": {
    "react": "^16.13.1",
    "react-intl": "^4.3.1"
  },
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "babel-preset-react-app": "^9.1.2",
    "extract-react-intl-messages": "latest"
  }
}


================================================
FILE: example/basic/src/App.jsx
================================================
import React from 'react'
import { injectIntl, useIntl } from 'react-intl'

export const SubmitButton = injectIntl(({ intl }) => {
  const label = intl.formatMessage({
    id: 'App.submit',
    defaultMessage: 'Submit Button'
  })
  return <button aria-label={label}>{label}</button>
})

export const HelloButton = () => {
  const intl = useIntl()
  const label = intl.formatMessage({
    id: 'App.hello',
    defaultMessage: 'Hello Button'
  })
  return <button aria-label={label}>{label}</button>
}


================================================
FILE: example/basic/src/messages.js
================================================
/* eslint-disable import/no-extraneous-dependencies */
import { defineMessages } from 'react-intl'

export default defineMessages({
  hello: {
    id: 'a.hello',
    defaultMessage: 'hello'
  },
  world: {
    id: 'a.world',
    defaultMessage: 'world'
  }
})


================================================
FILE: example/with-typescript/babel.config.js
================================================
module.exports = function (api) {
  api.cache(true)

  return {
    presets: ['@babel/preset-react', '@babel/preset-typescript']
  }
}


================================================
FILE: example/with-typescript/i18n/en.json
================================================
{
  "App": {
    "hello": "Hello Button",
    "submit": "Submit Button"
  },
  "a": {
    "hello": "hello",
    "world": "world"
  }
}


================================================
FILE: example/with-typescript/i18n/ja.json
================================================
{
  "App": {
    "hello": "",
    "submit": ""
  },
  "a": {
    "hello": "",
    "world": ""
  }
}


================================================
FILE: example/with-typescript/package.json
================================================
{
  "name": "with-typescript",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "i18n": "extract-messages -l=en,ja -o i18n -d en 'src/**/*.{ts,tsx}'"
  },
  "dependencies": {
    "react": "^16.13.1",
    "react-intl": "^4.3.1"
  },
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "@babel/preset-react": "^7.9.4",
    "@babel/preset-typescript": "^7.9.0",
    "extract-react-intl-messages": "latest",
    "typescript": "^3.8.3"
  }
}


================================================
FILE: example/with-typescript/src/App.tsx
================================================
import React from 'react'
import { injectIntl, useIntl } from 'react-intl'

export const SubmitButton = injectIntl(({ intl }) => {
  const label = intl.formatMessage({
    id: 'App.submit',
    defaultMessage: 'Submit Button'
  })
  return <button aria-label={label}>{label}</button>
})

export const HelloButton = () => {
  const intl = useIntl()
  const label = intl.formatMessage({
    id: 'App.hello',
    defaultMessage: 'Hello Button'
  })
  return <button aria-label={label}>{label}</button>
}


================================================
FILE: example/with-typescript/src/messages.ts
================================================
import { defineMessages } from 'react-intl'

export default defineMessages({
  hello: {
    id: 'a.hello',
    defaultMessage: 'hello'
  },
  world: {
    id: 'a.world',
    defaultMessage: 'world'
  }
})


================================================
FILE: jest.config.js
================================================
export default {
  testPathIgnorePatterns: [
    '<rootDir>[/\\\\](dist|compiled|node_modules)[/\\\\]'
  ],
  testEnvironment: 'node',
  preset: 'ts-jest',
  extensionsToTreatAsEsm: ['.ts'],
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1'
  },
  transform: {
    '^.+\\.ts$': [
      'ts-jest',
      {
        useESM: true,
        tsconfig: {
          module: 'esnext'
        }
      }
    ]
  },
  transformIgnorePatterns: ['node_modules/(?!(.*\\.mjs$))']
}


================================================
FILE: license
================================================
The MIT License (MIT)

Copyright (c) akameco <akameco.t@gmail.com> (akameco.github.io)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: package.json
================================================
{
  "name": "extract-react-intl-messages",
  "version": "5.0.0",
  "description": "Extract react-intl messages",
  "license": "MIT",
  "repository": "akameco/extract-react-intl-messages",
  "type": "module",
  "author": {
    "name": "akameco",
    "email": "akameco.t@gmail.com",
    "url": "https://akameco.github.io"
  },
  "engines": {
    "node": ">=20"
  },
  "main": "dist/index.js",
  "scripts": {
    "fmt": "prettier --write .",
    "example": "./cli.js -l=en,ja -o example/i18n -d en 'example/**/*.{js,tsx}'",
    "example:yaml": "./cli.js -l=en,ja -f=yaml -o example/i18n -d en 'example/**/*.{js,tsx}'",
    "prepublish": "npm run build",
    "build": "tsc",
    "lint": "eslint src/**/*.ts --fix --cache",
    "pretest": "node -e \"import('fs/promises').then(fs => fs.rm('.test-cache', {recursive: true, force: true})).catch(() => {})\"",
    "test": "jest",
    "posttest": "node -e \"import('fs/promises').then(fs => fs.rm('.test-cache', {recursive: true, force: true})).catch(() => {})\"",
    "prepare": "husky"
  },
  "bin": {
    "extract-messages": "dist/cli.js",
    "extract-react-intl-messages": "dist/cli.js"
  },
  "files": [
    "dist"
  ],
  "keywords": [
    "react",
    "i18n",
    "intl",
    "react-intl",
    "extract",
    "json",
    "messages"
  ],
  "dependencies": {
    "@babel/core": "^7.28.3",
    "babel-plugin-react-intl": "^7.9.4",
    "deepmerge": "^4.3.1",
    "file-entry-cache": "^5.0.1",
    "flat": "^5.0.2",
    "glob": "11.0.3",
    "js-yaml": "4.1.0",
    "load-json-file": "^6.2.0",
    "lodash.merge": "^4.6.2",
    "meow": "13.2.0",
    "read-babelrc-up": "^1.1.0",
    "sort-keys": "^4.2.0",
    "write-json-file": "^4.3.0"
  },
  "devDependencies": {
    "@babel/plugin-proposal-class-properties": "^7.18.6",
    "@babel/preset-env": "^7.28.3",
    "@babel/preset-flow": "^7.27.1",
    "@babel/preset-react": "^7.27.1",
    "@types/file-entry-cache": "^5.0.4",
    "@types/flat": "^5.0.5",
    "@types/js-yaml": "^3.12.10",
    "@types/load-json-file": "^2.0.7",
    "@types/lodash.merge": "^4.6.9",
    "@types/node": "^24.3.0",
    "@types/temp-write": "^4.0.0",
    "@types/write-json-file": "^2.2.1",
    "babel-plugin-react-intl-auto": "^3.3.0",
    "eslint": "^9.34.0",
    "husky": "9.1.7",
    "jest": "^30.1.1",
    "lint-staged": "16.1.5",
    "prettier": "3.6.2",
    "temp-write": "^4.0.0",
    "tempy": "^0.5.0",
    "ts-jest": "^29.4.1",
    "typescript": "^5.9.2",
    "typescript-eslint": "^8.41.0"
  },
  "lint-staged": {
    "*.ts": [
      "prettier --write",
      "eslint --fix"
    ],
    "*.{js,json,md}": [
      "prettier --write"
    ]
  }
}


================================================
FILE: readme.md
================================================
# extract-react-intl-messages

[![test](https://github.com/akameco/extract-react-intl-messages/workflows/test/badge.svg)](https://github.com/akameco/extract-react-intl-messages/actions?query=workflow%3Atest)
[![tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest)
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![MIT License](https://img.shields.io/npm/l/nps.svg?style=flat-square)](./license)
[![All Contributors](https://img.shields.io/badge/all_contributors-14-orange.svg?style=flat-square)](#contributors-)

This package will generate json or yaml files from a glob. It will generate one file per locale, with the ids of each message defined by the [`defineMessages`](https://github.com/yahoo/react-intl/wiki/API#definemessages) function of [react-intl](https://github.com/yahoo/react-intl). The value of each of these keys will be an empty string, except for your `defaultLocale` which will be populated with the [`defaultMessage`](https://github.com/yahoo/react-intl/wiki/API#message-descriptor).

## Dependencies

### Babel

- 0.x works with Babel 6

## Install

```
$ npm install --save-dev extract-react-intl-messages
```

## Usage

app/components/App/messages.js

```js
import { defineMessages, useIntl } from 'react-intl'

export default defineMessages({
  hello: {
    id: 'a.hello',
    defaultMessage: 'hello'
  },
  world: {
    id: 'a.world',
    defaultMessage: 'world'
  }
})

export const SubmitButton = () => {
  const intl = useIntl()
  const label = intl.formatMessage({
    id: 'a.submit',
    defaultMessage: 'Submit Button'
  })
  return <button aria-label={label}>{label}</button>
}
```

### Run Script

```
$ extract-messages -l=en,ja -o app/translations -d en --flat false './app/**/!(*.test).js'
```

### Output

app/translations/en.json

```json
{
  "a": {
    "hello": "hello",
    "world": "world",
    "submit": "Submit Button"
  }
}
```

app/translations/ja.json

```json
{
  "a": {
    "hello": "",
    "world": "",
    "submit": ""
  }
}
```

## Recommend

Use with [babel-plugin-react-intl-auto: i18n for the component age. Auto management react-intl ID.](https://github.com/akameco/babel-plugin-react-intl-auto)

## CLI

```console
$ extract-messages --help

  Extract react-intl messages

  Usage
  $ extract-react-intl-messages <input>
  $ extract-messages <input>

  Options
  -o, --output            Output directory [require: true]
  -l, --locales           locales [require: true]
  -f, --format            json | yaml [default: json]
  -d, --defaultLocale     default locale
  --overwriteDefault      default: true
  --flat                  json [default: true] | yaml [default: false]
  --indent                default: 2

  Example
  $ extract-messages --locales=ja,en --output app/translations 'app/**/*.js'
  $ extract-messages -l=ja,en -o i18n 'src/**/*.js'
  $ extract-messages -l=ja,en -o app/translations -f yaml 'app/**/messages.js'
```

### create-react-app user

create `.babelrc` like this.

```json
{
  "presets": ["react-app"]
}
```

Run with `NODE_ENV=development`.

```
$ NODE_ENV=development extract-messages ...
```

### TypeScript

babel required.

See [example/with-typescript](example/with-typescript)

```
npm install --save-dev @babel/core @babel/preset-typescript @babel/preset-react
```

`babel.config.js`

```js
module.exports = function (api) {
  api.cache(true)

  return {
    presets: ['@babel/preset-react', '@babel/preset-typescript']
  }
}
```

## API

### extractReactIntlMessages(locales, input, buildDir, [options])

#### locales

Type: `Array<string>`

Example: `['en', 'ja']`

#### input

Type: `Array<string>`

Target files.
glob.

#### buildDir

Type: `string`

Export directory.

#### options

##### defaultLocale

Type: `string`<br>
Default: `en`

##### format

Type: `json` | `yaml`<br>
Default: `json`

Set extension to output.

##### overwriteDefault

Type: `boolean`<br>
Default: true

If overwriteDefault is `false`, it will not overwrite messages in the default locale.

##### indent

Type: `number`<br>
Default: `2`

##### flat

Type: `boolean`<br>
Default: `true`

If format is `yaml`, set to `false`.

Be careful if `false`.
See [this issue](https://github.com/akameco/extract-react-intl-messages/issues/3).

##### babel-plugin-react-intl's Options

See https://github.com/formatjs/formatjs/tree/master/packages/babel-plugin-react-intl#options

## Contributors

Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
  <tbody>
    <tr>
      <td align="center" valign="top" width="14.28%"><a href="http://akameco.github.io"><img src="https://avatars2.githubusercontent.com/u/4002137?v=4?s=100" width="100px;" alt="akameco"/><br /><sub><b>akameco</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=akameco" title="Code">💻</a> <a href="https://github.com/akameco/extract-react-intl-messages/commits?author=akameco" title="Tests">⚠️</a> <a href="https://github.com/akameco/extract-react-intl-messages/commits?author=akameco" title="Documentation">📖</a> <a href="#infra-akameco" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
      <td align="center" valign="top" width="14.28%"><a href="http://hoantran.info"><img src="https://avatars3.githubusercontent.com/u/13161875?v=4?s=100" width="100px;" alt="Hoan Tran"/><br /><sub><b>Hoan Tran</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=hoantran-it" title="Code">💻</a> <a href="https://github.com/akameco/extract-react-intl-messages/commits?author=hoantran-it" title="Tests">⚠️</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/giantpinkwalrus"><img src="https://avatars1.githubusercontent.com/u/3383240?v=4?s=100" width="100px;" alt="giantpinkwalrus"/><br /><sub><b>giantpinkwalrus</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=giantpinkwalrus" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/enrique-ramirez"><img src="https://avatars3.githubusercontent.com/u/1190640?v=4?s=100" width="100px;" alt="enrique-ramirez"/><br /><sub><b>enrique-ramirez</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=enrique-ramirez" title="Documentation">📖</a></td>
      <td align="center" valign="top" width="14.28%"><a href="http://stefan-gojan.de"><img src="https://avatars2.githubusercontent.com/u/163128?v=4?s=100" width="100px;" alt="Stefan Gojan"/><br /><sub><b>Stefan Gojan</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/issues?q=author%3Ahoschi" title="Bug reports">🐛</a> <a href="https://github.com/akameco/extract-react-intl-messages/commits?author=hoschi" title="Code">💻</a> <a href="https://github.com/akameco/extract-react-intl-messages/commits?author=hoschi" title="Tests">⚠️</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://lithe.net"><img src="https://avatars1.githubusercontent.com/u/857744?v=4?s=100" width="100px;" alt="Solomon English"/><br /><sub><b>Solomon English</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=solomon23" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/Filson14"><img src="https://avatars1.githubusercontent.com/u/4540538?v=4?s=100" width="100px;" alt="Filip &quot;Filson&quot; Pasternak"/><br /><sub><b>Filip &quot;Filson&quot; Pasternak</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=Filson14" title="Code">💻</a></td>
    </tr>
    <tr>
      <td align="center" valign="top" width="14.28%"><a href="http://about.me/nodaguti"><img src="https://avatars0.githubusercontent.com/u/27622?v=4?s=100" width="100px;" alt="nodaguti"/><br /><sub><b>nodaguti</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=nodaguti" title="Code">💻</a> <a href="https://github.com/akameco/extract-react-intl-messages/commits?author=nodaguti" title="Tests">⚠️</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/fix-fix"><img src="https://avatars1.githubusercontent.com/u/11943024?v=4?s=100" width="100px;" alt="fix-fix"/><br /><sub><b>fix-fix</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=fix-fix" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="http://bradbarrow.com"><img src="https://avatars3.githubusercontent.com/u/1264276?v=4?s=100" width="100px;" alt="bradbarrow"/><br /><sub><b>bradbarrow</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/issues?q=author%3Abradbarrow" title="Bug reports">🐛</a> <a href="https://github.com/akameco/extract-react-intl-messages/commits?author=bradbarrow" title="Code">💻</a> <a href="https://github.com/akameco/extract-react-intl-messages/commits?author=bradbarrow" title="Tests">⚠️</a></td>
      <td align="center" valign="top" width="14.28%"><a href="http://ddem.us/"><img src="https://avatars1.githubusercontent.com/u/290457?v=4?s=100" width="100px;" alt="Gregor MacLennan"/><br /><sub><b>Gregor MacLennan</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=gmaclennan" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/zarv1k"><img src="https://avatars1.githubusercontent.com/u/6296643?v=4?s=100" width="100px;" alt="Dmitry Zarva"/><br /><sub><b>Dmitry Zarva</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=zarv1k" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/panpanc"><img src="https://avatars2.githubusercontent.com/u/29132669?v=4?s=100" width="100px;" alt="Michael Pan"/><br /><sub><b>Michael Pan</b></sub></a><br /><a href="#example-panpanc" title="Examples">💡</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/testower"><img src="https://avatars2.githubusercontent.com/u/231492?v=4?s=100" width="100px;" alt="Tom Erik Støwer"/><br /><sub><b>Tom Erik Støwer</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=testower" title="Code">💻</a></td>
    </tr>
    <tr>
      <td align="center" valign="top" width="14.28%"><a href="https://nextbook.io"><img src="https://avatars0.githubusercontent.com/u/20876627?v=4?s=100" width="100px;" alt="Bart Lens"/><br /><sub><b>Bart Lens</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=lensbart" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/revskill10"><img src="https://avatars3.githubusercontent.com/u/1390196?v=4?s=100" width="100px;" alt="Truong Hoang Dung"/><br /><sub><b>Truong Hoang Dung</b></sub></a><br /><a href="#example-revskill10" title="Examples">💡</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/Nestoro"><img src="https://avatars.githubusercontent.com/u/13397845?v=4?s=100" width="100px;" alt="Nestoro"/><br /><sub><b>Nestoro</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=Nestoro" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://lightnet328.com/"><img src="https://avatars.githubusercontent.com/u/2351326?v=4?s=100" width="100px;" alt="Yutaro Kido"/><br /><sub><b>Yutaro Kido</b></sub></a><br /><a href="https://github.com/akameco/extract-react-intl-messages/commits?author=lightnet328" title="Code">💻</a></td>
    </tr>
  </tbody>
</table>

<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

## License

MIT © [akameco](http://akameco.github.io)


================================================
FILE: src/cli.ts
================================================
#!/usr/bin/env node
 
import meow from 'meow'
import extractMessage from './index.js'

const cli = meow(
  `
  Usage
  $ extract-react-intl-messages <input>
  $ extract-messages <input>

  Options
  -o, --output            Output directory [require: true]
  -l, --locales           locales [require: true]
  -f, --format            json | yaml [default: json]
  -d, --default-locale    default locale
  --overwriteDefault      [default: true]
  --flat                  json [default: true] | yaml [default: false]
  --cache                 [default: false]
  --cacheLocation         [default: .extract-react-intl-messages-cache]
  --indent                default: 2

  Example
  $ extract-messages --locales=ja,en --output app/translations 'app/**/*.js'
  $ extract-messages -l=ja,en -o app/translations -f yaml 'app/**/messages.js'
`,
  {
    importMeta: import.meta,
    flags: {
      flat: {
        type: 'boolean'
      },
      output: {
        type: 'string',
        shortFlag: 'o'
      },
      locales: {
        type: 'string',
        shortFlag: 'l'
      },
      format: {
        type: 'string',
        shortFlag: 'f',
        default: 'json'
      },
      defaultLocale: {
        type: 'string',
        shortFlag: 'd'
      },
      overwriteDefault: {
        type: 'boolean',
        default: true
      },
      withDescriptions: {
        type: 'boolean',
        default: false
      },
      // babel-plugin-react-intl boolean options
      extractSourceLocation: {
        type: 'boolean',
        default: false
      },
      removeDefaultMessage: {
        type: 'boolean'
      },
      extractFromFormatMessageCall: {
        type: 'boolean',
        default: true
      },
      indent: {
        type: 'number',
        default: 2
      }
    }
  }
)

const { output, locales } = cli.flags

if (!output) {
  console.log('ERROR: required output')
  process.exit(1)
}

if (!locales || typeof locales !== 'string') {
  console.log('ERROR: required locales')
  process.exit(1)
}

const localesMap = locales.split(',')

/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
extractMessage(localesMap, cli.input[0], output, cli.flags as any)


================================================
FILE: src/extract-react-intl/index.ts
================================================
import path from 'path'
import { glob } from 'glob'
import { promisify } from 'util'
import merge from 'lodash.merge'
import deepmerge from 'deepmerge'
import {
  resolvePlugin,
  resolvePreset,
  transformFile,
  PluginItem
} from '@babel/core'
import readBabelrcUp from 'read-babelrc-up'
import babelPluginReactIntl from 'babel-plugin-react-intl'
import fileEntryCache, { FileDescriptor } from 'file-entry-cache'

type LocaleMap = Record<string, Record<string, unknown>>

const localeMap = (arr: string[]): LocaleMap =>
  arr.reduce((obj: LocaleMap, x: string) => {
    obj[x] = {}
    return obj
  }, {})

const createResolveList =
  (fn: (name: string, dirname: string) => string | null) =>
  (list: PluginItem[], cwd: string) =>
    list.map((x) => (typeof x === 'string' ? fn(x, cwd) : x))

const resolvePresets = createResolveList(resolvePreset)
const resolvePlugins = createResolveList(resolvePlugin)

const getBabelrc = (cwd: string) => {
  try {
    const babelrc = readBabelrcUp.sync({ cwd }).babel
    if (!babelrc.env) {
      return babelrc
    }

    const env = process.env.BABEL_ENV || process.env.NODE_ENV || 'development'

    return deepmerge(babelrc, babelrc.env[env] || {})
  } catch {
    return { presets: [], plugins: [] }
  }
}

const getBabelrcDir = (cwd: string) =>
  path.dirname(readBabelrcUp.sync({ cwd }).path)

const babelPluginReactIntlOptions = new Set([
  'moduleSourceName',
  'extractSourceLocation',
  'messagesDir',
  'overrideIdFn',
  'removeDefaultMessage',
  'extractFromFormatMessageCall',
  'additionalComponentNames'
])

type Options = {
  [key: string]: unknown
  defaultLocale?: string
  cwd?: string
  cache?: boolean
  cacheLocation?: string
  withDescriptions?: boolean
}

type Message = {
  id: string
  defaultMessage: string
  description: string
}

type CacheData = {
  defaultLocale: string
  localeMap: LocaleMap
}

type File = FileDescriptor & {
  meta: {
    data: CacheData
  }
}

export default async (
  locales: string[],
  pattern: string,
  {
    defaultLocale = 'en',
    withDescriptions = false,
    cwd = process.cwd(),
    cache: cacheEnabled = false,
    cacheLocation = '.extract-react-intl-messages-cache',
    ...pluginOptions
  }: Options = {}
) => {
  if (!Array.isArray(locales)) {
    throw new TypeError(`Expected a Array, got ${typeof locales}`)
  }

  if (typeof pattern !== 'string') {
    throw new TypeError(`Expected a string, got ${typeof pattern}`)
  }

  const babelrc = getBabelrc(cwd) || {}
  const babelrcDir = getBabelrcDir(cwd)

  const presets = babelrc.presets || []
  const plugins = babelrc.plugins || []

  if (
    !plugins.find(
      (plugin: PluginItem) =>
        (Array.isArray(plugin) ? plugin[0] : plugin) === 'react-intl'
    )
  ) {
    // Append a the `react-intl` babel plugin only when it isn’t already included in the babel config
    presets.unshift({
      plugins: [
        [
          babelPluginReactIntl,
          Object.entries(pluginOptions).reduce((acc, [key, value]) => {
            if (babelPluginReactIntlOptions.has(key)) {
              return { ...acc, [key]: value }
            }
            return acc
          }, {})
        ]
      ]
    })
  }

  const files: string[] = await glob(pattern)
  if (files.length === 0) {
    throw new Error(`File not found (${pattern})`)
  }

  const cachePath = path.resolve(cacheLocation)
  const cacheDirname = path.dirname(cachePath)
  const cacheBasename = path.basename(cachePath)

  const cache = cacheEnabled
    ? fileEntryCache.create(cacheBasename, cacheDirname)
    : null

  const extractFromFile = async (file: string) => {
    const babelOpts = {
      presets: resolvePresets(presets, babelrcDir),
      plugins: resolvePlugins(plugins, babelrcDir)
    }
    /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
    const transformResult = await promisify(transformFile as any)(
      file,
      babelOpts
    )
    /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
    const { metadata } = transformResult as { metadata: any }
    const localeObj = localeMap(locales)
    const result = metadata['react-intl'].messages as Message[]
    for (const { id, defaultMessage, description } of result) {
      for (const locale of locales) {
        const message = defaultLocale === locale ? defaultMessage : ''
        localeObj[locale][id] = withDescriptions
          ? { message, description }
          : message
      }
    }
    return localeObj
  }

  const extractFromCache = async (file: string) => {
    if (cache === null) {
      return extractFromFile(file)
    }

    const cachedLocaleObj = cache?.getFileDescriptor(file) as File | undefined
    const changed = cachedLocaleObj?.changed
    const data = cachedLocaleObj?.meta.data

    if (changed === false && data?.defaultLocale === defaultLocale) {
      return data.localeMap
    }

    const localeObj = await extractFromFile(file)

    if (cachedLocaleObj) {
      cachedLocaleObj.meta.data = { defaultLocale, localeMap: localeObj }
    }

    return localeObj
  }

  const extract = cacheEnabled ? extractFromCache : extractFromFile

  const arr = await Promise.all(files.map(extract))

  if (cache) {
    cache.reconcile()
  }

  return arr.reduce((h, obj) => merge(h, obj), localeMap(locales))
}


================================================
FILE: src/extract-react-intl/readme.md
================================================
# extract-react-intl

## API

### extractReactIntl(locales, pattern, [options])

Return a `Promise` wrapped extracted messages.

#### locales

Type: `Array<string>`

Example: `['en', 'ja']`

#### pattern

Type: `string`

File path with glob.

#### options

Additional options.

#### defaultLocale

Type: `string`<br> Default: `en`

Set default locale for your app.

##### cwd

Type: `string`<br> Default: `.`

**You most likely don't need this.**

Change run path.


================================================
FILE: src/extract-react-intl/test/__snapshots__/test.ts.snap
================================================
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`babel plugin execution order 1`] = `
{
  "en": {
    "src.extract-react-intl.test.pluginOrdering.test": "auto",
  },
}
`;

exports[`extract from file 1`] = `
{
  "en": {
    "components.App.1248161314": "Submit button",
    "components.App.hello": "hello",
    "components.App.world": "world",
    "components/Greeting/welcome": "
    Welcome {name}, you have received {unreadCount, plural,
      =0 {no new messages}
      one {{formattedUnreadCount} new message}
      other {{formattedUnreadCount} new messages}
    } since {formattedLastLoginTime}.
    ",
  },
  "ja": {
    "components.App.1248161314": "",
    "components.App.hello": "",
    "components.App.world": "",
    "components/Greeting/welcome": "",
  },
}
`;

exports[`extract from file by enabling cache and extract from cache 1`] = `
{
  "en": {
    "components.App.1248161314": "Submit button",
    "components.App.hello": "hello",
    "components.App.world": "world",
    "components/Greeting/welcome": "
    Welcome {name}, you have received {unreadCount, plural,
      =0 {no new messages}
      one {{formattedUnreadCount} new message}
      other {{formattedUnreadCount} new messages}
    } since {formattedLastLoginTime}.
    ",
  },
  "ja": {
    "components.App.1248161314": "",
    "components.App.hello": "",
    "components.App.world": "",
    "components/Greeting/welcome": "",
  },
}
`;

exports[`extract from file by enabling cache and extract from cache 2`] = `
{
  "en": {
    "components.App.1248161314": "Submit button",
    "components.App.hello": "hello",
    "components.App.world": "world",
    "components/Greeting/welcome": "
    Welcome {name}, you have received {unreadCount, plural,
      =0 {no new messages}
      one {{formattedUnreadCount} new message}
      other {{formattedUnreadCount} new messages}
    } since {formattedLastLoginTime}.
    ",
  },
  "ja": {
    "components.App.1248161314": "",
    "components.App.hello": "",
    "components.App.world": "",
    "components/Greeting/welcome": "",
  },
}
`;

exports[`extract from file with descriptions 1`] = `
{
  "en": {
    "components.App.hello": {
      "description": "hello message description",
      "message": "hello",
    },
    "components.App.world": {
      "description": "world message description",
      "message": "world",
    },
    "components/Greeting/welcome": {
      "description": "Welcome message description",
      "message": "
    Welcome {name}, you have received {unreadCount, plural,
      =0 {no new messages}
      one {{formattedUnreadCount} new message}
      other {{formattedUnreadCount} new messages}
    } since {formattedLastLoginTime}.
    ",
    },
  },
  "ja": {
    "components.App.hello": {
      "description": "hello message description",
      "message": "",
    },
    "components.App.world": {
      "description": "world message description",
      "message": "",
    },
    "components/Greeting/welcome": {
      "description": "Welcome message description",
      "message": "",
    },
  },
}
`;


================================================
FILE: src/extract-react-intl/test/fixtures/.babelrc
================================================
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "browsers": [
            "last 2 versions",
            "safari >= 7"
          ]
        }
      }
    ],
    "@babel/preset-react",
    "@babel/preset-flow"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties"
  ],
  "env": {
    "react-intl": {
      "plugins": [
        [
          "react-intl-auto",
          {
            "removePrefix": "src.extract-react-intl.test.fixtures"
          }
        ]
      ]
    }
  }
}


================================================
FILE: src/extract-react-intl/test/fixtures/components/App/index.js
================================================
// @flow
import React, { Component } from 'react'
import { FormattedMessage, injectIntl } from 'react-intl'
import Greeting from '../Greeting'
import messages from './messages'

injectIntl(({ intl }) => {
  const label = intl.formatMessage({ defaultMessage: "Submit button" })

  return <button aria-label={label}>{label}</button>
});


export default class App extends Component {
  render() {
    const user = {
      name: 'Eric',
      unreadCount: 4,
      lastLoginTime: Date.now() - 1000 * 60 * 60 * 24
    }

    return (
      <div>
        <FormattedMessage {...messages.hello} />
        <Greeting user={user} />
      </div>
    )
  }
}


================================================
FILE: src/extract-react-intl/test/fixtures/components/App/messages.js
================================================
// @flow
import { defineMessages } from 'react-intl'

export default defineMessages({
  // hello message description
  hello: 'hello',
  // world message description
  world: 'world'
})


================================================
FILE: src/extract-react-intl/test/fixtures/components/Greeting/index.js
================================================
// @flow
import React, { Component } from 'react'
import {
  FormattedMessage,
  FormattedNumber,
  FormattedRelative
} from 'react-intl'
import messages from './messages'

type Props = {
  user: {
    name: string,
    unreadCount: number,
    lastLoginTime: number
  }
}

export default class Greeting extends Component {
  props: Props

  render() {
    const { user } = this.props

    return (
      <p>
        <FormattedMessage
          {...messages.welcome}
          values={{
            name: <b>{user.name}</b>,
            unreadCount: user.unreadCount,
            formattedUnreadCount: (
              <b>
                <FormattedNumber value={user.unreadCount} />
              </b>
            ),
            formattedLastLoginTime: (
              <FormattedRelative value={user.lastLoginTime} />
            )
          }}
        />
      </p>
    )
  }
}

function defaultMessage() {
  return 'hello'
}


================================================
FILE: src/extract-react-intl/test/fixtures/components/Greeting/messages.js
================================================
// @flow
import { defineMessages } from 'react-intl'

export default defineMessages({
  // Welcome message description
  welcome: {
    id: 'components/Greeting/welcome',
    defaultMessage: `
    Welcome {name}, you have received {unreadCount, plural,
      =0 {no new messages}
      one {{formattedUnreadCount} new message}
      other {{formattedUnreadCount} new messages}
    } since {formattedLastLoginTime}.
    `
  }
})


================================================
FILE: src/extract-react-intl/test/fixtures/components/LanguageProvider/index.js
================================================
// @flow
import React, { Component } from 'react'
import { addLocaleData, IntlProvider } from 'react-intl'
import enLocaleData from 'react-intl/locale-data/en'
import jaLocaleData from 'react-intl/locale-data/ja'

import enMessages from '../../translations/en.json'
import jaMessages from '../../translations/ja.json'

addLocaleData(enLocaleData)
addLocaleData(jaLocaleData)

const messages = {
  en: enMessages,
  ja: jaMessages
}

export default class LanguageProvider extends Component {
  props: { children?: React$Element<*> }
  state: { locale: string } = { locale: 'en' }

  render() {
    const { locale } = this.state
    return (
      <div>
        <IntlProvider locale={locale} messages={messages[locale]}>
          {this.props.children}
        </IntlProvider>
        <a onClick={() => this.setState({ locale: 'en' })}>English</a>
        /
        <a onClick={() => this.setState({ locale: 'ja' })}>日本語</a>
      </div>
    )
  }
}


================================================
FILE: src/extract-react-intl/test/fixtures/index.html
================================================
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
      <title>Example</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="/assets/bundle.js"></script>
  </body>
</html>


================================================
FILE: src/extract-react-intl/test/fixtures/index.js
================================================
// @flow
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'
import LanguageProvider from './components/LanguageProvider'

ReactDOM.render(
  <LanguageProvider>
    <App />
  </LanguageProvider>,
  document.getElementById('root')
)


================================================
FILE: src/extract-react-intl/test/pluginOrdering/.babelrc
================================================
{
  "presets": ["@babel/preset-flow"],
  "plugins": ["react-intl-auto"]
}


================================================
FILE: src/extract-react-intl/test/pluginOrdering/messages.js
================================================
import { defineMessages } from 'react-intl'

export default defineMessages({
  test: 'auto'
})


================================================
FILE: src/extract-react-intl/test/resolution/.babelrc
================================================
{
  "presets": ["@babel/preset-flow"],
  "plugins": ["react-intl"]
}


================================================
FILE: src/extract-react-intl/test/resolution/messages.js
================================================
import { defineMessages } from 'react-intl'

export default defineMessages({
  test: {
    id: 'test',
    defaultMessage: 'test'
  }
})


================================================
FILE: src/extract-react-intl/test/test.ts
================================================
import m from '..'

const pattern = 'src/extract-react-intl/test/fixtures/**/*.js'
const locales = ['en', 'ja']

test('extract from file', async () => {
  process.env.BABEL_ENV = 'react-intl'
  const x = await m(locales, pattern, {
    defaultLocale: 'en',
    cwd: `${__dirname}/fixtures`,
    extractFromFormatMessageCall: true
  })
  expect(x).toMatchSnapshot()
})

test('extract from file by enabling cache and extract from cache', async () => {
  process.env.BABEL_ENV = 'react-intl'
  const x = await m(locales, pattern, {
    defaultLocale: 'en',
    cwd: `${__dirname}/fixtures`,
    extractFromFormatMessageCall: true,
    cache: true,
    cacheLocation: '.test-cache'
  })
  expect(x).toMatchSnapshot()

  const y = await m(locales, pattern, {
    defaultLocale: 'en',
    cwd: `${__dirname}/fixtures`,
    extractFromFormatMessageCall: true,
    cache: true,
    cacheLocation: '.test-cache'
  })
  expect(y).toMatchSnapshot()
})

// TODO: fix
test.skip('babelrc path resolution', async () => {
  const x = await m(['en'], './extract-react-intl/test/resolution/**/*.js', {
    defaultLocale: 'en',
    cwd: `${__dirname}/resolution`
  })
  expect(x).toMatchSnapshot()
})

test('babel plugin execution order', async () => {
  const x = await m(
    ['en'],
    'src/extract-react-intl/test/pluginOrdering/**/*.js',
    { defaultLocale: 'en', cwd: `${__dirname}/pluginOrdering` }
  )
  expect(x).toMatchSnapshot()
})

test('error', async () => {
  expect.assertions(1)
  await m(locales, 'notfound', {
    defaultLocale: 'en',
    cwd: `${__dirname}/fixtures`
  }).catch((error) => {
    expect(error.message).toMatch('File not found')
  })
})

test('extract from file with descriptions', async () => {
  process.env.BABEL_ENV = 'react-intl'
  const x = await m(locales, pattern, {
    defaultLocale: 'en',
    cwd: './test/fixtures',
    withDescriptions: true
  })
  expect(x).toMatchSnapshot()
})


================================================
FILE: src/global.d.ts
================================================
declare module 'read-babelrc-up' {
  function sync(opts: { cwd: string }): {
    path: string
    babel: import('@babel/core').TransformOptions
  }
}


================================================
FILE: src/index.ts
================================================
import path from 'path'
import fs from 'fs'
import yaml from 'js-yaml'
import { promisify } from 'util'
import { flatten, unflatten } from 'flat'
import loadJsonFile from 'load-json-file'
import writeJsonFile from 'write-json-file'
import sortKeys from 'sort-keys'
import _extractReactIntl from './extract-react-intl/index.js'

const writeJson = (outputPath: string, obj: object, indent: number) => {
  return writeJsonFile(`${outputPath}.json`, obj, { indent })
}

const writeYaml = (outputPath: string, obj: object, indent: number) => {
  return promisify(fs.writeFile)(
    `${outputPath}.yml`,
    yaml.dump(obj, { indent }),
    'utf8'
  )
}

const isJson = (ext: string) => ext === 'json'

function loadLocaleFiles(locales: string[], buildDir: string, ext: string) {
  const oldLocaleMaps: Record<string, Record<string, object>> = {}

  try {
    fs.mkdirSync(buildDir, { recursive: true })
  } catch {
    // Directory already exists or other error, continue
  }

  for (const locale of locales) {
    const file = path.resolve(buildDir, `${locale}.${ext}`)
    // Initialize json file
    try {
      const output = isJson(ext) ? JSON.stringify({}) : yaml.dump({})
      fs.writeFileSync(file, output, { flag: 'wx' })
    } catch (error: unknown) {
      if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {
        throw error
      }
    }

    let messages = isJson(ext)
      ? loadJsonFile.sync(file)
      : yaml.load(fs.readFileSync(file, 'utf8'), { json: true })

    messages = flatten(messages)

    oldLocaleMaps[locale] = {}
    for (const messageKey of Object.keys(messages as object)) {
      const message = (messages as Record<string, unknown>)[messageKey]
      if (message && typeof message === 'string' && message !== '') {
        oldLocaleMaps[locale][messageKey] = (
          messages as Record<string, unknown>
        )[messageKey] as object
      }
    }
  }

  return oldLocaleMaps
}

type Opts = {
  [key: string]: unknown
  defaultLocale: string
  format?: string
  flat?: boolean
  overwriteDefault?: boolean
  indent?: number
}

const extractMessage = async (
  locales: string[],
  pattern: string,
  buildDir: string,
  {
    format = 'json',
    flat = isJson(format),
    defaultLocale = 'en',
    overwriteDefault = true,
    indent = 2,
    ...opts
  }: Opts = {
    defaultLocale: 'en'
  }
) => {
  if (!Array.isArray(locales)) {
    throw new TypeError(`Expected a Array, got ${typeof locales}`)
  }

  if (typeof pattern !== 'string') {
    throw new TypeError(`Expected a string, got ${typeof pattern}`)
  }

  if (typeof buildDir !== 'string') {
    throw new TypeError(`Expected a string, got ${typeof buildDir}`)
  }

  const ext = isJson(format) ? 'json' : 'yml'

  const oldLocaleMaps = loadLocaleFiles(locales, buildDir, ext)

  const extractorOptions = {
    defaultLocale,
    withDescriptions: false,
    cwd: process.cwd(),
    extractFromFormatMessageCall: true,
    ...opts
  }

  const newLocaleMaps = await _extractReactIntl(
    locales,
    pattern,
    extractorOptions
  )

  return Promise.all(
    locales.map((locale) => {
      // If the default locale, overwrite the origin file
      let localeMap =
        locale === defaultLocale && overwriteDefault
          ? // Create a clone so we can use only current valid messages below
            { ...oldLocaleMaps[locale], ...newLocaleMaps[locale] }
          : { ...newLocaleMaps[locale], ...oldLocaleMaps[locale] }
      // Only keep existing keys
      localeMap = Object.fromEntries(
        Object.entries(localeMap).filter(([key]) =>
          Object.keys(newLocaleMaps[locale]).includes(key)
        )
      )

      const fomattedLocaleMap: object = flat
        ? sortKeys(localeMap, { deep: true })
        : sortKeys(unflatten(localeMap, { object: true }), { deep: true })

      const fn = isJson(format) ? writeJson : writeYaml

      return fn(path.resolve(buildDir, locale), fomattedLocaleMap, indent)
    })
  )
}

extractMessage.extractReactIntl = _extractReactIntl

export default extractMessage


================================================
FILE: src/test/fixtures/custom/a/messages.js
================================================
import { defineMessages } from '../i18n'

export default defineMessages({
  hello: {
    id: 'a.custom.hello',
    defaultMessage: 'hello'
  },
  world: {
    id: 'a.custom.world',
    defaultMessage: 'world'
  }
})


================================================
FILE: src/test/fixtures/custom/b/messages.js
================================================
import { defineMessages } from '../i18n'

export default defineMessages({
  hello: {
    id: 'b.custom.message',
    defaultMessage: 'Message'
  }
})


================================================
FILE: src/test/fixtures/custom/i18n.js
================================================
export { defineMessages } from 'react-intl'


================================================
FILE: src/test/fixtures/default/a/App.js
================================================
import React from 'react'
import { useIntl } from 'react-intl'

export const SubmitButton = () => {
  const intl = useIntl()
  const label = intl.formatMessage({
    id: 'a.submit',
    defaultMessage: 'Submit Button'
  })
  return <button aria-label={label}>{label}</button>
}


================================================
FILE: src/test/fixtures/default/a/messages.js
================================================
import { defineMessages } from 'react-intl'

export default defineMessages({
  hello: {
    id: 'a.hello',
    defaultMessage: 'hello'
  },
  world: {
    id: 'a.world',
    defaultMessage: 'world'
  }
})


================================================
FILE: src/test/fixtures/default/b/messages.js
================================================
import { defineMessages } from 'react-intl'

export default defineMessages({
  hello: {
    id: 'b.hello',
    defaultMessage: 'hello'
  },
  world: {
    id: 'b.world',
    defaultMessage: 'world'
  }
})


================================================
FILE: src/test/fixtures/removed/a/messages.js
================================================
import { defineMessages } from 'react-intl'

export default defineMessages({
  hello: {
    id: 'a.hello',
    defaultMessage: 'hello'
  }
})


================================================
FILE: src/test/fixtures/removed/b/messages.js
================================================
import { defineMessages } from 'react-intl'

export default defineMessages({
  hello: {
    id: 'b.hello',
    defaultMessage: 'hello'
  },
  world: {
    id: 'b.world',
    defaultMessage: 'world'
  }
})


================================================
FILE: src/test/fixtures/unsorted/a/messages.js
================================================
import { defineMessages } from 'react-intl'

export default defineMessages({
  hello: {
    id: 'a.hello',
    defaultMessage: 'hello'
  },
  helloZ: {
    id: 'z.hello',
    defaultMessage: 'hello'
  },
  helloC: {
    id: 'c.hello',
    defaultMessage: 'hello'
  },
  world: {
    id: 'a.world',
    defaultMessage: 'world'
  }
})


================================================
FILE: src/test/fixtures/unsorted/b/messages.js
================================================
import { defineMessages } from 'react-intl'

export default defineMessages({
  hello: {
    id: 'b.hello',
    defaultMessage: 'hello'
  },
  helloY: {
    id: 'y.hello',
    defaultMessage: 'hello'
  },
  world: {
    id: 'b.world',
    defaultMessage: 'world'
  }
})


================================================
FILE: src/test/json/__snapshots__/test.ts.snap
================================================
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`export json - nest 1`] = `
{
  "a": {
    "hello": "hello",
    "submit": "Submit Button",
    "world": "world",
  },
  "b": {
    "hello": "hello",
    "world": "world",
  },
}
`;

exports[`export json - nest 2`] = `
{
  "a": {
    "hello": "",
    "submit": "",
    "world": "",
  },
  "b": {
    "hello": "",
    "world": "",
  },
}
`;

exports[`export json 1`] = `
{
  "a.hello": "hello",
  "a.submit": "Submit Button",
  "a.world": "world",
  "b.hello": "hello",
  "b.world": "world",
}
`;

exports[`export json 2`] = `
{
  "a.hello": "",
  "a.submit": "",
  "a.world": "",
  "b.hello": "",
  "b.world": "",
}
`;

exports[`export json with removed messages 1`] = `
{
  "a.hello": "hello",
  "a.submit": "Submit Button",
  "a.world": "world",
  "b.hello": "hello",
  "b.world": "world",
}
`;

exports[`export json with removed messages 2`] = `
{
  "a.hello": "",
  "a.submit": "",
  "a.world": "",
  "b.hello": "",
  "b.world": "",
}
`;

exports[`export json with removed messages 3`] = `
{
  "a.hello": "hello",
  "b.hello": "hello",
  "b.world": "world",
}
`;

exports[`export json with removed messages 4`] = `
{
  "a.hello": "",
  "b.hello": "",
  "b.world": "",
}
`;

exports[`export using custom module 1`] = `
{
  "a.custom.hello": "hello",
  "a.custom.world": "world",
  "b.custom.message": "Message",
}
`;

exports[`export using custom module 2`] = `
{
  "a.custom.hello": "",
  "a.custom.world": "",
  "b.custom.message": "",
}
`;

exports[`sort keys 1`] = `
[
  "a.hello",
  "a.world",
  "b.hello",
  "b.world",
  "c.hello",
  "y.hello",
  "z.hello",
]
`;

exports[`sort keys 2`] = `
[
  "a.hello",
  "a.world",
  "b.hello",
  "b.world",
  "c.hello",
  "y.hello",
  "z.hello",
]
`;

exports[`with overwriteDefault 1`] = `
{
  "a.custom.hello": "hello",
  "a.custom.world": "world",
  "b.custom.message": "Default Message",
}
`;

exports[`with overwriteDefault 2`] = `
{
  "a.custom.hello": "",
  "a.custom.world": "",
  "b.custom.message": "",
}
`;


================================================
FILE: src/test/json/test.ts
================================================
import fs from 'fs'
import path from 'path'
import tempy from 'tempy'
import m from '../..'

test('export json', async () => {
  const tmp = tempy.directory()
  await m(['en', 'ja'], 'src/test/fixtures/default/**/*.js', tmp)
  const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
  const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
  expect(en).toMatchSnapshot()
  expect(ja).toMatchSnapshot()
})

test('export json with removed messages', async () => {
  const tmp = tempy.directory()
  await m(['en', 'ja'], 'src/test/fixtures/default/**/*.js', tmp)
  const enBefore = JSON.parse(
    fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8')
  )
  const jaBefore = JSON.parse(
    fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8')
  )
  expect(enBefore).toMatchSnapshot()
  expect(jaBefore).toMatchSnapshot()
  await m(['en', 'ja'], 'src/test/fixtures/removed/**/*.js', tmp)
  const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
  const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
  expect(en).toMatchSnapshot()
  expect(ja).toMatchSnapshot()
})

test('export json - nest', async () => {
  const tmp = tempy.directory()
  await m(['en', 'ja'], 'src/test/fixtures/default/**/*.js', tmp, {
    defaultLocale: 'en',
    flat: false
  })
  const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
  const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
  expect(en).toMatchSnapshot()
  expect(ja).toMatchSnapshot()
})

test('sort keys', async () => {
  const tmp = tempy.directory()
  const enPath = path.resolve(tmp, 'en.json')
  const jaPath = path.resolve(tmp, 'ja.json')

  await m(['en', 'ja'], 'src/test/fixtures/unsorted/**/*.js', tmp)
  const en = JSON.parse(fs.readFileSync(enPath, 'utf8'))
  const ja = JSON.parse(fs.readFileSync(jaPath, 'utf8'))

  expect(Object.keys(en)).toMatchSnapshot()
  expect(Object.keys(ja)).toMatchSnapshot()
})

test('export using custom module', async () => {
  const tmp = tempy.directory()
  await m(['en', 'ja'], 'src/test/fixtures/custom/**/*.js', tmp, {
    defaultLocale: 'en',
    moduleSourceName: '../i18n'
  })
  const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
  const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
  expect(en).toMatchSnapshot()
  expect(ja).toMatchSnapshot()
})

test('with overwriteDefault', async () => {
  const tmp = tempy.directory()
  fs.writeFileSync(
    path.resolve(tmp, 'en.json'),
    JSON.stringify(
      {
        'a.custom.hello': 'hello',
        'a.custom.world': 'world',
        'b.custom.message': 'Default Message'
      },
      null,
      2
    ),
    'utf8'
  )
  await m(['en', 'ja'], 'src/test/fixtures/custom/**/*.js', tmp, {
    defaultLocale: 'en',
    moduleSourceName: '../i18n',
    overwriteDefault: false
  })
  const en = JSON.parse(fs.readFileSync(path.resolve(tmp, 'en.json'), 'utf8'))
  const ja = JSON.parse(fs.readFileSync(path.resolve(tmp, 'ja.json'), 'utf8'))
  expect(en).toMatchSnapshot()
  expect(ja).toMatchSnapshot()
})


================================================
FILE: src/test/test.ts
================================================
import m from '..'

test('errors', async () => {
  // @ts-expect-error testing invalid argument type
  await expect(m('hello')).rejects.toThrow('Expected a Array')
  // @ts-expect-error testing invalid argument type
  await expect(m(['en', 'ja'], 2)).rejects.toThrow('Expected a string')
  // @ts-expect-error testing invalid argument type
  await expect(m(['en', 'ja'], 'app/', 2)).rejects.toThrow('Expected a string')
})


================================================
FILE: src/test/yaml/__snapshots__/test.ts.snap
================================================
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`export yaml - flat 1`] = `
{
  "a.hello": "hello",
  "a.submit": "Submit Button",
  "a.world": "world",
  "b.hello": "hello",
  "b.world": "world",
}
`;

exports[`export yaml - flat 2`] = `
{
  "a.hello": "",
  "a.submit": "",
  "a.world": "",
  "b.hello": "",
  "b.world": "",
}
`;

exports[`export yaml 1`] = `
{
  "a": {
    "hello": "hello",
    "submit": "Submit Button",
    "world": "world",
  },
  "b": {
    "hello": "hello",
    "world": "world",
  },
}
`;

exports[`export yaml 2`] = `
{
  "a": {
    "hello": "",
    "submit": "",
    "world": "",
  },
  "b": {
    "hello": "",
    "world": "",
  },
}
`;

exports[`exsit yaml 1`] = `
{
  "a": {
    "hello": "hello",
    "submit": "Submit Button",
    "world": "world",
  },
  "b": {
    "hello": "hello",
    "world": "world",
  },
}
`;

exports[`exsit yaml 2`] = `
{
  "a": {
    "hello": "hello2",
    "submit": "",
    "world": "",
  },
  "b": {
    "hello": "",
    "world": "",
  },
}
`;


================================================
FILE: src/test/yaml/test.ts
================================================
import fs from 'fs'
import path from 'path'
import tempy from 'tempy'
import tempWrite from 'temp-write'
import yaml from 'js-yaml'
import m from '../..'

const fixturesPath = 'src/test/fixtures/default/**/*.js'

const yamlLoad = (tmp: string, file = '') =>
  yaml.load(fs.readFileSync(path.resolve(tmp, file), 'utf8'))

const defaultLocale = 'en'

test('export yaml', async () => {
  const tmp = tempy.directory()
  await m(['en', 'ja'], fixturesPath, tmp, { defaultLocale, format: 'yaml' })

  expect(yamlLoad(tmp, 'en.yml')).toMatchSnapshot()
  expect(yamlLoad(tmp, 'ja.yml')).toMatchSnapshot()
})

test('export yaml - flat', async () => {
  const tmp = tempy.directory()
  await m(['en', 'ja'], fixturesPath, tmp, {
    defaultLocale,
    format: 'yaml',
    flat: true
  })

  expect(yamlLoad(tmp, 'en.yml')).toMatchSnapshot()
  expect(yamlLoad(tmp, 'ja.yml')).toMatchSnapshot()
})

test('exsit yaml', async () => {
  const x = { a: { hello: 'hello2' } }

  const tmpEn = tempWrite.sync(yaml.dump(x), 'en.yml')
  await m(['en'], fixturesPath, path.dirname(tmpEn), {
    defaultLocale,
    format: 'yaml'
  })
  expect(yaml.load(fs.readFileSync(tmpEn, 'utf8'))).toMatchSnapshot()

  const tmpJa = tempWrite.sync(yaml.dump(x), 'ja.yml')
  await m(['ja'], fixturesPath, path.dirname(tmpJa), {
    defaultLocale,
    format: 'yaml'
  })

  expect(yaml.load(fs.readFileSync(tmpJa, 'utf8'))).toMatchSnapshot()
})


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    /* Language and Environment */
    "target": "ES2022",
    "lib": ["ES2022", "DOM"],
    "module": "Node16",
    "moduleResolution": "Node16",

    /* Modules */
    "allowImportingTsExtensions": false,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,

    /* Emit */
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "dist",
    "removeComments": false,
    "importHelpers": false,
    "downlevelIteration": false,

    /* Interop Constraints */
    "allowJs": false,
    "checkJs": false,

    /* Type Checking */
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "useUnknownInCatchVariables": true,
    "alwaysStrict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "exactOptionalPropertyTypes": false,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": false,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": false,

    /* Completeness */
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": [
    "node_modules",
    "dist",
    "**/*.test.ts",
    "**/*.spec.ts",
    "**/test.*"
  ]
}
Download .txt
gitextract_9ar36m_8/

├── .all-contributorsrc
├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .prettierignore
├── .prettierrc
├── CODE_OF_CONDUCT.md
├── eslint.config.mjs
├── example/
│   ├── basic/
│   │   ├── babel.config.js
│   │   ├── i18n/
│   │   │   ├── .keep
│   │   │   ├── en.json
│   │   │   └── ja.json
│   │   ├── package.json
│   │   └── src/
│   │       ├── App.jsx
│   │       └── messages.js
│   └── with-typescript/
│       ├── babel.config.js
│       ├── i18n/
│       │   ├── en.json
│       │   └── ja.json
│       ├── package.json
│       └── src/
│           ├── App.tsx
│           └── messages.ts
├── jest.config.js
├── license
├── package.json
├── readme.md
├── src/
│   ├── cli.ts
│   ├── extract-react-intl/
│   │   ├── index.ts
│   │   ├── readme.md
│   │   └── test/
│   │       ├── __snapshots__/
│   │       │   └── test.ts.snap
│   │       ├── fixtures/
│   │       │   ├── .babelrc
│   │       │   ├── components/
│   │       │   │   ├── App/
│   │       │   │   │   ├── index.js
│   │       │   │   │   └── messages.js
│   │       │   │   ├── Greeting/
│   │       │   │   │   ├── index.js
│   │       │   │   │   └── messages.js
│   │       │   │   └── LanguageProvider/
│   │       │   │       └── index.js
│   │       │   ├── index.html
│   │       │   └── index.js
│   │       ├── pluginOrdering/
│   │       │   ├── .babelrc
│   │       │   └── messages.js
│   │       ├── resolution/
│   │       │   ├── .babelrc
│   │       │   └── messages.js
│   │       └── test.ts
│   ├── global.d.ts
│   ├── index.ts
│   └── test/
│       ├── fixtures/
│       │   ├── custom/
│       │   │   ├── a/
│       │   │   │   └── messages.js
│       │   │   ├── b/
│       │   │   │   └── messages.js
│       │   │   └── i18n.js
│       │   ├── default/
│       │   │   ├── a/
│       │   │   │   ├── App.js
│       │   │   │   └── messages.js
│       │   │   └── b/
│       │   │       └── messages.js
│       │   ├── removed/
│       │   │   ├── a/
│       │   │   │   └── messages.js
│       │   │   └── b/
│       │   │       └── messages.js
│       │   └── unsorted/
│       │       ├── a/
│       │       │   └── messages.js
│       │       └── b/
│       │           └── messages.js
│       ├── json/
│       │   ├── __snapshots__/
│       │   │   └── test.ts.snap
│       │   └── test.ts
│       ├── test.ts
│       └── yaml/
│           ├── __snapshots__/
│           │   └── test.ts.snap
│           └── test.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (12 symbols across 4 files)

FILE: src/extract-react-intl/index.ts
  type LocaleMap (line 16) | type LocaleMap = Record<string, Record<string, unknown>>
  type Options (line 60) | type Options = {
  type Message (line 69) | type Message = {
  type CacheData (line 75) | type CacheData = {
  type File (line 80) | type File = FileDescriptor & {

FILE: src/extract-react-intl/test/fixtures/components/App/index.js
  class App (line 14) | class App extends Component {
    method render (line 15) | render() {

FILE: src/extract-react-intl/test/fixtures/components/Greeting/index.js
  class Greeting (line 18) | class Greeting extends Component {
    method render (line 21) | render() {
  function defaultMessage (line 46) | function defaultMessage() {

FILE: src/index.ts
  function loadLocaleFiles (line 25) | function loadLocaleFiles(locales: string[], buildDir: string, ext: strin...
  type Opts (line 66) | type Opts = {
Condensed preview — 66 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (72K chars).
[
  {
    "path": ".all-contributorsrc",
    "chars": 4424,
    "preview": "{\n  \"projectName\": \"extract-react-intl-messages\",\n  \"projectOwner\": \"akameco\",\n  \"files\": [\"readme.md\"],\n  \"imageSize\": "
  },
  {
    "path": ".babelrc",
    "chars": 164,
    "preview": "{\n  \"presets\": [\n    [\n      \"@babel/preset-env\",\n      {\n        \"targets\": {\n          \"node\": \"current\"\n        }\n   "
  },
  {
    "path": ".editorconfig",
    "chars": 147,
    "preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ni"
  },
  {
    "path": ".eslintrc",
    "chars": 114,
    "preview": "{\n  \"extends\": [\"precure/auto\"],\n  \"rules\": {\n    \"@typescript-eslint/explicit-function-return-type\": \"off\"\n  }\n}\n"
  },
  {
    "path": ".gitattributes",
    "chars": 29,
    "preview": "* text=auto\n*.js text eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 420,
    "preview": "<!--\nThanks for your interest in the project.\nI appreciate bugs filed and PRs submitted!\nI'll probably ask you to submit"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 738,
    "preview": "<!--\nThanks for your interest in the project. I appreciate bugs filed and PRs submitted!\nEnglish/日本語(日本語で入力して大丈夫です。日本語の方"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 413,
    "preview": "name: test\n\non: [push]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        # LTS (20.x) and l"
  },
  {
    "path": ".gitignore",
    "chars": 359,
    "preview": "# Dependencies\nnode_modules/\n\n# Build outputs\nlib/\ndist/\ncompiled/\n\n# Cache files\n.eslintcache\n.test-cache\n\n# OS files\n."
  },
  {
    "path": ".husky/pre-commit",
    "chars": 16,
    "preview": "npx lint-staged\n"
  },
  {
    "path": ".prettierignore",
    "chars": 46,
    "preview": "**/test/fixtures/**\n.github\ndist\npackage.json\n"
  },
  {
    "path": ".prettierrc",
    "chars": 70,
    "preview": "{\n  \"semi\": false,\n  \"singleQuote\": true,\n  \"trailingComma\": \"none\"\n}\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3216,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "eslint.config.mjs",
    "chars": 416,
    "preview": "// ESLint v9+ config\nimport js from '@eslint/js'\nimport tseslint from 'typescript-eslint'\n\nexport default [\n  js.configs"
  },
  {
    "path": "example/basic/babel.config.js",
    "chars": 97,
    "preview": "module.exports = function (api) {\n  api.cache(true)\n\n  return {\n    presets: ['react-app']\n  }\n}\n"
  },
  {
    "path": "example/basic/i18n/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "example/basic/i18n/en.json",
    "chars": 135,
    "preview": "{\n  \"App\": {\n    \"hello\": \"Hello Button\",\n    \"submit\": \"Submit Button\"\n  },\n  \"a\": {\n    \"hello\": \"hello\",\n    \"world\":"
  },
  {
    "path": "example/basic/i18n/ja.json",
    "chars": 100,
    "preview": "{\n  \"App\": {\n    \"hello\": \"\",\n    \"submit\": \"\"\n  },\n  \"a\": {\n    \"hello\": \"\",\n    \"world\": \"\"\n  }\n}\n"
  },
  {
    "path": "example/basic/package.json",
    "chars": 397,
    "preview": "{\n  \"name\": \"basic\",\n  \"version\": \"1.0.0\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"i18n\": \"NODE_ENV=development extract-"
  },
  {
    "path": "example/basic/src/App.jsx",
    "chars": 501,
    "preview": "import React from 'react'\nimport { injectIntl, useIntl } from 'react-intl'\n\nexport const SubmitButton = injectIntl(({ in"
  },
  {
    "path": "example/basic/src/messages.js",
    "chars": 260,
    "preview": "/* eslint-disable import/no-extraneous-dependencies */\nimport { defineMessages } from 'react-intl'\n\nexport default defin"
  },
  {
    "path": "example/with-typescript/babel.config.js",
    "chars": 135,
    "preview": "module.exports = function (api) {\n  api.cache(true)\n\n  return {\n    presets: ['@babel/preset-react', '@babel/preset-type"
  },
  {
    "path": "example/with-typescript/i18n/en.json",
    "chars": 135,
    "preview": "{\n  \"App\": {\n    \"hello\": \"Hello Button\",\n    \"submit\": \"Submit Button\"\n  },\n  \"a\": {\n    \"hello\": \"hello\",\n    \"world\":"
  },
  {
    "path": "example/with-typescript/i18n/ja.json",
    "chars": 100,
    "preview": "{\n  \"App\": {\n    \"hello\": \"\",\n    \"submit\": \"\"\n  },\n  \"a\": {\n    \"hello\": \"\",\n    \"world\": \"\"\n  }\n}\n"
  },
  {
    "path": "example/with-typescript/package.json",
    "chars": 453,
    "preview": "{\n  \"name\": \"with-typescript\",\n  \"version\": \"1.0.0\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"i18n\": \"extract-messages -l"
  },
  {
    "path": "example/with-typescript/src/App.tsx",
    "chars": 501,
    "preview": "import React from 'react'\nimport { injectIntl, useIntl } from 'react-intl'\n\nexport const SubmitButton = injectIntl(({ in"
  },
  {
    "path": "example/with-typescript/src/messages.ts",
    "chars": 205,
    "preview": "import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  hello: {\n    id: 'a.hello',\n    defaultMe"
  },
  {
    "path": "jest.config.js",
    "chars": 470,
    "preview": "export default {\n  testPathIgnorePatterns: [\n    '<rootDir>[/\\\\\\\\](dist|compiled|node_modules)[/\\\\\\\\]'\n  ],\n  testEnviro"
  },
  {
    "path": "license",
    "chars": 1111,
    "preview": "The MIT License (MIT)\n\nCopyright (c) akameco <akameco.t@gmail.com> (akameco.github.io)\n\nPermission is hereby granted, fr"
  },
  {
    "path": "package.json",
    "chars": 2626,
    "preview": "{\n  \"name\": \"extract-react-intl-messages\",\n  \"version\": \"5.0.0\",\n  \"description\": \"Extract react-intl messages\",\n  \"lice"
  },
  {
    "path": "readme.md",
    "chars": 12403,
    "preview": "# extract-react-intl-messages\n\n[![test](https://github.com/akameco/extract-react-intl-messages/workflows/test/badge.svg)"
  },
  {
    "path": "src/cli.ts",
    "chars": 2185,
    "preview": "#!/usr/bin/env node\n \nimport meow from 'meow'\nimport extractMessage from './index.js'\n\nconst cli = meow(\n  `\n  Usage\n  $"
  },
  {
    "path": "src/extract-react-intl/index.ts",
    "chars": 5280,
    "preview": "import path from 'path'\nimport { glob } from 'glob'\nimport { promisify } from 'util'\nimport merge from 'lodash.merge'\nim"
  },
  {
    "path": "src/extract-react-intl/readme.md",
    "chars": 465,
    "preview": "# extract-react-intl\n\n## API\n\n### extractReactIntl(locales, pattern, [options])\n\nReturn a `Promise` wrapped extracted me"
  },
  {
    "path": "src/extract-react-intl/test/__snapshots__/test.ts.snap",
    "chars": 3078,
    "preview": "// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing\n\nexports[`babel plugin execution order 1`] = `\n{\n  \"en\": {\n"
  },
  {
    "path": "src/extract-react-intl/test/fixtures/.babelrc",
    "chars": 530,
    "preview": "{\n  \"presets\": [\n    [\n      \"@babel/preset-env\",\n      {\n        \"targets\": {\n          \"browsers\": [\n            \"last"
  },
  {
    "path": "src/extract-react-intl/test/fixtures/components/App/index.js",
    "chars": 649,
    "preview": "// @flow\nimport React, { Component } from 'react'\nimport { FormattedMessage, injectIntl } from 'react-intl'\nimport Greet"
  },
  {
    "path": "src/extract-react-intl/test/fixtures/components/App/messages.js",
    "chars": 186,
    "preview": "// @flow\nimport { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  // hello message description\n  h"
  },
  {
    "path": "src/extract-react-intl/test/fixtures/components/Greeting/index.js",
    "chars": 927,
    "preview": "// @flow\nimport React, { Component } from 'react'\nimport {\n  FormattedMessage,\n  FormattedNumber,\n  FormattedRelative\n} "
  },
  {
    "path": "src/extract-react-intl/test/fixtures/components/Greeting/messages.js",
    "chars": 428,
    "preview": "// @flow\nimport { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  // Welcome message description\n "
  },
  {
    "path": "src/extract-react-intl/test/fixtures/components/LanguageProvider/index.js",
    "chars": 948,
    "preview": "// @flow\nimport React, { Component } from 'react'\nimport { addLocaleData, IntlProvider } from 'react-intl'\nimport enLoca"
  },
  {
    "path": "src/extract-react-intl/test/fixtures/index.html",
    "chars": 197,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n      <title>Example</title>\n  </head>\n  <body>\n    <div id=\""
  },
  {
    "path": "src/extract-react-intl/test/fixtures/index.js",
    "chars": 274,
    "preview": "// @flow\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport App from './components/App'\nimport LanguagePr"
  },
  {
    "path": "src/extract-react-intl/test/pluginOrdering/.babelrc",
    "chars": 74,
    "preview": "{\n  \"presets\": [\"@babel/preset-flow\"],\n  \"plugins\": [\"react-intl-auto\"]\n}\n"
  },
  {
    "path": "src/extract-react-intl/test/pluginOrdering/messages.js",
    "chars": 95,
    "preview": "import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  test: 'auto'\n})\n"
  },
  {
    "path": "src/extract-react-intl/test/resolution/.babelrc",
    "chars": 69,
    "preview": "{\n  \"presets\": [\"@babel/preset-flow\"],\n  \"plugins\": [\"react-intl\"]\n}\n"
  },
  {
    "path": "src/extract-react-intl/test/resolution/messages.js",
    "chars": 137,
    "preview": "import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  test: {\n    id: 'test',\n    defaultMessag"
  },
  {
    "path": "src/extract-react-intl/test/test.ts",
    "chars": 1909,
    "preview": "import m from '..'\n\nconst pattern = 'src/extract-react-intl/test/fixtures/**/*.js'\nconst locales = ['en', 'ja']\n\ntest('e"
  },
  {
    "path": "src/global.d.ts",
    "chars": 150,
    "preview": "declare module 'read-babelrc-up' {\n  function sync(opts: { cwd: string }): {\n    path: string\n    babel: import('@babel/"
  },
  {
    "path": "src/index.ts",
    "chars": 4040,
    "preview": "import path from 'path'\nimport fs from 'fs'\nimport yaml from 'js-yaml'\nimport { promisify } from 'util'\nimport { flatten"
  },
  {
    "path": "src/test/fixtures/custom/a/messages.js",
    "chars": 216,
    "preview": "import { defineMessages } from '../i18n'\n\nexport default defineMessages({\n  hello: {\n    id: 'a.custom.hello',\n    defau"
  },
  {
    "path": "src/test/fixtures/custom/b/messages.js",
    "chars": 150,
    "preview": "import { defineMessages } from '../i18n'\n\nexport default defineMessages({\n  hello: {\n    id: 'b.custom.message',\n    def"
  },
  {
    "path": "src/test/fixtures/custom/i18n.js",
    "chars": 44,
    "preview": "export { defineMessages } from 'react-intl'\n"
  },
  {
    "path": "src/test/fixtures/default/a/App.js",
    "chars": 278,
    "preview": "import React from 'react'\nimport { useIntl } from 'react-intl'\n\nexport const SubmitButton = () => {\n  const intl = useIn"
  },
  {
    "path": "src/test/fixtures/default/a/messages.js",
    "chars": 205,
    "preview": "import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  hello: {\n    id: 'a.hello',\n    defaultMe"
  },
  {
    "path": "src/test/fixtures/default/b/messages.js",
    "chars": 205,
    "preview": "import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  hello: {\n    id: 'b.hello',\n    defaultMe"
  },
  {
    "path": "src/test/fixtures/removed/a/messages.js",
    "chars": 142,
    "preview": "import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  hello: {\n    id: 'a.hello',\n    defaultMe"
  },
  {
    "path": "src/test/fixtures/removed/b/messages.js",
    "chars": 205,
    "preview": "import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  hello: {\n    id: 'b.hello',\n    defaultMe"
  },
  {
    "path": "src/test/fixtures/unsorted/a/messages.js",
    "chars": 333,
    "preview": "import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  hello: {\n    id: 'a.hello',\n    defaultMe"
  },
  {
    "path": "src/test/fixtures/unsorted/b/messages.js",
    "chars": 269,
    "preview": "import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n  hello: {\n    id: 'b.hello',\n    defaultMe"
  },
  {
    "path": "src/test/json/__snapshots__/test.ts.snap",
    "chars": 2035,
    "preview": "// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing\n\nexports[`export json - nest 1`] = `\n{\n  \"a\": {\n    \"hello\""
  },
  {
    "path": "src/test/json/test.ts",
    "chars": 3135,
    "preview": "import fs from 'fs'\nimport path from 'path'\nimport tempy from 'tempy'\nimport m from '../..'\n\ntest('export json', async ("
  },
  {
    "path": "src/test/test.ts",
    "chars": 423,
    "preview": "import m from '..'\n\ntest('errors', async () => {\n  // @ts-expect-error testing invalid argument type\n  await expect(m('h"
  },
  {
    "path": "src/test/yaml/__snapshots__/test.ts.snap",
    "chars": 1028,
    "preview": "// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing\n\nexports[`export yaml - flat 1`] = `\n{\n  \"a.hello\": \"hello\""
  },
  {
    "path": "src/test/yaml/test.ts",
    "chars": 1412,
    "preview": "import fs from 'fs'\nimport path from 'path'\nimport tempy from 'tempy'\nimport tempWrite from 'temp-write'\nimport yaml fro"
  },
  {
    "path": "tsconfig.json",
    "chars": 1474,
    "preview": "{\n  \"compilerOptions\": {\n    /* Language and Environment */\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\"],\n    \"m"
  }
]

About this extraction

This page contains the full source code of the akameco/extract-react-intl-messages GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 66 files (61.9 KB), approximately 19.8k tokens, and a symbol index with 12 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!