├── .all-contributorsrc
├── .babelrc
├── .coveralls.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── ci.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── CODE_OF_CONDUCT.md
├── examples
└── with-typescript
│ ├── app
│ ├── components
│ │ ├── App
│ │ │ ├── index.tsx
│ │ │ └── messages.ts
│ │ ├── Greeting
│ │ │ ├── index.tsx
│ │ │ └── messages.ts
│ │ └── LanguageProvider
│ │ │ └── index.tsx
│ ├── index.d.ts
│ ├── index.html
│ ├── index.tsx
│ └── translations
│ │ ├── en.json
│ │ └── ja.json
│ ├── babel.config.js
│ ├── package.json
│ ├── readme.md
│ ├── tsconfig.json
│ ├── webpack.config.js
│ └── yarn.lock
├── jest.config.js
├── license
├── package.json
├── readme.md
├── renovate.json
├── src
├── __tests__
│ ├── __snapshots__
│ │ ├── components.test.ts.snap
│ │ ├── hook.test.ts.snap
│ │ ├── index.test.ts.snap
│ │ └── injection.test.ts.snap
│ ├── components.test.ts
│ ├── hook.test.ts
│ ├── index.test.ts
│ └── injection.test.ts
├── babel-plugin-tester.d.ts
├── index.ts
├── types.ts
├── utils
│ ├── getPrefix.ts
│ ├── index.ts
│ ├── isImportLocalName.ts
│ └── testUtils.ts
└── visitors
│ ├── addIdToDefineMessage.ts
│ ├── addIdToFormatMessage.ts
│ └── jsx.ts
├── tsconfig.json
├── types.d.ts
└── yarn.lock
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "babel-plugin-react-intl-auto",
3 | "projectOwner": "akameco",
4 | "files": [
5 | "readme.md"
6 | ],
7 | "imageSize": 100,
8 | "commit": true,
9 | "contributors": [
10 | {
11 | "login": "akameco",
12 | "name": "akameco",
13 | "avatar_url": "https://avatars2.githubusercontent.com/u/4002137?v=4",
14 | "profile": "http://akameco.github.io",
15 | "contributions": [
16 | "code",
17 | "test",
18 | "review",
19 | "doc"
20 | ]
21 | },
22 | {
23 | "login": "Alxandr",
24 | "name": "Aleksander Heintz",
25 | "avatar_url": "https://avatars0.githubusercontent.com/u/112334?v=4",
26 | "profile": "http://alxandr.me",
27 | "contributions": [
28 | "code",
29 | "doc"
30 | ]
31 | },
32 | {
33 | "login": "mehcode",
34 | "name": "Ryan Leckey",
35 | "avatar_url": "https://avatars1.githubusercontent.com/u/753919?v=4",
36 | "profile": "https://github.com/mehcode",
37 | "contributions": [
38 | "code"
39 | ]
40 | },
41 | {
42 | "login": "adam-26",
43 | "name": "Adam",
44 | "avatar_url": "https://avatars1.githubusercontent.com/u/2652619?v=4",
45 | "profile": "https://github.com/adam-26",
46 | "contributions": [
47 | "code",
48 | "doc"
49 | ]
50 | },
51 | {
52 | "login": "Ephys",
53 | "name": "Guylian Cox",
54 | "avatar_url": "https://avatars0.githubusercontent.com/u/1280915?v=4",
55 | "profile": "https://ephys.github.io",
56 | "contributions": [
57 | "code",
58 | "doc",
59 | "test"
60 | ]
61 | },
62 | {
63 | "login": "carlgrundberg",
64 | "name": "Carl Grundberg",
65 | "avatar_url": "https://avatars1.githubusercontent.com/u/928407?v=4",
66 | "profile": "http://carlgrundberg.github.io/",
67 | "contributions": [
68 | "example",
69 | "doc"
70 | ]
71 | },
72 | {
73 | "login": "bradbarrow",
74 | "name": "bradbarrow",
75 | "avatar_url": "https://avatars3.githubusercontent.com/u/1264276?v=4",
76 | "profile": "http://bradbarrow.com",
77 | "contributions": [
78 | "code",
79 | "doc",
80 | "test"
81 | ]
82 | },
83 | {
84 | "login": "mgtitimoli",
85 | "name": "Mauro Gabriel Titimoli",
86 | "avatar_url": "https://avatars2.githubusercontent.com/u/4404683?v=4",
87 | "profile": "https://github.com/mgtitimoli",
88 | "contributions": [
89 | "code",
90 | "test"
91 | ]
92 | },
93 | {
94 | "login": "stanislav-ermakov",
95 | "name": "Stanislav Ermakov",
96 | "avatar_url": "https://avatars2.githubusercontent.com/u/15980086?v=4",
97 | "profile": "https://github.com/stanislav-ermakov",
98 | "contributions": [
99 | "code"
100 | ]
101 | },
102 | {
103 | "login": "chitoku-k",
104 | "name": "Chitoku",
105 | "avatar_url": "https://avatars1.githubusercontent.com/u/6535425?v=4",
106 | "profile": "https://chitoku.jp/",
107 | "contributions": [
108 | "code"
109 | ]
110 | },
111 | {
112 | "login": "kuma-kuma",
113 | "name": "Kouta Kumagai",
114 | "avatar_url": "https://avatars0.githubusercontent.com/u/12218082?v=4",
115 | "profile": "https://github.com/kuma-kuma",
116 | "contributions": [
117 | "doc",
118 | "code",
119 | "test"
120 | ]
121 | },
122 | {
123 | "login": "shahyar",
124 | "name": "Shahyar G",
125 | "avatar_url": "https://avatars0.githubusercontent.com/u/255846?v=4",
126 | "profile": "http://shah.yar.gs",
127 | "contributions": [
128 | "code"
129 | ]
130 | },
131 | {
132 | "login": "remcohaszing",
133 | "name": "Remco Haszing",
134 | "avatar_url": "https://avatars2.githubusercontent.com/u/779047?v=4",
135 | "profile": "https://gitlab.com/remcohaszing",
136 | "contributions": [
137 | "code"
138 | ]
139 | },
140 | {
141 | "login": "jmarceli",
142 | "name": "jmarceli",
143 | "avatar_url": "https://avatars1.githubusercontent.com/u/4281333?v=4",
144 | "profile": "https://github.com/jmarceli",
145 | "contributions": [
146 | "code",
147 | "test"
148 | ]
149 | },
150 | {
151 | "login": "dominik-zeglen",
152 | "name": "Dominik Żegleń",
153 | "avatar_url": "https://avatars3.githubusercontent.com/u/6833443?v=4",
154 | "profile": "https://github.com/dominik-zeglen",
155 | "contributions": [
156 | "code",
157 | "test"
158 | ]
159 | },
160 | {
161 | "login": "Filson14",
162 | "name": "Filip \"Filson\" Pasternak",
163 | "avatar_url": "https://avatars1.githubusercontent.com/u/4540538?v=4",
164 | "profile": "https://github.com/Filson14",
165 | "contributions": [
166 | "code"
167 | ]
168 | },
169 | {
170 | "login": "ericmasiello",
171 | "name": "Eric Masiello",
172 | "avatar_url": "https://avatars3.githubusercontent.com/u/3525886?v=4",
173 | "profile": "https://github.com/ericmasiello",
174 | "contributions": [
175 | "code",
176 | "test"
177 | ]
178 | }
179 | ],
180 | "repoType": "github",
181 | "commitConvention": "none"
182 | }
183 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env", { "targets": { "node": "8" } }],
4 | "@babel/preset-typescript"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | repo_token: xgkdsD7kShO8c0G1f51R77L0oshots57p
2 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | lib
2 | examples
3 | jest.config.js
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "precure/auto",
3 | "rules": {
4 | "unicorn/prevent-abbreviations": "off",
5 | "@typescript-eslint/explicit-function-return-type": "off",
6 | "@typescript-eslint/no-explicit-any": "off",
7 | "jest/require-tothrow-message": "off",
8 | "jest/no-empty-title": "off"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.js text eol=lf
3 | flowtyped/npm binary
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
8 |
9 | * version:
10 | * `node` version:
11 | * `npm` (or `yarn`) version:
12 |
13 | **Do you want to request a _feature_ or report a _bug_?:**
14 |
15 | **What is the current behavior?:**
16 |
17 | **What is the expected behavior?:**
18 |
19 | **Suggested solution:**
20 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | **What**:
8 |
9 |
10 |
11 | **Why**:
12 |
13 |
14 |
15 | **How**:
16 |
17 |
18 | **Checklist**:
19 |
20 | * [ ] Documentation
21 | * [ ] Tests
22 | * [ ] Ready to be merged
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on: [push]
4 |
5 | jobs:
6 | test:
7 | strategy:
8 | matrix:
9 | platform: [ubuntu-latest, windows-latest]
10 | node-version: [12.x, 13.x]
11 | runs-on: ${{ matrix.platform }}
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: actions/setup-node@v1
15 | with:
16 | node-version: ${{ matrix.node }}
17 | - run: yarn install
18 | - run: yarn run fmt --check
19 | - run: yarn run tsc --noEmit
20 | - run: yarn run test:ci
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | dist
4 | coverage
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/flow-typed/**
2 | coverage
3 | lib
4 | dist
5 | package.json
6 | .github
7 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "trailingComma": "es5"
5 | }
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | 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.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | - Using welcoming and inclusive language
12 | - Being respectful of differing viewpoints and experiences
13 | - Gracefully accepting constructive criticism
14 | - Focusing on what is best for the community
15 | - Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | - Trolling, insulting/derogatory comments, and personal or political attacks
21 | - Public or private harassment
22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | - Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | 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.
28 |
29 | 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.
30 |
31 | ## Scope
32 |
33 | 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.
34 |
35 | ## Enforcement
36 |
37 | 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.
38 |
39 | 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.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/components/App/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { FormattedMessage, useIntl } from 'react-intl'
3 | import Greeting from '../Greeting'
4 | import messages from './messages'
5 |
6 | export default function App() {
7 | const intl = useIntl()
8 | const user = {
9 | name: 'Eric',
10 | unreadCount: 4,
11 | lastLoginTime: Date.now() - 1000 * 60 * 60 * 24,
12 | }
13 |
14 | return (
15 |
16 |
17 | {intl.formatMessage({ defaultMessage: 'world' })}
18 |
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/components/App/messages.ts:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | hello: 'hello',
5 | })
6 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/components/Greeting/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import {
3 | FormattedMessage,
4 | FormattedNumber,
5 | FormattedRelativeTime,
6 | } from 'react-intl'
7 | import messages from './messages'
8 |
9 | interface User {
10 | name: string
11 | unreadCount: number
12 | lastLoginTime: number
13 | }
14 |
15 | interface UserProps {
16 | user: User
17 | }
18 |
19 | export default function Greeting({ user }: UserProps) {
20 | return (
21 |
22 | {user.name},
26 | unreadCount: user.unreadCount,
27 | formattedUnreadCount: (
28 |
29 |
30 |
31 | ),
32 | formattedLastLoginTime: (
33 |
34 | ),
35 | }}
36 | />
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/components/Greeting/messages.ts:
--------------------------------------------------------------------------------
1 | import { defineMessages } from 'react-intl'
2 |
3 | export default defineMessages({
4 | welcome: `Welcome {name}, you have received {unreadCount, plural, =0 {no new messages} one {{formattedUnreadCount} new message} other {{formattedUnreadCount} new messages}} since {formattedLastLoginTime}.`,
5 | })
6 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/components/LanguageProvider/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { IntlProvider } from 'react-intl'
3 |
4 | import enMessages from '../../translations/en.json'
5 | import jaMessages from '../../translations/ja.json'
6 |
7 | const messages: {
8 | [key: string]: {}
9 | } = {
10 | en: enMessages,
11 | ja: jaMessages,
12 | }
13 |
14 | export default function LanguageProvider({
15 | children,
16 | }: {
17 | children: React.ReactNode
18 | }) {
19 | const [locale, setLocale] = React.useState('en')
20 |
21 | return (
22 |
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.json' {
2 | interface Translation {
3 | [key: string]: {}
4 | }
5 |
6 | const Translation: Translation
7 | export default Translation
8 | }
9 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | react-intl-auto example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import * as ReactDOM from 'react-dom'
3 | import App from './components/App'
4 | import LanguageProvider from './components/LanguageProvider'
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/translations/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "components.App.4220927227": "world",
3 | "components.App.hello": "hello",
4 | "components.Greeting.welcome": "Welcome {name}, you have received {unreadCount, plural, =0 {no new messages} one {{formattedUnreadCount} new message} other {{formattedUnreadCount} new messages}} since {formattedLastLoginTime}."
5 | }
6 |
--------------------------------------------------------------------------------
/examples/with-typescript/app/translations/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "components.App.4220927227": "世界",
3 | "components.App.hello": "こんにちは",
4 | "components.Greeting.welcome": "ようこそ {name}, {formattedLastLoginTime}から {unreadCount, plural, =0 {メッセージはありません} other {{formattedUnreadCount} 件のメッセージがあります}}"
5 | }
6 |
--------------------------------------------------------------------------------
/examples/with-typescript/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true)
3 |
4 | return {
5 | presets: ['@babel/preset-react', '@babel/preset-typescript'],
6 | plugins: [
7 | [
8 | 'react-intl-auto',
9 | {
10 | removePrefix: 'app/',
11 | },
12 | ],
13 | ],
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/with-typescript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tiny-react-app-example-typescript",
3 | "version": "0.0.1",
4 | "scripts": {
5 | "start": "webpack-dev-server --hot",
6 | "build": "webpack",
7 | "i18n": "extract-react-intl-messages -l=en,ja -o app/translations -d en --flat 'app/**/!(*.test).{ts,tsx}'"
8 | },
9 | "dependencies": {
10 | "@babel/preset-typescript": "^7.9.0",
11 | "react": "^16.13.1",
12 | "react-dom": "^16.13.1",
13 | "react-intl": "^4.5.0"
14 | },
15 | "devDependencies": {
16 | "@babel/cli": "^7.8.4",
17 | "@babel/core": "^7.9.0",
18 | "@babel/plugin-transform-typescript": "^7.9.4",
19 | "@babel/preset-env": "^7.9.5",
20 | "@babel/preset-react": "^7.9.4",
21 | "@types/react": "^16.9.34",
22 | "@types/react-dom": "^16.9.6",
23 | "@types/react-intl": "^3.0.0",
24 | "babel-loader": "~8.1.0",
25 | "babel-plugin-react-intl": "^7.5.2",
26 | "babel-plugin-react-intl-auto": "3.3.0",
27 | "extract-react-intl": "^0.8.1",
28 | "extract-react-intl-messages": "^4.1.1",
29 | "ts-loader": "^7.0.1",
30 | "typescript": "^3.8.3",
31 | "webpack": "^4.43.0",
32 | "webpack-cli": "^3.3.11",
33 | "webpack-dev-server": "^3.10.3"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/with-typescript/readme.md:
--------------------------------------------------------------------------------
1 | # Example App
2 |
3 | ## Install
4 |
5 | ```
6 | $ yarn
7 | $ npm link babel-plugin-react-intl-auto
8 | ```
9 |
10 | ## Run
11 |
12 | ```
13 | $ yarn start
14 | ```
15 |
--------------------------------------------------------------------------------
/examples/with-typescript/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2015",
4 | "module": "esnext",
5 | "lib": ["dom", "esnext"],
6 | "moduleResolution": "node",
7 | "jsx": "react",
8 | "sourceMap": true,
9 | "strict": true
10 | },
11 | "include": [
12 | "app/**/*.ts",
13 | "node_modules/babel-plugin-react-intl-auto/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/examples/with-typescript/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 |
4 | module.exports = {
5 | mode: 'development',
6 | devtool: 'inline-source-map',
7 | context: path.resolve(__dirname, './app'),
8 | entry: './index.tsx',
9 | output: {
10 | filename: 'bundle.js',
11 | path: path.resolve(__dirname, './dist'),
12 | publicPath: '/assets',
13 | },
14 | devServer: {
15 | contentBase: path.resolve(__dirname, './app'),
16 | },
17 | resolve: {
18 | extensions: ['.ts', '.tsx', '.js'],
19 | },
20 | module: {
21 | rules: [
22 | {
23 | test: /\.js$/,
24 | exclude: [/node_modules/],
25 | use: [
26 | {
27 | loader: 'babel-loader',
28 | },
29 | ],
30 | },
31 | {
32 | test: /\.tsx?$/,
33 | exclude: [/node_modules/],
34 | use: [
35 | {
36 | loader: 'babel-loader',
37 | },
38 | {
39 | loader: 'ts-loader',
40 | },
41 | ],
42 | },
43 | ],
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'node',
3 | snapshotSerializers: [
4 | require.resolve('string-snapshot-serializer/serializer'),
5 | ],
6 | modulePathIgnorePatterns: ['/lib'],
7 | }
8 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) akameco (akameco.github.io)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "babel-plugin-react-intl-auto",
3 | "version": "3.3.0",
4 | "main": "lib/index.js",
5 | "types": "types.d.ts",
6 | "description": "i18n for the component age. Auto management react-intl ID",
7 | "license": "MIT",
8 | "repository": "akameco/babel-plugin-react-intl-auto",
9 | "author": {
10 | "name": "akameco",
11 | "email": "akameco.t@gmail.com",
12 | "url": "https://akameco.github.io"
13 | },
14 | "engines": {
15 | "node": ">=10"
16 | },
17 | "scripts": {
18 | "build": "babel src -d lib --ignore __tests__,__fixtures__ --extensions .ts",
19 | "prepack": "yarn build",
20 | "fmt": "prettier --write .",
21 | "lint": "eslint src --ext ts",
22 | "add:coveralls": "cat ./coverage/lcov.info | coveralls",
23 | "test": "jest",
24 | "test:watch": "jest --watch",
25 | "test:coverage": "jest --coverage --ci --runInBand",
26 | "test:ci": "yarn lint && yarn test:coverage"
27 | },
28 | "lint-staged": {
29 | "*.{ts}": [
30 | "prettier --write",
31 | "eslint"
32 | ],
33 | "*.{js,json,md}": [
34 | "prettier --write"
35 | ]
36 | },
37 | "keywords": [
38 | "react",
39 | "react-components",
40 | "react-intl",
41 | "i18n",
42 | "react-intl-auto",
43 | "babel-plugin",
44 | "auto",
45 | "babel",
46 | "plugin",
47 | "generate",
48 | "defineMessages"
49 | ],
50 | "files": [
51 | "lib",
52 | "types.d.ts"
53 | ],
54 | "dependencies": {
55 | "@babel/core": "^7.9.0",
56 | "@babel/traverse": "^7.9.0",
57 | "@babel/types": "^7.9.0",
58 | "murmurhash3js": "^3.0.1"
59 | },
60 | "devDependencies": {
61 | "@akameco/tsconfig": "0.4.0",
62 | "@babel/cli": "7.14.8",
63 | "@babel/preset-env": "7.14.8",
64 | "@babel/preset-typescript": "7.14.5",
65 | "@babel/register": "7.14.5",
66 | "@types/babel__core": "7.1.15",
67 | "@types/babel__traverse": "7.14.2",
68 | "@types/jest": "26.0.24",
69 | "@types/murmurhash3js": "3.0.2",
70 | "@types/node": "14.17.5",
71 | "babel-core": "7.0.0-bridge.0",
72 | "babel-eslint": "10.1.0",
73 | "babel-jest": "26.6.3",
74 | "babel-log": "2.0.0",
75 | "babel-plugin-tester": "9.2.0",
76 | "coveralls": "3.1.1",
77 | "eslint": "7.31.0",
78 | "eslint-config-precure": "5.4.0",
79 | "husky": "4.3.8",
80 | "jest": "26.6.3",
81 | "lint-staged": "10.5.4",
82 | "prettier": "2.3.2",
83 | "react-intl": "4.7.6",
84 | "string-snapshot-serializer": "1.0.1",
85 | "typescript": "3.9.10"
86 | },
87 | "husky": {
88 | "hooks": {
89 | "pre-commit": "lint-staged"
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # babel-plugin-react-intl-auto
2 |
3 | [](https://github.com/akameco/babel-plugin-react-intl-auto/actions?query=workflow%3Atest)
4 | [](https://coveralls.io/github/akameco/babel-plugin-react-intl-auto?branch=master)
5 | [](https://github.com/prettier/prettier)
6 | [](https://github.com/facebook/jest)
7 | [](#contributors-)
8 | [](https://devtoken.rocks/package/babel-plugin-react-intl-auto)
9 |
10 | > i18n for the component age. Auto management react-intl ID.
11 |
12 | [React Intl](https://github.com/formatjs/react-intl) is awesome. But, Global ID management is difficult and confusing.
13 |
14 | Many projects, like [react-boilerplate](https://github.com/react-boilerplate/react-boilerplate), give the ID to the name of the component as a prefix.
15 | But it is redundant and troublesome.
16 |
17 | This babel-plugin releases you from cumbersome ID management.
18 | Based on the file path, this automatically generates a prefixed id.
19 |
20 | Also, we strongly encourage you to use [extract-react-intl-messages](https://github.com/akameco/extract-react-intl-messages).
21 | You can generate json automatically.
22 |
23 | Goodbye, global ID!!
24 |
25 | #### Before
26 |
27 | ```js
28 | import { defineMessages, FormattedMessage } from 'react-intl'
29 |
30 | export default defineMessages({
31 | hello: {
32 | id: 'App.Components.Greeting.hello',
33 | defaultMessage: 'hello {name}',
34 | },
35 | welcome: {
36 | id: 'App.Components.Greeting.welcome',
37 | defaultMessage: 'Welcome!',
38 | },
39 | })
40 |
41 | const MyComponent = () => (
42 |
46 | )
47 | ```
48 |
49 | #### After
50 |
51 | With babel-plugin-react-intl-auto.
52 |
53 | ```js
54 | import { defineMessages, FormattedMessage } from 'react-intl'
55 |
56 | export default defineMessages({
57 | hello: 'hello {name}',
58 | welcome: 'Welcome!',
59 | })
60 |
61 | const MyComponent = () =>
62 | ```
63 |
64 | See [examples](https://github.com/akameco/babel-plugin-react-intl-auto/tree/master/examples).
65 |
66 | ### With `extract-react-intl-messages`
67 |
68 | Example usage with [extract-react-intl-messages](https://github.com/akameco/extract-react-intl-messages).
69 |
70 | ```
71 | $ extract-messages -l=en -o translations 'src/**/*.js'
72 | ```
73 |
74 | en.json
75 |
76 | ```json
77 | {
78 | "components.App.hello": "hello {name}",
79 | "components.App.welcome": "Welcome",
80 | "components.App.189751785": "goodbye {name}" // unique hash of defaultMessage
81 | }
82 | ```
83 |
84 | ## Install
85 |
86 | npm
87 |
88 | ```shell
89 | $ npm install --save-dev babel-plugin-react-intl-auto
90 |
91 | # Optional: TypeScript support
92 | $ npm install --save-dev @babel/plugin-transform-typescript
93 | ```
94 |
95 | yarn
96 |
97 | ```shell
98 | $ yarn add --dev babel-plugin-react-intl-auto
99 |
100 | # Optional: TypeScript support
101 | $ yarn add --dev @babel/plugin-transform-typescript
102 | ```
103 |
104 | ## Usage
105 |
106 | .babelrc
107 |
108 | ```json
109 | {
110 | "plugins": [
111 | [
112 | "react-intl-auto",
113 | {
114 | "removePrefix": "app/",
115 | "filebase": false
116 | }
117 | ]
118 | ]
119 | }
120 | ```
121 |
122 | ### with injectIntl
123 |
124 | Input:
125 |
126 | ```js
127 | import { injectIntl } from 'react-intl'
128 |
129 | const MyComponent = ({ intl }) => {
130 | const label = intl.formatMessage({ defaultMessage: 'Submit button' })
131 | return
132 | }
133 |
134 | injectIntl(MyComponent)
135 | ```
136 |
137 | ↓ ↓ ↓
138 |
139 | Output:
140 |
141 | ```js
142 | import { injectIntl } from 'react-intl'
143 |
144 | const MyComponent = ({ intl }) => {
145 | const label = intl.formatMessage({
146 | id: 'App.Components.Button.label',
147 | defaultMessage: 'Submit button',
148 | })
149 | return
150 | }
151 |
152 | injectIntl(MyComponent)
153 | ```
154 |
155 | ### with useIntl
156 |
157 | Input:
158 |
159 | ```js
160 | import { useIntl } from 'react-intl'
161 |
162 | const MyComponent = () => {
163 | const intl = useIntl()
164 | const label = intl.formatMessage({ defaultMessage: 'Submit button' })
165 | return
166 | }
167 | ```
168 |
169 | ↓ ↓ ↓
170 |
171 | Output:
172 |
173 | ```js
174 | import { useIntl } from 'react-intl'
175 |
176 | const MyComponent = () => {
177 | const intl = useIntl()
178 | const label = intl.formatMessage({
179 | id: 'App.Components.Button.label',
180 | defaultMessage: 'Submit button',
181 | })
182 | return
183 | }
184 | ```
185 |
186 | ### Options
187 |
188 | #### removePrefix
189 |
190 | remove prefix.
191 |
192 | Type: `string | boolean` | `regexp`
193 | Default: `''`
194 |
195 | if `removePrefix` is `true`, no file path prefix is included in the id.
196 |
197 | ##### Example (src/components/App/messages.js)
198 |
199 | when `removePrefix` is `"src"`
200 |
201 | ```js
202 | import { defineMessages } from 'react-intl';
203 |
204 | export default defineMessages({
205 | hello: 'hello world'
206 | });
207 |
208 | ↓ ↓ ↓ ↓ ↓ ↓
209 |
210 | import { defineMessages } from 'react-intl';
211 |
212 | export default defineMessages({
213 | hello: {
214 | id: 'components.App.hello',
215 | defaultMessage: 'hello world'
216 | }
217 | });
218 | ```
219 |
220 | when `removePrefix` is `"src.components"`
221 |
222 | ```js
223 | import { defineMessages } from 'react-intl';
224 |
225 | export default defineMessages({
226 | hello: 'hello world'
227 | });
228 |
229 | ↓ ↓ ↓ ↓ ↓ ↓
230 |
231 | import { defineMessages } from 'react-intl';
232 |
233 | export default defineMessages({
234 | hello: {
235 | id: 'App.hello',
236 | defaultMessage: 'hello world'
237 | }
238 | });
239 | ```
240 |
241 | when `removePrefix` is `true`
242 |
243 | ```js
244 | import { defineMessages } from 'react-intl';
245 |
246 | export default defineMessages({
247 | hello: 'hello world'
248 | });
249 |
250 | ↓ ↓ ↓ ↓ ↓ ↓
251 |
252 | import { defineMessages } from 'react-intl';
253 |
254 | export default defineMessages({
255 | hello: {
256 | id: 'hello',
257 | defaultMessage: 'hello world'
258 | }
259 | });
260 | ```
261 |
262 | #### filebase
263 |
264 | Type: `boolean`
265 | Default: `false`
266 |
267 | if `filebase` is `true`, generate id with filename.
268 |
269 | #### moduleSourceName
270 |
271 | Type: `string`
272 | Default: `react-intl`
273 |
274 | if set, enables to use custom module as a source for _defineMessages_ etc.
275 |
276 | https://github.com/akameco/babel-plugin-react-intl-auto/issues/74#issuecomment-528562743
277 |
278 | #### includeExportName
279 |
280 | Type: `boolean | 'all'`
281 | Default: `false`
282 |
283 | if `includeExportName` is `true`, adds named exports as part of the id.
284 |
285 | Only works with `defineMessages`.
286 |
287 | ##### Example
288 |
289 | ```js
290 | export const test = defineMessages({
291 | hello: 'hello {name}',
292 | })
293 |
294 | ↓ ↓ ↓ ↓ ↓ ↓
295 |
296 | export const test = defineMessages({
297 | hello: {
298 | id: 'path.to.file.test.hello',
299 | defaultMessage: 'hello {name}',
300 | },
301 | })
302 | ```
303 |
304 | If includeExportName is `'all'`, it will also add `default` to the id on default
305 | exports.
306 |
307 | #### extractComments
308 |
309 | Use leading comments as the message description.
310 |
311 | Only works with `defineMessages`
312 |
313 | Type: `boolean`
314 | Default: `true`
315 |
316 | ##### Example
317 |
318 | ```js
319 | export const test = defineMessages({
320 | // Message used to greet the user
321 | hello: 'hello {name}',
322 | })
323 |
324 | ↓ ↓ ↓ ↓ ↓ ↓
325 |
326 | export const test = defineMessages({
327 | hello: {
328 | id: 'path.to.file.test.hello',
329 | defaultMessage: 'hello {name}',
330 | description: 'Message used to greet the user',
331 | },
332 | })
333 | ```
334 |
335 | #### useKey
336 |
337 | Only works with `intl.formatMessage`, `FormattedMessage` and `FormattedHTMLMessage`. Instead of
338 | generating an ID by hashing `defaultMessage`, it will use the `key` property if
339 | it exists.
340 |
341 | Type: `boolean`
342 | Default: `false`
343 |
344 | ##### Example
345 |
346 | ```js
347 | intl.formatMessage({
348 | key: 'foobar',
349 | defaultMessage: 'hello'
350 | });
351 |
352 | ↓ ↓ ↓ ↓ ↓ ↓
353 |
354 | intl.formatMessage({
355 | key: 'foobar',
356 | defaultMessage: 'hello',
357 | "id": "path.to.file.foobar"
358 | });
359 | ```
360 |
361 | ```js
362 |
363 |
364 | ↓ ↓ ↓ ↓ ↓ ↓
365 |
366 |
367 | ```
368 |
369 | #### separator
370 |
371 | Allows you to specify a custom separator
372 |
373 | Type: `string`
374 | Default: `.`
375 |
376 | ##### Example
377 |
378 | when `separator` is `"_"`
379 |
380 | ```js
381 | export const test = defineMessages({
382 | hello: 'hello {name}',
383 | })
384 |
385 | ↓ ↓ ↓ ↓ ↓ ↓
386 |
387 | export const test = defineMessages({
388 | hello: {
389 | id: 'path_to_file_test_hello',
390 | defaultMessage: 'hello {name}',
391 | },
392 | })
393 | ```
394 |
395 | #### relativeTo
396 |
397 | Allows you to specify the directory that is used when determining a file's prefix.
398 |
399 | This option is useful for monorepo setups.
400 |
401 | Type: `string`
402 | Default: `process.cwd()`
403 |
404 | ##### Example
405 |
406 | Folder structure with two sibling packages. `packageB` contains babel config and depends on `packageA`.
407 |
408 | ```bash
409 | |- packageA
410 | | |
411 | | -- componentA
412 | |
413 | |- packageB
414 | | |
415 | | -- componentB
416 | | |
417 | | -- .babelrc
418 | ```
419 |
420 | Set `relativeTo` to parent directory in `packageB` babel config
421 |
422 | ```js
423 | {
424 | "plugins": [
425 | [
426 | "react-intl-auto",
427 | {
428 | "relativeTo": "..",
429 | // ...
430 | },
431 | ],
432 | ]
433 | }
434 | ```
435 |
436 | Run babel in packageB
437 |
438 | ```bash
439 | cd packageB && babel
440 | ```
441 |
442 | Messages in `componentA` are prefixed relative to the project root
443 |
444 | ```js
445 | export const test = defineMessages({
446 | hello: 'hello {name}',
447 | })
448 |
449 | ↓ ↓ ↓ ↓ ↓ ↓
450 |
451 | export const test = defineMessages({
452 | hello: {
453 | id: 'packageA.componentA.hello',
454 | defaultMessage: 'hello {name}',
455 | },
456 | })
457 | ```
458 |
459 | ### Support variable
460 |
461 | ##### Example
462 |
463 | ```js
464 | const messages = { hello: 'hello world' }
465 |
466 | export default defineMessages(messages)
467 |
468 | ↓ ↓ ↓ ↓ ↓ ↓
469 |
470 | const messages = {
471 | hello: {
472 | id: 'path.to.file.hello',
473 | defaultMessage: 'hello wolrd'
474 | }
475 | };
476 |
477 | export default defineMessages(messages);
478 | ```
479 |
480 | ## TypeScript
481 |
482 | TypeScript support is bundled with this package. Be sure to include our type
483 | definition and run `@babel/plugin-transform-typescript` beforehand. This way,
484 | you can also be empowered by [extract-react-intl-messages](https://github.com/akameco/extract-react-intl-messages).
485 |
486 | ### tsconfig.json
487 |
488 | ```json
489 | {
490 | "compilerOptions": {
491 | // ...
492 | "jsx": "preserve"
493 | // ...
494 | },
495 | "include": ["node_modules/babel-plugin-react-intl-auto/**/*.d.ts"]
496 | }
497 | ```
498 |
499 | ### .babelrc
500 |
501 | ```json
502 | {
503 | "plugins": [["@babel/plugin-transform-typescript"], ["react-intl-auto"]]
504 | }
505 | ```
506 |
507 | ### webpack.config.js
508 |
509 | Use `babel-loader` along with `ts-loader` when using webpack as well.
510 |
511 | ```js
512 | module.exports = {
513 | module: {
514 | rules: [
515 | {
516 | test: /\.tsx?$/,
517 | exclude: [/node_modules/],
518 | use: [
519 | {
520 | loader: 'babel-loader',
521 | },
522 | {
523 | loader: 'ts-loader',
524 | },
525 | ],
526 | },
527 | ],
528 | },
529 | }
530 | ```
531 |
532 | ## Related
533 |
534 | ### [babel-plugin-react-intl-id-hash](https://github.com/adam-26/babel-plugin-react-intl-id-hash)
535 |
536 | If you want short consistent hash values for the ID, you can use [react-intl-id-hash](https://github.com/adam-26/babel-plugin-react-intl-id-hash) in addition to this plugin to help reduce your applications bundle size.
537 |
538 | ### [extract-react-intl-messages](https://github.com/akameco/extract-react-intl-messages)
539 |
540 | Extract react-intl messages.
541 |
542 | ## Contributors
543 |
544 | Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
545 |
546 |
547 |
548 |
549 |
575 |
576 |
577 |
578 |
579 |
580 |
581 | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
582 |
583 | ## License
584 |
585 | MIT © [akameco](http://akameco.github.io)
586 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@akameco"]
3 | }
4 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/components.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`default default: default 1`] = `
4 |
5 | import { FormattedMessage } from 'react-intl';
6 |
7 | ;
8 |
9 | ↓ ↓ ↓ ↓ ↓ ↓
10 |
11 | import { FormattedMessage } from 'react-intl';
12 | ;
13 |
14 | `;
15 |
16 | exports[`default import all supported components: import all supported components 1`] = `
17 |
18 | import { FormattedHTMLMessage, FormattedMessage } from 'react-intl';
19 |
20 | ;
21 | ;
22 |
23 | ↓ ↓ ↓ ↓ ↓ ↓
24 |
25 | import { FormattedHTMLMessage, FormattedMessage } from 'react-intl';
26 | ;
27 | ;
28 |
29 | `;
30 |
31 | exports[`default multiple uses: multiple uses 1`] = `
32 |
33 | import { FormattedMessage } from 'react-intl';
34 |
35 | ;
36 | ;
37 |
38 | ↓ ↓ ↓ ↓ ↓ ↓
39 |
40 | import { FormattedMessage } from 'react-intl';
41 | ;
42 | ;
43 |
44 | `;
45 |
46 | exports[`default using key: using key 1`] = `
47 |
48 | import { FormattedMessage } from 'react-intl';
49 |
50 | ;
51 |
52 | ↓ ↓ ↓ ↓ ↓ ↓
53 |
54 | import { FormattedMessage } from 'react-intl';
55 | ;
56 |
57 | `;
58 |
59 | exports[`default with FormattedMessage imported as something else: with FormattedMessage imported as something else 1`] = `
60 |
61 | import { FormattedMessage as T } from 'react-intl';
62 |
63 | ;
64 |
65 | ↓ ↓ ↓ ↓ ↓ ↓
66 |
67 | import { FormattedMessage as T } from 'react-intl';
68 | ;
69 |
70 | `;
71 |
72 | exports[`default with FormattedMessage nested in other JSX: with FormattedMessage nested in other JSX 1`] = `
73 |
74 | import { FormattedMessage } from 'react-intl';
75 |
76 |
77 |
78 |
79 |
80 | ↓ ↓ ↓ ↓ ↓ ↓
81 |
82 | import { FormattedMessage } from 'react-intl';
83 |
84 |
85 |
;
86 |
87 | `;
88 |
89 | exports[`default with a value interpolated in the message: with a value interpolated in the message 1`] = `
90 |
91 | import { FormattedMessage } from 'react-intl';
92 |
93 | ;
94 |
95 | ↓ ↓ ↓ ↓ ↓ ↓
96 |
97 | import { FormattedMessage } from 'react-intl';
98 | ;
99 |
100 | `;
101 |
102 | exports[`default with a variable as the defaultMessage: with a variable as the defaultMessage 1`] = `
103 |
104 | import { FormattedMessage } from 'react-intl';
105 |
106 | const message = "variable message";
107 |
108 | ;
109 |
110 | ↓ ↓ ↓ ↓ ↓ ↓
111 |
112 | import { FormattedMessage } from 'react-intl';
113 | const message = "variable message";
114 | ;
115 |
116 | `;
117 |
118 | exports[`extractComments = false default: default 1`] = `
119 |
120 | import { FormattedMessage } from 'react-intl';
121 |
122 | ;
123 |
124 | ↓ ↓ ↓ ↓ ↓ ↓
125 |
126 | import { FormattedMessage } from 'react-intl';
127 | ;
128 |
129 | `;
130 |
131 | exports[`filebase = true default: default 1`] = `
132 |
133 | import { FormattedMessage } from 'react-intl';
134 |
135 | ;
136 |
137 | ↓ ↓ ↓ ↓ ↓ ↓
138 |
139 | import { FormattedMessage } from 'react-intl';
140 | ;
141 |
142 | `;
143 |
144 | exports[`includeExportName = all default: default 1`] = `
145 |
146 | import { FormattedMessage } from 'react-intl';
147 |
148 | ;
149 |
150 | ↓ ↓ ↓ ↓ ↓ ↓
151 |
152 | import { FormattedMessage } from 'react-intl';
153 | ;
154 |
155 | `;
156 |
157 | exports[`includeExportName = true default: default 1`] = `
158 |
159 | import { FormattedMessage } from 'react-intl';
160 |
161 | ;
162 |
163 | ↓ ↓ ↓ ↓ ↓ ↓
164 |
165 | import { FormattedMessage } from 'react-intl';
166 | ;
167 |
168 | `;
169 |
170 | exports[`removePrefix = "src" default: default 1`] = `
171 |
172 | import { FormattedMessage } from 'react-intl';
173 |
174 | ;
175 |
176 | ↓ ↓ ↓ ↓ ↓ ↓
177 |
178 | import { FormattedMessage } from 'react-intl';
179 | ;
180 |
181 | `;
182 |
183 | exports[`removePrefix = "src.__fixtures__" default: default 1`] = `
184 |
185 | import { FormattedMessage } from 'react-intl';
186 |
187 | ;
188 |
189 | ↓ ↓ ↓ ↓ ↓ ↓
190 |
191 | import { FormattedMessage } from 'react-intl';
192 | ;
193 |
194 | `;
195 |
196 | exports[`removePrefix = "src/" -- with slash default: default 1`] = `
197 |
198 | import { FormattedMessage } from 'react-intl';
199 |
200 | ;
201 |
202 | ↓ ↓ ↓ ↓ ↓ ↓
203 |
204 | import { FormattedMessage } from 'react-intl';
205 | ;
206 |
207 | `;
208 |
209 | exports[`removePrefix = /__fixtures__/ default: default 1`] = `
210 |
211 | import { FormattedMessage } from 'react-intl';
212 |
213 | ;
214 |
215 | ↓ ↓ ↓ ↓ ↓ ↓
216 |
217 | import { FormattedMessage } from 'react-intl';
218 | ;
219 |
220 | `;
221 |
222 | exports[`removePrefix = false default: default 1`] = `
223 |
224 | import { FormattedMessage } from 'react-intl';
225 |
226 | ;
227 |
228 | ↓ ↓ ↓ ↓ ↓ ↓
229 |
230 | import { FormattedMessage } from 'react-intl';
231 | ;
232 |
233 | `;
234 |
235 | exports[`removePrefix = true, includeExportName = all default: default 1`] = `
236 |
237 | import { FormattedMessage } from 'react-intl';
238 |
239 | ;
240 |
241 | ↓ ↓ ↓ ↓ ↓ ↓
242 |
243 | import { FormattedMessage } from 'react-intl';
244 | ;
245 |
246 | `;
247 |
248 | exports[`removePrefix = true, includeExportName = true default: default 1`] = `
249 |
250 | import { FormattedMessage } from 'react-intl';
251 |
252 | ;
253 |
254 | ↓ ↓ ↓ ↓ ↓ ↓
255 |
256 | import { FormattedMessage } from 'react-intl';
257 | ;
258 |
259 | `;
260 |
261 | exports[`useKey = true default: default 1`] = `
262 |
263 | import { FormattedMessage } from 'react-intl';
264 |
265 | ;
266 |
267 | ↓ ↓ ↓ ↓ ↓ ↓
268 |
269 | import { FormattedMessage } from 'react-intl';
270 | ;
271 |
272 | `;
273 |
274 | exports[`useKey = true using key: using key 1`] = `
275 |
276 | import { FormattedMessage } from 'react-intl';
277 |
278 | ;
279 |
280 | ↓ ↓ ↓ ↓ ↓ ↓
281 |
282 | import { FormattedMessage } from 'react-intl';
283 | ;
284 |
285 | `;
286 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/hook.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`default multiple uses: multiple uses 1`] = `
4 |
5 | import { useIntl } from 'react-intl';
6 |
7 | intl.formatMessage({ defaultMessage: "hello" });
8 | intl.formatMessage({ defaultMessage: "hello" });
9 | intl.formatMessage({ defaultMessage: "some other message" });
10 |
11 | ↓ ↓ ↓ ↓ ↓ ↓
12 |
13 | import { useIntl } from 'react-intl';
14 | intl.formatMessage({
15 | defaultMessage: "hello",
16 | "id": "src.__fixtures__.613153351"
17 | });
18 | intl.formatMessage({
19 | defaultMessage: "hello",
20 | "id": "src.__fixtures__.613153351"
21 | });
22 | intl.formatMessage({
23 | defaultMessage: "some other message",
24 | "id": "src.__fixtures__.831373691"
25 | });
26 |
27 | `;
28 |
29 | exports[`default some supported use cases: some supported use cases 1`] = `
30 |
31 | import { useIntl } from 'react-intl';
32 |
33 | const Component2 = () => {
34 | const intl = useIntl();
35 | const label = intl.formatMessage({ defaultMessage: "hello" });
36 | return (
37 |
40 | );
41 | };
42 |
43 | ↓ ↓ ↓ ↓ ↓ ↓
44 |
45 | import { useIntl } from 'react-intl';
46 |
47 | const Component2 = () => {
48 | const intl = useIntl();
49 | const label = intl.formatMessage({
50 | defaultMessage: "hello",
51 | "id": "src.__fixtures__.613153351"
52 | });
53 | return ;
62 | };
63 |
64 | `;
65 |
66 | exports[`default with FormattedMessage imported as something else: with FormattedMessage imported as something else 1`] = `
67 |
68 | import { useIntl as i18n } from 'react-intl';
69 |
70 | intl.formatMessage({ defaultMessage: "i18n" });
71 |
72 | ↓ ↓ ↓ ↓ ↓ ↓
73 |
74 | import { useIntl as i18n } from 'react-intl';
75 | intl.formatMessage({
76 | defaultMessage: "i18n",
77 | "id": "src.__fixtures__.94348014"
78 | });
79 |
80 | `;
81 |
82 | exports[`default with a value interpolated in the message: with a value interpolated in the message 1`] = `
83 |
84 | import { useIntl } from 'react-intl';
85 |
86 | intl.formatMessage({ defaultMessage: \`template string 2\` });
87 |
88 | ↓ ↓ ↓ ↓ ↓ ↓
89 |
90 | import { useIntl } from 'react-intl';
91 | intl.formatMessage({
92 | defaultMessage: \`template string 2\`,
93 | "id": "src.__fixtures__.1045198380"
94 | });
95 |
96 | `;
97 |
98 | exports[`default with a variable as the defaultMessage: with a variable as the defaultMessage 1`] = `
99 |
100 | import { useIntl } from 'react-intl';
101 |
102 | const message = "variable message";
103 |
104 | intl.formatMessage({ defaultMessage: message });
105 |
106 | ↓ ↓ ↓ ↓ ↓ ↓
107 |
108 | import { useIntl } from 'react-intl';
109 | const message = "variable message";
110 | intl.formatMessage({
111 | defaultMessage: message,
112 | "id": "src.__fixtures__.3082794952"
113 | });
114 |
115 | `;
116 |
117 | exports[`default with a variable as the defaultMessage: with a variable as the defaultMessage 2`] = `
118 |
119 | import { useIntl } from 'react-intl';
120 | import { message } from './messages'
121 |
122 | intl.formatMessage(messages);
123 |
124 | ↓ ↓ ↓ ↓ ↓ ↓
125 |
126 | import { useIntl } from 'react-intl';
127 | import { message } from './messages';
128 | intl.formatMessage(messages);
129 |
130 | `;
131 |
132 | exports[`default with custom properties in formatMessage call: with custom properties in formatMessage call 1`] = `
133 |
134 | import { useIntl } from 'react-intl';
135 |
136 | intl.formatMessage({ defaultMessage: "custom prop", other: 123 });
137 |
138 | ↓ ↓ ↓ ↓ ↓ ↓
139 |
140 | import { useIntl } from 'react-intl';
141 | intl.formatMessage({
142 | defaultMessage: "custom prop",
143 | "id": "src.__fixtures__.2983810267",
144 | other: 123
145 | });
146 |
147 | `;
148 |
149 | exports[`default with injectIntl: with injectIntl 1`] = `
150 |
151 | import { injectIntl } from 'react-intl';
152 | function App({ intl }) {
153 | return {intl.formatMessage({ defaultMessage: 'hello' })}
154 | }
155 |
156 | export default injectIntl(App)
157 |
158 | ↓ ↓ ↓ ↓ ↓ ↓
159 |
160 | import { injectIntl } from 'react-intl';
161 |
162 | function App({
163 | intl
164 | }) {
165 | return {intl.formatMessage({
166 | defaultMessage: 'hello',
167 | "id": "src.__fixtures__.613153351"
168 | })}
;
169 | }
170 |
171 | export default injectIntl(App);
172 |
173 | `;
174 |
175 | exports[`default with useIntl hook imported: with useIntl hook imported 1`] = `
176 |
177 | import { useIntl } from 'react-intl';
178 |
179 | intl.formatMessage({ defaultMessage: "hello" });
180 |
181 | ↓ ↓ ↓ ↓ ↓ ↓
182 |
183 | import { useIntl } from 'react-intl';
184 | intl.formatMessage({
185 | defaultMessage: "hello",
186 | "id": "src.__fixtures__.613153351"
187 | });
188 |
189 | `;
190 |
191 | exports[`default withKeyFlag: withKeyFlag 1`] = `
192 |
193 | import { useIntl } from 'react-intl';
194 | intl.formatMessage({
195 | key: 'foobar',
196 | defaultMessage: 'hello'
197 | });
198 |
199 | ↓ ↓ ↓ ↓ ↓ ↓
200 |
201 | import { useIntl } from 'react-intl';
202 | intl.formatMessage({
203 | key: 'foobar',
204 | defaultMessage: 'hello',
205 | "id": "src.__fixtures__.613153351"
206 | });
207 |
208 | `;
209 |
210 | exports[`filebase = true with useIntl hook imported: with useIntl hook imported 1`] = `
211 |
212 | import { useIntl } from 'react-intl';
213 |
214 | intl.formatMessage({ defaultMessage: "hello" });
215 |
216 | ↓ ↓ ↓ ↓ ↓ ↓
217 |
218 | import { useIntl } from 'react-intl';
219 | intl.formatMessage({
220 | defaultMessage: "hello",
221 | "id": "src.__fixtures__.messages.613153351"
222 | });
223 |
224 | `;
225 |
226 | exports[`removePrefix = "src" with useIntl hook imported: with useIntl hook imported 1`] = `
227 |
228 | import { useIntl } from 'react-intl';
229 |
230 | intl.formatMessage({ defaultMessage: "hello" });
231 |
232 | ↓ ↓ ↓ ↓ ↓ ↓
233 |
234 | import { useIntl } from 'react-intl';
235 | intl.formatMessage({
236 | defaultMessage: "hello",
237 | "id": "__fixtures__.613153351"
238 | });
239 |
240 | `;
241 |
242 | exports[`removePrefix = "src.__fixtures__" with useIntl hook imported: with useIntl hook imported 1`] = `
243 |
244 | import { useIntl } from 'react-intl';
245 |
246 | intl.formatMessage({ defaultMessage: "hello" });
247 |
248 | ↓ ↓ ↓ ↓ ↓ ↓
249 |
250 | import { useIntl } from 'react-intl';
251 | intl.formatMessage({
252 | defaultMessage: "hello",
253 | "id": "613153351"
254 | });
255 |
256 | `;
257 |
258 | exports[`removePrefix = "src/" -- with slash with useIntl hook imported: with useIntl hook imported 1`] = `
259 |
260 | import { useIntl } from 'react-intl';
261 |
262 | intl.formatMessage({ defaultMessage: "hello" });
263 |
264 | ↓ ↓ ↓ ↓ ↓ ↓
265 |
266 | import { useIntl } from 'react-intl';
267 | intl.formatMessage({
268 | defaultMessage: "hello",
269 | "id": "__fixtures__.613153351"
270 | });
271 |
272 | `;
273 |
274 | exports[`removePrefix = /__fixtures__/ with useIntl hook imported: with useIntl hook imported 1`] = `
275 |
276 | import { useIntl } from 'react-intl';
277 |
278 | intl.formatMessage({ defaultMessage: "hello" });
279 |
280 | ↓ ↓ ↓ ↓ ↓ ↓
281 |
282 | import { useIntl } from 'react-intl';
283 | intl.formatMessage({
284 | defaultMessage: "hello",
285 | "id": "src.613153351"
286 | });
287 |
288 | `;
289 |
290 | exports[`removePrefix = false with useIntl hook imported: with useIntl hook imported 1`] = `
291 |
292 | import { useIntl } from 'react-intl';
293 |
294 | intl.formatMessage({ defaultMessage: "hello" });
295 |
296 | ↓ ↓ ↓ ↓ ↓ ↓
297 |
298 | import { useIntl } from 'react-intl';
299 | intl.formatMessage({
300 | defaultMessage: "hello",
301 | "id": "src.__fixtures__.613153351"
302 | });
303 |
304 | `;
305 |
306 | exports[`useKey = true with useIntl hook imported: with useIntl hook imported 1`] = `
307 |
308 | import { useIntl } from 'react-intl';
309 |
310 | intl.formatMessage({ defaultMessage: "hello" });
311 |
312 | ↓ ↓ ↓ ↓ ↓ ↓
313 |
314 | import { useIntl } from 'react-intl';
315 | intl.formatMessage({
316 | defaultMessage: "hello",
317 | "id": "src.__fixtures__.613153351"
318 | });
319 |
320 | `;
321 |
322 | exports[`useKey = true withKeyFlag: withKeyFlag 1`] = `
323 |
324 | import { useIntl } from 'react-intl';
325 | intl.formatMessage({
326 | key: 'foobar',
327 | defaultMessage: 'hello'
328 | });
329 |
330 | ↓ ↓ ↓ ↓ ↓ ↓
331 |
332 | import { useIntl } from 'react-intl';
333 | intl.formatMessage({
334 | key: 'foobar',
335 | defaultMessage: 'hello',
336 | "id": "src.__fixtures__.foobar"
337 | });
338 |
339 | `;
340 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/index.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`default Object: Object 1`] = `
4 |
5 | import { defineMessages } from 'react-intl'
6 |
7 | defineMessages({
8 | new: {
9 | id: 'this is id',
10 | defaultMessage: 'id',
11 | },
12 | world: {
13 | defaultMessage: 'world',
14 | },
15 | headerTitle: {
16 | defaultMessage: 'Welcome to dashboard {name}!',
17 | description: 'Message to greet the user.',
18 | },
19 | })
20 |
21 | ↓ ↓ ↓ ↓ ↓ ↓
22 |
23 | import { defineMessages } from 'react-intl';
24 | defineMessages({
25 | new: {
26 | id: 'this is id',
27 | defaultMessage: 'id'
28 | },
29 | world: {
30 | "id": "src.__fixtures__.world",
31 | defaultMessage: 'world'
32 | },
33 | headerTitle: {
34 | "id": "src.__fixtures__.headerTitle",
35 | defaultMessage: 'Welcome to dashboard {name}!',
36 | description: 'Message to greet the user.'
37 | }
38 | });
39 |
40 | `;
41 |
42 | exports[`default default: default 1`] = `
43 |
44 | import { defineMessages } from 'react-intl'
45 |
46 | export default defineMessages({
47 | hello: 'hello',
48 | })
49 |
50 | ↓ ↓ ↓ ↓ ↓ ↓
51 |
52 | import { defineMessages } from 'react-intl';
53 | export default defineMessages({
54 | hello: {
55 | "id": "src.__fixtures__.hello",
56 | "defaultMessage": 'hello'
57 | }
58 | });
59 |
60 | `;
61 |
62 | exports[`default eval string: eval string 1`] = `
63 |
64 | import { defineMessages } from 'react-intl'
65 |
66 | export default defineMessages({
67 | hello: 'hello' + 'world',
68 | })
69 |
70 | ↓ ↓ ↓ ↓ ↓ ↓
71 |
72 | import { defineMessages } from 'react-intl';
73 | export default defineMessages({
74 | hello: {
75 | "id": "src.__fixtures__.helloworld",
76 | "defaultMessage": "helloworld"
77 | }
78 | });
79 |
80 | `;
81 |
82 | exports[`default import as: import as 1`] = `
83 |
84 | import { defineMessages as m } from 'react-intl'
85 |
86 | m({
87 | hello: 'hello'
88 | })
89 |
90 | ↓ ↓ ↓ ↓ ↓ ↓
91 |
92 | import { defineMessages as m } from 'react-intl';
93 | m({
94 | hello: {
95 | "id": "src.__fixtures__.hello",
96 | "defaultMessage": 'hello'
97 | }
98 | });
99 |
100 | `;
101 |
102 | exports[`default leading comment with description: leading comment with description 1`] = `
103 |
104 | import { defineMessages } from 'react-intl'
105 |
106 | export default defineMessages({
107 |
108 | // This comment should not be used
109 | world: {
110 | defaultMessage: 'hello world',
111 | description: 'The hello world',
112 | }
113 | })
114 |
115 | ↓ ↓ ↓ ↓ ↓ ↓
116 |
117 | import { defineMessages } from 'react-intl';
118 | export default defineMessages({
119 | // This comment should not be used
120 | world: {
121 | "id": "src.__fixtures__.world",
122 | defaultMessage: 'hello world',
123 | description: 'The hello world'
124 | }
125 | });
126 |
127 | `;
128 |
129 | exports[`default leading comment: leading comment 1`] = `
130 |
131 | import { defineMessages } from 'react-intl'
132 |
133 | export default defineMessages({
134 | // The main Hello of our app.
135 | hello: 'hello',
136 |
137 | // Another Hello,
138 | // multiline this time
139 | world: {
140 | id: 'hello.world',
141 | defaultMessage: 'hello world',
142 | }
143 | })
144 |
145 | ↓ ↓ ↓ ↓ ↓ ↓
146 |
147 | import { defineMessages } from 'react-intl';
148 | export default defineMessages({
149 | // The main Hello of our app.
150 | hello: {
151 | "id": "src.__fixtures__.hello",
152 | "defaultMessage": 'hello',
153 | "description": "The main Hello of our app."
154 | },
155 | // Another Hello,
156 | // multiline this time
157 | world: {
158 | id: 'hello.world',
159 | defaultMessage: 'hello world',
160 | "description": "Another Hello,\\nmultiline this time"
161 | }
162 | });
163 |
164 | `;
165 |
166 | exports[`default multi export: multi export 1`] = `
167 |
168 | import { defineMessages } from 'react-intl'
169 |
170 | export const extra = defineMessages({
171 | hello: 'hello world extra'
172 | })
173 |
174 | export default defineMessages({
175 | hello: 'hello world',
176 | })
177 |
178 | ↓ ↓ ↓ ↓ ↓ ↓
179 |
180 | import { defineMessages } from 'react-intl';
181 | export const extra = defineMessages({
182 | hello: {
183 | "id": "src.__fixtures__.hello",
184 | "defaultMessage": 'hello world extra'
185 | }
186 | });
187 | export default defineMessages({
188 | hello: {
189 | "id": "src.__fixtures__.hello",
190 | "defaultMessage": 'hello world'
191 | }
192 | });
193 |
194 | `;
195 |
196 | exports[`default not transform if callee is not identifier: not transform if callee is not identifier 1`] = `
197 |
198 | import { defineMessages } from 'react-intl'
199 |
200 | const m = [defineMessages]
201 |
202 | export default m[0]({
203 | hello: 'hello world'
204 | })
205 |
206 | ↓ ↓ ↓ ↓ ↓ ↓
207 |
208 | import { defineMessages } from 'react-intl';
209 | const m = [defineMessages];
210 | export default m[0]({
211 | hello: 'hello world'
212 | });
213 |
214 | `;
215 |
216 | exports[`default not transform if defineMessages is not imported: not transform if defineMessages is not imported 1`] = `
217 |
218 | import any from 'any-module'
219 |
220 | export default defineMessages({
221 | hello: 'hello'
222 | })
223 |
224 | ↓ ↓ ↓ ↓ ↓ ↓
225 |
226 | import any from 'any-module';
227 | export default defineMessages({
228 | hello: 'hello'
229 | });
230 |
231 | `;
232 |
233 | exports[`default not transform when defineMessages argumens is empty: not transform when defineMessages argumens is empty 1`] = `
234 |
235 | import { defineMessages } from 'react-intl'
236 |
237 | export default defineMessages()
238 |
239 | ↓ ↓ ↓ ↓ ↓ ↓
240 |
241 | import { defineMessages } from 'react-intl';
242 | export default defineMessages();
243 |
244 | `;
245 |
246 | exports[`default not transform when defineMessages argumens is not object: not transform when defineMessages argumens is not object 1`] = `
247 |
248 | import { defineMessages } from 'react-intl'
249 |
250 | export default defineMessages(1)
251 |
252 | ↓ ↓ ↓ ↓ ↓ ↓
253 |
254 | import { defineMessages } from 'react-intl';
255 | export default defineMessages(1);
256 |
257 | `;
258 |
259 | exports[`default not transfrom when the variable can not be found: not transfrom when the variable can not be found 1`] = `
260 |
261 | import { defineMessages } from 'react-intl'
262 |
263 | export default defineMessages(messages)
264 |
265 | ↓ ↓ ↓ ↓ ↓ ↓
266 |
267 | import { defineMessages } from 'react-intl';
268 | export default defineMessages(messages);
269 |
270 | `;
271 |
272 | exports[`default string literal: string literal 1`] = `
273 |
274 | import { defineMessages } from 'react-intl'
275 |
276 | defineMessages({
277 | 'hello': 'hello world'
278 | })
279 |
280 | ↓ ↓ ↓ ↓ ↓ ↓
281 |
282 | import { defineMessages } from 'react-intl';
283 | defineMessages({
284 | 'hello': {
285 | "id": "src.__fixtures__.hello",
286 | "defaultMessage": 'hello world'
287 | }
288 | });
289 |
290 | `;
291 |
292 | exports[`default when using the variable: when using the variable 1`] = `
293 |
294 | import { defineMessages } from 'react-intl'
295 |
296 | const messages = {hello: 'hello'}
297 |
298 | export default defineMessages(messages)
299 |
300 | ↓ ↓ ↓ ↓ ↓ ↓
301 |
302 | import { defineMessages } from 'react-intl';
303 | const messages = {
304 | hello: {
305 | "id": "src.__fixtures__.hello",
306 | "defaultMessage": 'hello'
307 | }
308 | };
309 | export default defineMessages(messages);
310 |
311 | `;
312 |
313 | exports[`default with include value: with include value 1`] = `
314 |
315 | import { defineMessages } from 'react-intl'
316 |
317 | defineMessages({
318 | hello: \`hello world \${1}\`,
319 | })
320 |
321 | ↓ ↓ ↓ ↓ ↓ ↓
322 |
323 | import { defineMessages } from 'react-intl';
324 | defineMessages({
325 | hello: {
326 | "id": "src.__fixtures__.hello",
327 | "defaultMessage": \`hello world \${1}\`
328 | }
329 | });
330 |
331 | `;
332 |
333 | exports[`default with other func: with other func 1`] = `
334 |
335 | import { defineMessages } from 'react-intl'
336 |
337 | defineMessages({
338 | hello: 'hello',
339 | })
340 |
341 | hello({
342 | id: 'hoge',
343 | })
344 |
345 | ↓ ↓ ↓ ↓ ↓ ↓
346 |
347 | import { defineMessages } from 'react-intl';
348 | defineMessages({
349 | hello: {
350 | "id": "src.__fixtures__.hello",
351 | "defaultMessage": 'hello'
352 | }
353 | });
354 | hello({
355 | id: 'hoge'
356 | });
357 |
358 | `;
359 |
360 | exports[`default with other specifier: with other specifier 1`] = `
361 |
362 | import { defineMessages, FormattedMessage } from 'react-intl'
363 |
364 | export default defineMessages({
365 | hello: 'hello world',
366 | })
367 |
368 | ↓ ↓ ↓ ↓ ↓ ↓
369 |
370 | import { defineMessages, FormattedMessage } from 'react-intl';
371 | export default defineMessages({
372 | hello: {
373 | "id": "src.__fixtures__.hello",
374 | "defaultMessage": 'hello world'
375 | }
376 | });
377 |
378 | `;
379 |
380 | exports[`extractComments = false default: default 1`] = `
381 |
382 | import { defineMessages } from 'react-intl'
383 |
384 | export default defineMessages({
385 | hello: 'hello',
386 | })
387 |
388 | ↓ ↓ ↓ ↓ ↓ ↓
389 |
390 | import { defineMessages } from 'react-intl';
391 | export default defineMessages({
392 | hello: {
393 | "id": "src.__fixtures__.hello",
394 | "defaultMessage": 'hello'
395 | }
396 | });
397 |
398 | `;
399 |
400 | exports[`extractComments = false leading comment with description: leading comment with description 1`] = `
401 |
402 | import { defineMessages } from 'react-intl'
403 |
404 | export default defineMessages({
405 |
406 | // This comment should not be used
407 | world: {
408 | defaultMessage: 'hello world',
409 | description: 'The hello world',
410 | }
411 | })
412 |
413 | ↓ ↓ ↓ ↓ ↓ ↓
414 |
415 | import { defineMessages } from 'react-intl';
416 | export default defineMessages({
417 | // This comment should not be used
418 | world: {
419 | "id": "src.__fixtures__.world",
420 | defaultMessage: 'hello world',
421 | description: 'The hello world'
422 | }
423 | });
424 |
425 | `;
426 |
427 | exports[`extractComments = false leading comment: leading comment 1`] = `
428 |
429 | import { defineMessages } from 'react-intl'
430 |
431 | export default defineMessages({
432 | // The main Hello of our app.
433 | hello: 'hello',
434 |
435 | // Another Hello,
436 | // multiline this time
437 | world: {
438 | id: 'hello.world',
439 | defaultMessage: 'hello world',
440 | }
441 | })
442 |
443 | ↓ ↓ ↓ ↓ ↓ ↓
444 |
445 | import { defineMessages } from 'react-intl';
446 | export default defineMessages({
447 | // The main Hello of our app.
448 | hello: {
449 | "id": "src.__fixtures__.hello",
450 | "defaultMessage": 'hello'
451 | },
452 | // Another Hello,
453 | // multiline this time
454 | world: {
455 | id: 'hello.world',
456 | defaultMessage: 'hello world'
457 | }
458 | });
459 |
460 | `;
461 |
462 | exports[`filebase = true default: default 1`] = `
463 |
464 | import { defineMessages } from 'react-intl'
465 |
466 | export default defineMessages({
467 | hello: 'hello',
468 | })
469 |
470 | ↓ ↓ ↓ ↓ ↓ ↓
471 |
472 | import { defineMessages } from 'react-intl';
473 | export default defineMessages({
474 | hello: {
475 | "id": "src.__fixtures__.messages.hello",
476 | "defaultMessage": 'hello'
477 | }
478 | });
479 |
480 | `;
481 |
482 | exports[`includeExportName = all default: default 1`] = `
483 |
484 | import { defineMessages } from 'react-intl'
485 |
486 | export default defineMessages({
487 | hello: 'hello',
488 | })
489 |
490 | ↓ ↓ ↓ ↓ ↓ ↓
491 |
492 | import { defineMessages } from 'react-intl';
493 | export default defineMessages({
494 | hello: {
495 | "id": "src.__fixtures__.default.hello",
496 | "defaultMessage": 'hello'
497 | }
498 | });
499 |
500 | `;
501 |
502 | exports[`includeExportName = all multi export: multi export 1`] = `
503 |
504 | import { defineMessages } from 'react-intl'
505 |
506 | export const extra = defineMessages({
507 | hello: 'hello world extra'
508 | })
509 |
510 | export default defineMessages({
511 | hello: 'hello world',
512 | })
513 |
514 | ↓ ↓ ↓ ↓ ↓ ↓
515 |
516 | import { defineMessages } from 'react-intl';
517 | export const extra = defineMessages({
518 | hello: {
519 | "id": "src.__fixtures__.extra.hello",
520 | "defaultMessage": 'hello world extra'
521 | }
522 | });
523 | export default defineMessages({
524 | hello: {
525 | "id": "src.__fixtures__.default.hello",
526 | "defaultMessage": 'hello world'
527 | }
528 | });
529 |
530 | `;
531 |
532 | exports[`includeExportName = true default: default 1`] = `
533 |
534 | import { defineMessages } from 'react-intl'
535 |
536 | export default defineMessages({
537 | hello: 'hello',
538 | })
539 |
540 | ↓ ↓ ↓ ↓ ↓ ↓
541 |
542 | import { defineMessages } from 'react-intl';
543 | export default defineMessages({
544 | hello: {
545 | "id": "src.__fixtures__.hello",
546 | "defaultMessage": 'hello'
547 | }
548 | });
549 |
550 | `;
551 |
552 | exports[`includeExportName = true multi export: multi export 1`] = `
553 |
554 | import { defineMessages } from 'react-intl'
555 |
556 | export const extra = defineMessages({
557 | hello: 'hello world extra'
558 | })
559 |
560 | export default defineMessages({
561 | hello: 'hello world',
562 | })
563 |
564 | ↓ ↓ ↓ ↓ ↓ ↓
565 |
566 | import { defineMessages } from 'react-intl';
567 | export const extra = defineMessages({
568 | hello: {
569 | "id": "src.__fixtures__.extra.hello",
570 | "defaultMessage": 'hello world extra'
571 | }
572 | });
573 | export default defineMessages({
574 | hello: {
575 | "id": "src.__fixtures__.hello",
576 | "defaultMessage": 'hello world'
577 | }
578 | });
579 |
580 | `;
581 |
582 | exports[`moduleSourceNameTest default: default 1`] = `
583 |
584 | import { defineMessages } from 'react-intl'
585 |
586 | export default defineMessages({
587 | hello: 'hello',
588 | })
589 |
590 | ↓ ↓ ↓ ↓ ↓ ↓
591 |
592 | import { defineMessages } from 'react-intl';
593 | export default defineMessages({
594 | hello: 'hello'
595 | });
596 |
597 | `;
598 |
599 | exports[`moduleSourceNameTest moduleSourceName: moduleSourceName 1`] = `
600 |
601 | import { defineMessages } from 'gatsby-plugin-intl'
602 |
603 | export default defineMessages({
604 | hello: 'hello',
605 | })
606 |
607 | ↓ ↓ ↓ ↓ ↓ ↓
608 |
609 | import { defineMessages } from 'gatsby-plugin-intl';
610 | export default defineMessages({
611 | hello: {
612 | "id": "src.__fixtures__.hello",
613 | "defaultMessage": 'hello'
614 | }
615 | });
616 |
617 | `;
618 |
619 | exports[`relativeTo = "" default: default 1`] = `
620 |
621 | import { defineMessages } from 'react-intl'
622 |
623 | export default defineMessages({
624 | hello: 'hello',
625 | })
626 |
627 | ↓ ↓ ↓ ↓ ↓ ↓
628 |
629 | import { defineMessages } from 'react-intl';
630 | export default defineMessages({
631 | hello: {
632 | "id": "src.__fixtures__.hello",
633 | "defaultMessage": 'hello'
634 | }
635 | });
636 |
637 | `;
638 |
639 | exports[`relativeTo = "" multi export: multi export 1`] = `
640 |
641 | import { defineMessages } from 'react-intl'
642 |
643 | export const extra = defineMessages({
644 | hello: 'hello world extra'
645 | })
646 |
647 | export default defineMessages({
648 | hello: 'hello world',
649 | })
650 |
651 | ↓ ↓ ↓ ↓ ↓ ↓
652 |
653 | import { defineMessages } from 'react-intl';
654 | export const extra = defineMessages({
655 | hello: {
656 | "id": "src.__fixtures__.hello",
657 | "defaultMessage": 'hello world extra'
658 | }
659 | });
660 | export default defineMessages({
661 | hello: {
662 | "id": "src.__fixtures__.hello",
663 | "defaultMessage": 'hello world'
664 | }
665 | });
666 |
667 | `;
668 |
669 | exports[`relativeTo = "../" default: default 1`] = `
670 |
671 | import { defineMessages } from 'react-intl'
672 |
673 | export default defineMessages({
674 | hello: 'hello',
675 | })
676 |
677 | ↓ ↓ ↓ ↓ ↓ ↓
678 |
679 | import { defineMessages } from 'react-intl';
680 | export default defineMessages({
681 | hello: {
682 | "id": "babel-plugin-react-intl-auto.src.__fixtures__.hello",
683 | "defaultMessage": 'hello'
684 | }
685 | });
686 |
687 | `;
688 |
689 | exports[`relativeTo = "../" multi export: multi export 1`] = `
690 |
691 | import { defineMessages } from 'react-intl'
692 |
693 | export const extra = defineMessages({
694 | hello: 'hello world extra'
695 | })
696 |
697 | export default defineMessages({
698 | hello: 'hello world',
699 | })
700 |
701 | ↓ ↓ ↓ ↓ ↓ ↓
702 |
703 | import { defineMessages } from 'react-intl';
704 | export const extra = defineMessages({
705 | hello: {
706 | "id": "babel-plugin-react-intl-auto.src.__fixtures__.hello",
707 | "defaultMessage": 'hello world extra'
708 | }
709 | });
710 | export default defineMessages({
711 | hello: {
712 | "id": "babel-plugin-react-intl-auto.src.__fixtures__.hello",
713 | "defaultMessage": 'hello world'
714 | }
715 | });
716 |
717 | `;
718 |
719 | exports[`removePrefix = "src" default: default 1`] = `
720 |
721 | import { defineMessages } from 'react-intl'
722 |
723 | export default defineMessages({
724 | hello: 'hello',
725 | })
726 |
727 | ↓ ↓ ↓ ↓ ↓ ↓
728 |
729 | import { defineMessages } from 'react-intl';
730 | export default defineMessages({
731 | hello: {
732 | "id": "__fixtures__.hello",
733 | "defaultMessage": 'hello'
734 | }
735 | });
736 |
737 | `;
738 |
739 | exports[`removePrefix = "src.__fixtures__" default: default 1`] = `
740 |
741 | import { defineMessages } from 'react-intl'
742 |
743 | export default defineMessages({
744 | hello: 'hello',
745 | })
746 |
747 | ↓ ↓ ↓ ↓ ↓ ↓
748 |
749 | import { defineMessages } from 'react-intl';
750 | export default defineMessages({
751 | hello: {
752 | "id": "hello",
753 | "defaultMessage": 'hello'
754 | }
755 | });
756 |
757 | `;
758 |
759 | exports[`removePrefix = "src.__fixtures__", includeExportName = true default: default 1`] = `
760 |
761 | import { defineMessages } from 'react-intl'
762 |
763 | export default defineMessages({
764 | hello: 'hello',
765 | })
766 |
767 | ↓ ↓ ↓ ↓ ↓ ↓
768 |
769 | import { defineMessages } from 'react-intl';
770 | export default defineMessages({
771 | hello: {
772 | "id": "hello",
773 | "defaultMessage": 'hello'
774 | }
775 | });
776 |
777 | `;
778 |
779 | exports[`removePrefix = "src.__fixtures__", includeExportName = true multi export: multi export 1`] = `
780 |
781 | import { defineMessages } from 'react-intl'
782 |
783 | export const extra = defineMessages({
784 | hello: 'hello world extra'
785 | })
786 |
787 | export default defineMessages({
788 | hello: 'hello world',
789 | })
790 |
791 | ↓ ↓ ↓ ↓ ↓ ↓
792 |
793 | import { defineMessages } from 'react-intl';
794 | export const extra = defineMessages({
795 | hello: {
796 | "id": "extra.hello",
797 | "defaultMessage": 'hello world extra'
798 | }
799 | });
800 | export default defineMessages({
801 | hello: {
802 | "id": "hello",
803 | "defaultMessage": 'hello world'
804 | }
805 | });
806 |
807 | `;
808 |
809 | exports[`removePrefix = "src/" -- with slash default: default 1`] = `
810 |
811 | import { defineMessages } from 'react-intl'
812 |
813 | export default defineMessages({
814 | hello: 'hello',
815 | })
816 |
817 | ↓ ↓ ↓ ↓ ↓ ↓
818 |
819 | import { defineMessages } from 'react-intl';
820 | export default defineMessages({
821 | hello: {
822 | "id": "__fixtures__.hello",
823 | "defaultMessage": 'hello'
824 | }
825 | });
826 |
827 | `;
828 |
829 | exports[`removePrefix = /__fixtures__/ default: default 1`] = `
830 |
831 | import { defineMessages } from 'react-intl'
832 |
833 | export default defineMessages({
834 | hello: 'hello',
835 | })
836 |
837 | ↓ ↓ ↓ ↓ ↓ ↓
838 |
839 | import { defineMessages } from 'react-intl';
840 | export default defineMessages({
841 | hello: {
842 | "id": "_.hello",
843 | "defaultMessage": 'hello'
844 | }
845 | });
846 |
847 | `;
848 |
849 | exports[`removePrefix = false default: default 1`] = `
850 |
851 | import { defineMessages } from 'react-intl'
852 |
853 | export default defineMessages({
854 | hello: 'hello',
855 | })
856 |
857 | ↓ ↓ ↓ ↓ ↓ ↓
858 |
859 | import { defineMessages } from 'react-intl';
860 | export default defineMessages({
861 | hello: {
862 | "id": "src.__fixtures__.hello",
863 | "defaultMessage": 'hello'
864 | }
865 | });
866 |
867 | `;
868 |
869 | exports[`removePrefix = false multi export: multi export 1`] = `
870 |
871 | import { defineMessages } from 'react-intl'
872 |
873 | export const extra = defineMessages({
874 | hello: 'hello world extra'
875 | })
876 |
877 | export default defineMessages({
878 | hello: 'hello world',
879 | })
880 |
881 | ↓ ↓ ↓ ↓ ↓ ↓
882 |
883 | import { defineMessages } from 'react-intl';
884 | export const extra = defineMessages({
885 | hello: {
886 | "id": "src.__fixtures__.hello",
887 | "defaultMessage": 'hello world extra'
888 | }
889 | });
890 | export default defineMessages({
891 | hello: {
892 | "id": "src.__fixtures__.hello",
893 | "defaultMessage": 'hello world'
894 | }
895 | });
896 |
897 | `;
898 |
899 | exports[`removePrefix = true, includeExportName = all default: default 1`] = `
900 |
901 | import { defineMessages } from 'react-intl'
902 |
903 | export default defineMessages({
904 | hello: 'hello',
905 | })
906 |
907 | ↓ ↓ ↓ ↓ ↓ ↓
908 |
909 | import { defineMessages } from 'react-intl';
910 | export default defineMessages({
911 | hello: {
912 | "id": "default.hello",
913 | "defaultMessage": 'hello'
914 | }
915 | });
916 |
917 | `;
918 |
919 | exports[`removePrefix = true, includeExportName = all multi export: multi export 1`] = `
920 |
921 | import { defineMessages } from 'react-intl'
922 |
923 | export const extra = defineMessages({
924 | hello: 'hello world extra'
925 | })
926 |
927 | export default defineMessages({
928 | hello: 'hello world',
929 | })
930 |
931 | ↓ ↓ ↓ ↓ ↓ ↓
932 |
933 | import { defineMessages } from 'react-intl';
934 | export const extra = defineMessages({
935 | hello: {
936 | "id": "extra.hello",
937 | "defaultMessage": 'hello world extra'
938 | }
939 | });
940 | export default defineMessages({
941 | hello: {
942 | "id": "default.hello",
943 | "defaultMessage": 'hello world'
944 | }
945 | });
946 |
947 | `;
948 |
949 | exports[`removePrefix = true, includeExportName = true default: default 1`] = `
950 |
951 | import { defineMessages } from 'react-intl'
952 |
953 | export default defineMessages({
954 | hello: 'hello',
955 | })
956 |
957 | ↓ ↓ ↓ ↓ ↓ ↓
958 |
959 | import { defineMessages } from 'react-intl';
960 | export default defineMessages({
961 | hello: {
962 | "id": "hello",
963 | "defaultMessage": 'hello'
964 | }
965 | });
966 |
967 | `;
968 |
969 | exports[`removePrefix = true, includeExportName = true multi export: multi export 1`] = `
970 |
971 | import { defineMessages } from 'react-intl'
972 |
973 | export const extra = defineMessages({
974 | hello: 'hello world extra'
975 | })
976 |
977 | export default defineMessages({
978 | hello: 'hello world',
979 | })
980 |
981 | ↓ ↓ ↓ ↓ ↓ ↓
982 |
983 | import { defineMessages } from 'react-intl';
984 | export const extra = defineMessages({
985 | hello: {
986 | "id": "extra.hello",
987 | "defaultMessage": 'hello world extra'
988 | }
989 | });
990 | export default defineMessages({
991 | hello: {
992 | "id": "hello",
993 | "defaultMessage": 'hello world'
994 | }
995 | });
996 |
997 | `;
998 |
999 | exports[`separator = "" default: default 1`] = `
1000 |
1001 | import { defineMessages } from 'react-intl'
1002 |
1003 | export default defineMessages({
1004 | hello: 'hello',
1005 | })
1006 |
1007 | ↓ ↓ ↓ ↓ ↓ ↓
1008 |
1009 | import { defineMessages } from 'react-intl';
1010 | export default defineMessages({
1011 | hello: {
1012 | "id": "src__fixtures__hello",
1013 | "defaultMessage": 'hello'
1014 | }
1015 | });
1016 |
1017 | `;
1018 |
1019 | exports[`separator = "" multi export: multi export 1`] = `
1020 |
1021 | import { defineMessages } from 'react-intl'
1022 |
1023 | export const extra = defineMessages({
1024 | hello: 'hello world extra'
1025 | })
1026 |
1027 | export default defineMessages({
1028 | hello: 'hello world',
1029 | })
1030 |
1031 | ↓ ↓ ↓ ↓ ↓ ↓
1032 |
1033 | import { defineMessages } from 'react-intl';
1034 | export const extra = defineMessages({
1035 | hello: {
1036 | "id": "src__fixtures__hello",
1037 | "defaultMessage": 'hello world extra'
1038 | }
1039 | });
1040 | export default defineMessages({
1041 | hello: {
1042 | "id": "src__fixtures__hello",
1043 | "defaultMessage": 'hello world'
1044 | }
1045 | });
1046 |
1047 | `;
1048 |
1049 | exports[`separator = "_" default: default 1`] = `
1050 |
1051 | import { defineMessages } from 'react-intl'
1052 |
1053 | export default defineMessages({
1054 | hello: 'hello',
1055 | })
1056 |
1057 | ↓ ↓ ↓ ↓ ↓ ↓
1058 |
1059 | import { defineMessages } from 'react-intl';
1060 | export default defineMessages({
1061 | hello: {
1062 | "id": "src___fixtures___hello",
1063 | "defaultMessage": 'hello'
1064 | }
1065 | });
1066 |
1067 | `;
1068 |
1069 | exports[`separator = "_" multi export: multi export 1`] = `
1070 |
1071 | import { defineMessages } from 'react-intl'
1072 |
1073 | export const extra = defineMessages({
1074 | hello: 'hello world extra'
1075 | })
1076 |
1077 | export default defineMessages({
1078 | hello: 'hello world',
1079 | })
1080 |
1081 | ↓ ↓ ↓ ↓ ↓ ↓
1082 |
1083 | import { defineMessages } from 'react-intl';
1084 | export const extra = defineMessages({
1085 | hello: {
1086 | "id": "src___fixtures___hello",
1087 | "defaultMessage": 'hello world extra'
1088 | }
1089 | });
1090 | export default defineMessages({
1091 | hello: {
1092 | "id": "src___fixtures___hello",
1093 | "defaultMessage": 'hello world'
1094 | }
1095 | });
1096 |
1097 | `;
1098 |
1099 | exports[`separator = "foo" default: default 1`] = `
1100 |
1101 | import { defineMessages } from 'react-intl'
1102 |
1103 | export default defineMessages({
1104 | hello: 'hello',
1105 | })
1106 |
1107 | ↓ ↓ ↓ ↓ ↓ ↓
1108 |
1109 | import { defineMessages } from 'react-intl';
1110 | export default defineMessages({
1111 | hello: {
1112 | "id": "srcfoo__fixtures__foohello",
1113 | "defaultMessage": 'hello'
1114 | }
1115 | });
1116 |
1117 | `;
1118 |
1119 | exports[`separator = "foo" multi export: multi export 1`] = `
1120 |
1121 | import { defineMessages } from 'react-intl'
1122 |
1123 | export const extra = defineMessages({
1124 | hello: 'hello world extra'
1125 | })
1126 |
1127 | export default defineMessages({
1128 | hello: 'hello world',
1129 | })
1130 |
1131 | ↓ ↓ ↓ ↓ ↓ ↓
1132 |
1133 | import { defineMessages } from 'react-intl';
1134 | export const extra = defineMessages({
1135 | hello: {
1136 | "id": "srcfoo__fixtures__foohello",
1137 | "defaultMessage": 'hello world extra'
1138 | }
1139 | });
1140 | export default defineMessages({
1141 | hello: {
1142 | "id": "srcfoo__fixtures__foohello",
1143 | "defaultMessage": 'hello world'
1144 | }
1145 | });
1146 |
1147 | `;
1148 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/injection.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`default multiple uses: multiple uses 1`] = `
4 |
5 | import { injectIntl } from 'react-intl';
6 |
7 | intl.formatMessage({ defaultMessage: "hello" });
8 | intl.formatMessage({ defaultMessage: "hello" });
9 | intl.formatMessage({ defaultMessage: "some other message" });
10 |
11 | ↓ ↓ ↓ ↓ ↓ ↓
12 |
13 | import { injectIntl } from 'react-intl';
14 | intl.formatMessage({
15 | defaultMessage: "hello",
16 | "id": "src.__fixtures__.613153351"
17 | });
18 | intl.formatMessage({
19 | defaultMessage: "hello",
20 | "id": "src.__fixtures__.613153351"
21 | });
22 | intl.formatMessage({
23 | defaultMessage: "some other message",
24 | "id": "src.__fixtures__.831373691"
25 | });
26 |
27 | `;
28 |
29 | exports[`default some supported use cases: some supported use cases 1`] = `
30 |
31 | import { injectIntl } from 'react-intl';
32 |
33 | const Component2 = ({ intl }) => {
34 | const label = intl.formatMessage({ defaultMessage: "hello" });
35 | return (
36 |
39 | );
40 | };
41 | injectIntl(Components2);
42 |
43 | ↓ ↓ ↓ ↓ ↓ ↓
44 |
45 | import { injectIntl } from 'react-intl';
46 |
47 | const Component2 = ({
48 | intl
49 | }) => {
50 | const label = intl.formatMessage({
51 | defaultMessage: "hello",
52 | "id": "src.__fixtures__.613153351"
53 | });
54 | return ;
63 | };
64 |
65 | injectIntl(Components2);
66 |
67 | `;
68 |
69 | exports[`default with FormattedMessage imported as something else: with FormattedMessage imported as something else 1`] = `
70 |
71 | import { injectIntl as i18n } from 'react-intl';
72 |
73 | intl.formatMessage({ defaultMessage: "i18n" });
74 |
75 | ↓ ↓ ↓ ↓ ↓ ↓
76 |
77 | import { injectIntl as i18n } from 'react-intl';
78 | intl.formatMessage({
79 | defaultMessage: "i18n",
80 | "id": "src.__fixtures__.94348014"
81 | });
82 |
83 | `;
84 |
85 | exports[`default with Injection API HOC imported: with Injection API HOC imported 1`] = `
86 |
87 | import { injectIntl } from 'react-intl';
88 |
89 | intl.formatMessage({ defaultMessage: "hello" });
90 |
91 | ↓ ↓ ↓ ↓ ↓ ↓
92 |
93 | import { injectIntl } from 'react-intl';
94 | intl.formatMessage({
95 | defaultMessage: "hello",
96 | "id": "src.__fixtures__.613153351"
97 | });
98 |
99 | `;
100 |
101 | exports[`default with a value interpolated in the message: with a value interpolated in the message 1`] = `
102 |
103 | import { injectIntl } from 'react-intl';
104 |
105 | intl.formatMessage({ defaultMessage: \`template string 2\` });
106 |
107 | ↓ ↓ ↓ ↓ ↓ ↓
108 |
109 | import { injectIntl } from 'react-intl';
110 | intl.formatMessage({
111 | defaultMessage: \`template string 2\`,
112 | "id": "src.__fixtures__.1045198380"
113 | });
114 |
115 | `;
116 |
117 | exports[`default with a variable as the defaultMessage: with a variable as the defaultMessage 1`] = `
118 |
119 | import { injectIntl } from 'react-intl';
120 |
121 | const message = "variable message";
122 |
123 | intl.formatMessage({ defaultMessage: message });
124 |
125 | ↓ ↓ ↓ ↓ ↓ ↓
126 |
127 | import { injectIntl } from 'react-intl';
128 | const message = "variable message";
129 | intl.formatMessage({
130 | defaultMessage: message,
131 | "id": "src.__fixtures__.3082794952"
132 | });
133 |
134 | `;
135 |
136 | exports[`default with a variable as the defaultMessage: with a variable as the defaultMessage 2`] = `
137 |
138 | import { injectIntl } from 'react-intl';
139 | import { message } from './messages';
140 |
141 | intl.formatMessage(message);
142 |
143 | ↓ ↓ ↓ ↓ ↓ ↓
144 |
145 | import { injectIntl } from 'react-intl';
146 | import { message } from './messages';
147 | intl.formatMessage(message);
148 |
149 | `;
150 |
151 | exports[`default with custom properties in formatMessage call: with custom properties in formatMessage call 1`] = `
152 |
153 | import { injectIntl } from 'react-intl';
154 |
155 | intl.formatMessage({ defaultMessage: "custom prop", other: 123 });
156 |
157 | ↓ ↓ ↓ ↓ ↓ ↓
158 |
159 | import { injectIntl } from 'react-intl';
160 | intl.formatMessage({
161 | defaultMessage: "custom prop",
162 | "id": "src.__fixtures__.2983810267",
163 | other: 123
164 | });
165 |
166 | `;
167 |
168 | exports[`filebase = true with Injection API HOC imported: with Injection API HOC imported 1`] = `
169 |
170 | import { injectIntl } from 'react-intl';
171 |
172 | intl.formatMessage({ defaultMessage: "hello" });
173 |
174 | ↓ ↓ ↓ ↓ ↓ ↓
175 |
176 | import { injectIntl } from 'react-intl';
177 | intl.formatMessage({
178 | defaultMessage: "hello",
179 | "id": "src.__fixtures__.messages.613153351"
180 | });
181 |
182 | `;
183 |
184 | exports[`removePrefix = "src" with Injection API HOC imported: with Injection API HOC imported 1`] = `
185 |
186 | import { injectIntl } from 'react-intl';
187 |
188 | intl.formatMessage({ defaultMessage: "hello" });
189 |
190 | ↓ ↓ ↓ ↓ ↓ ↓
191 |
192 | import { injectIntl } from 'react-intl';
193 | intl.formatMessage({
194 | defaultMessage: "hello",
195 | "id": "__fixtures__.613153351"
196 | });
197 |
198 | `;
199 |
200 | exports[`removePrefix = "src.__fixtures__" with Injection API HOC imported: with Injection API HOC imported 1`] = `
201 |
202 | import { injectIntl } from 'react-intl';
203 |
204 | intl.formatMessage({ defaultMessage: "hello" });
205 |
206 | ↓ ↓ ↓ ↓ ↓ ↓
207 |
208 | import { injectIntl } from 'react-intl';
209 | intl.formatMessage({
210 | defaultMessage: "hello",
211 | "id": "613153351"
212 | });
213 |
214 | `;
215 |
216 | exports[`removePrefix = "src/" -- with slash with Injection API HOC imported: with Injection API HOC imported 1`] = `
217 |
218 | import { injectIntl } from 'react-intl';
219 |
220 | intl.formatMessage({ defaultMessage: "hello" });
221 |
222 | ↓ ↓ ↓ ↓ ↓ ↓
223 |
224 | import { injectIntl } from 'react-intl';
225 | intl.formatMessage({
226 | defaultMessage: "hello",
227 | "id": "__fixtures__.613153351"
228 | });
229 |
230 | `;
231 |
232 | exports[`removePrefix = /__fixtures__/ with Injection API HOC imported: with Injection API HOC imported 1`] = `
233 |
234 | import { injectIntl } from 'react-intl';
235 |
236 | intl.formatMessage({ defaultMessage: "hello" });
237 |
238 | ↓ ↓ ↓ ↓ ↓ ↓
239 |
240 | import { injectIntl } from 'react-intl';
241 | intl.formatMessage({
242 | defaultMessage: "hello",
243 | "id": "src.613153351"
244 | });
245 |
246 | `;
247 |
248 | exports[`removePrefix = false with Injection API HOC imported: with Injection API HOC imported 1`] = `
249 |
250 | import { injectIntl } from 'react-intl';
251 |
252 | intl.formatMessage({ defaultMessage: "hello" });
253 |
254 | ↓ ↓ ↓ ↓ ↓ ↓
255 |
256 | import { injectIntl } from 'react-intl';
257 | intl.formatMessage({
258 | defaultMessage: "hello",
259 | "id": "src.__fixtures__.613153351"
260 | });
261 |
262 | `;
263 |
--------------------------------------------------------------------------------
/src/__tests__/components.test.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import { cases } from '../utils/testUtils'
3 |
4 | const filename = path.resolve(__dirname, '..', '__fixtures__', 'messages.js')
5 |
6 | const defaultTest = {
7 | title: 'default',
8 | code: `
9 | import { FormattedMessage } from 'react-intl';
10 |
11 | ;
12 | `,
13 | }
14 |
15 | const multiUseTest = {
16 | title: 'multiple uses',
17 | code: `
18 | import { FormattedMessage } from 'react-intl';
19 |
20 | ;
21 | ;
22 | `,
23 | }
24 |
25 | const allSupportedComponentsTest = {
26 | title: 'import all supported components',
27 | code: `
28 | import { FormattedHTMLMessage, FormattedMessage } from 'react-intl';
29 |
30 | ;
31 | ;
32 | `,
33 | }
34 |
35 | const withValueInMessageTest = {
36 | title: 'with a value interpolated in the message',
37 | code: `
38 | import { FormattedMessage } from 'react-intl';
39 |
40 | ;
41 | `,
42 | }
43 |
44 | const withVariableMessageTest = {
45 | title: 'with a variable as the defaultMessage',
46 | code: `
47 | import { FormattedMessage } from 'react-intl';
48 |
49 | const message = "variable message";
50 |
51 | ;
52 | `,
53 | }
54 |
55 | const importAsTest = {
56 | title: 'with FormattedMessage imported as something else',
57 | code: `
58 | import { FormattedMessage as T } from 'react-intl';
59 |
60 | ;
61 | `,
62 | }
63 |
64 | const nestedJSXTest = {
65 | title: 'with FormattedMessage nested in other JSX',
66 | code: `
67 | import { FormattedMessage } from 'react-intl';
68 |
69 |
70 |
71 |
72 | `,
73 | }
74 |
75 | const throwWhenNotAnalyzableTest = {
76 | title: 'throws if defaultMessage isn’t analyzable',
77 | code: `
78 | import { FormattedMessage } from 'react-intl';
79 |
80 | const getMsg = () => 'hello';
81 |
82 | ;
83 | `,
84 | error: /\[React Intl Auto\] defaultMessage must be statically evaluate-able for extraction/u,
85 | snapshot: false,
86 | }
87 |
88 | const notTransformIfNotImportedTest = {
89 | title: 'does nothing if components not imported from react-intl',
90 | snapshot: false,
91 | code: `
92 | import any from 'any-module';
93 | ;
94 | `,
95 | }
96 |
97 | const notTransformIfSpreadAttributeTest = {
98 | title: 'does nothing if component props are spread',
99 | snapshot: false,
100 | code: `
101 | import { FormattedMessage } from 'react-intl';
102 | const props = {
103 | defaultMessage: 'hello'
104 | };
105 | ;
106 | `,
107 | }
108 |
109 | const keyTest = {
110 | title: 'using key',
111 | code: `
112 | import { FormattedMessage } from 'react-intl';
113 |
114 | ;
115 | `,
116 | }
117 |
118 | const tests = [
119 | defaultTest,
120 | multiUseTest,
121 | allSupportedComponentsTest,
122 | withValueInMessageTest,
123 | withVariableMessageTest,
124 | importAsTest,
125 | nestedJSXTest,
126 | throwWhenNotAnalyzableTest,
127 | notTransformIfNotImportedTest,
128 | notTransformIfSpreadAttributeTest,
129 | keyTest,
130 | ]
131 |
132 | cases(filename, [
133 | { title: 'default', tests },
134 | {
135 | title: 'useKey = true',
136 | tests: [defaultTest, keyTest],
137 | pluginOptions: { useKey: true },
138 | },
139 |
140 | {
141 | title: 'removePrefix = "src"',
142 | tests: [defaultTest],
143 | pluginOptions: { removePrefix: 'src' },
144 | },
145 |
146 | {
147 | title: 'removePrefix = "src/" -- with slash',
148 | tests: [defaultTest],
149 | pluginOptions: { removePrefix: 'src/' },
150 | },
151 |
152 | {
153 | title: 'filebase = true',
154 | tests: [defaultTest],
155 | pluginOptions: { filebase: true },
156 | },
157 |
158 | {
159 | title: 'includeExportName = true',
160 | tests: [defaultTest],
161 | pluginOptions: { includeExportName: true },
162 | },
163 |
164 | {
165 | title: 'includeExportName = all',
166 | tests: [defaultTest],
167 | pluginOptions: { includeExportName: 'all' },
168 | },
169 |
170 | {
171 | title: 'removePrefix = true, includeExportName = true',
172 | tests: [defaultTest],
173 | pluginOptions: { removePrefix: true, includeExportName: true },
174 | },
175 |
176 | {
177 | title: 'removePrefix = false',
178 | tests: [defaultTest],
179 | pluginOptions: { removePrefix: false },
180 | },
181 |
182 | {
183 | title: 'removePrefix = true, includeExportName = all',
184 | tests: [defaultTest],
185 | pluginOptions: { removePrefix: true, includeExportName: 'all' },
186 | },
187 |
188 | {
189 | title: 'extractComments = false',
190 | tests: [defaultTest],
191 | pluginOptions: { extractComments: false },
192 | },
193 |
194 | {
195 | title: 'removePrefix = /__fixtures__/',
196 | tests: [defaultTest],
197 | pluginOptions: { removePrefix: /[\\/]__fixtures__/u },
198 | },
199 |
200 | {
201 | title: 'removePrefix = "src.__fixtures__"',
202 | tests: [defaultTest],
203 | pluginOptions: { removePrefix: 'src.__fixtures__' },
204 | },
205 | ])
206 |
--------------------------------------------------------------------------------
/src/__tests__/hook.test.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import { cases } from '../utils/testUtils'
3 |
4 | const filename = path.resolve(__dirname, '..', '__fixtures__', 'messages.js')
5 |
6 | const defaultTest = {
7 | title: 'with useIntl hook imported',
8 | code: `
9 | import { useIntl } from 'react-intl';
10 |
11 | intl.formatMessage({ defaultMessage: "hello" });
12 | `,
13 | }
14 |
15 | const multiUseTest = {
16 | title: 'multiple uses',
17 | code: `
18 | import { useIntl } from 'react-intl';
19 |
20 | intl.formatMessage({ defaultMessage: "hello" });
21 | intl.formatMessage({ defaultMessage: "hello" });
22 | intl.formatMessage({ defaultMessage: "some other message" });
23 | `,
24 | }
25 |
26 | const withValueInMessageTest = {
27 | title: 'with a value interpolated in the message',
28 | code: `
29 | import { useIntl } from 'react-intl';
30 |
31 | intl.formatMessage({ defaultMessage: \`template string ${1 + 1}\` });
32 | `,
33 | }
34 |
35 | const withVariableMessageTest = {
36 | title: 'with a variable as the defaultMessage',
37 | code: `
38 | import { useIntl } from 'react-intl';
39 |
40 | const message = "variable message";
41 |
42 | intl.formatMessage({ defaultMessage: message });
43 | `,
44 | }
45 |
46 | const withVariableMessageDescriptor = {
47 | title: 'with a variable as the defaultMessage',
48 | code: `
49 | import { useIntl } from 'react-intl';
50 | import { message } from './messages'
51 |
52 | intl.formatMessage(messages);
53 | `,
54 | }
55 |
56 | const withCustomProperties = {
57 | title: 'with custom properties in formatMessage call',
58 | code: `
59 | import { useIntl } from 'react-intl';
60 |
61 | intl.formatMessage({ defaultMessage: "custom prop", other: 123 });
62 | `,
63 | }
64 |
65 | const someSupportedUseCases = {
66 | title: 'some supported use cases',
67 | code: `
68 | import { useIntl } from 'react-intl';
69 |
70 | const Component2 = () => {
71 | const intl = useIntl();
72 | const label = intl.formatMessage({ defaultMessage: "hello" });
73 | return (
74 |
77 | );
78 | };
79 | `,
80 | }
81 |
82 | const importAsTest = {
83 | title: 'with FormattedMessage imported as something else',
84 | code: `
85 | import { useIntl as i18n } from 'react-intl';
86 |
87 | intl.formatMessage({ defaultMessage: "i18n" });
88 | `,
89 | }
90 |
91 | const throwWhenNotAnalyzableTest = {
92 | title: 'throws if defaultMessage isn’t analyzable',
93 | code: `
94 | import { useIntl } from 'react-intl';
95 | const getMsg = () => 'hello';
96 | intl.formatMessage({
97 | defaultMessage: getMsg(),
98 | });
99 | `,
100 | error: /\[React Intl Auto\] defaultMessage must be statically evaluate-able for extraction/u,
101 | snapshot: false,
102 | }
103 |
104 | const notTransformIfNotImportedTest = {
105 | title: 'does nothing if react-intl is not imported',
106 | snapshot: false,
107 | code: `
108 | import any from 'any-module';
109 | intl.formatMessage({
110 | defaultMessage: "hello"
111 | });
112 | `,
113 | }
114 |
115 | const notTransformIfIdIsProvided = {
116 | title: 'does nothing if id is already provided',
117 | snapshot: false,
118 | code: `
119 | import { useIntl } from 'react-intl';
120 | intl.formatMessage({
121 | id: "my.custom.id",
122 | defaultMessage: "hello"
123 | });
124 | `,
125 | }
126 |
127 | const injectIntlWithProps = {
128 | title: 'with injectIntl',
129 | code: `
130 | import { injectIntl } from 'react-intl';
131 | function App({ intl }) {
132 | return {intl.formatMessage({ defaultMessage: 'hello' })}
133 | }
134 |
135 | export default injectIntl(App)
136 | `,
137 | }
138 |
139 | const withKeyFlag = {
140 | title: 'withKeyFlag',
141 | code: `
142 | import { useIntl } from 'react-intl';
143 | intl.formatMessage({
144 | key: 'foobar',
145 | defaultMessage: 'hello'
146 | });
147 | `,
148 | }
149 |
150 | const tests = [
151 | defaultTest,
152 | multiUseTest,
153 | withValueInMessageTest,
154 | withVariableMessageTest,
155 | withVariableMessageDescriptor,
156 | withCustomProperties,
157 | someSupportedUseCases,
158 | importAsTest,
159 | throwWhenNotAnalyzableTest,
160 | notTransformIfNotImportedTest,
161 | notTransformIfIdIsProvided,
162 | injectIntlWithProps,
163 | withKeyFlag,
164 | ]
165 |
166 | cases(filename, [
167 | { title: 'default', tests },
168 | {
169 | title: 'removePrefix = "src"',
170 | tests: [defaultTest],
171 | pluginOptions: { removePrefix: 'src' },
172 | },
173 |
174 | {
175 | title: 'removePrefix = "src/" -- with slash',
176 | tests: [defaultTest],
177 | pluginOptions: { removePrefix: 'src/' },
178 | },
179 |
180 | {
181 | title: 'filebase = true',
182 | tests: [defaultTest],
183 | pluginOptions: { filebase: true },
184 | },
185 |
186 | // {
187 | // title: 'includeExportName = true',
188 | // tests: [defaultTest],
189 | // pluginOptions: { includeExportName: true },
190 | // },
191 | // {
192 | // title: 'includeExportName = all',
193 | // tests: [defaultTest],
194 | // pluginOptions: { includeExportName: 'all' },
195 | // },
196 | // {
197 | // title: 'removePrefix = true, includeExportName = true',
198 | // tests: [defaultTest],
199 | // pluginOptions: { removePrefix: true, includeExportName: true },
200 | // },
201 | {
202 | title: 'removePrefix = false',
203 | tests: [defaultTest],
204 | pluginOptions: { removePrefix: false },
205 | },
206 |
207 | // {
208 | // title: 'removePrefix = true, includeExportName = all',
209 | // tests: [defaultTest],
210 | // pluginOptions: { removePrefix: true, includeExportName: 'all' },
211 | // },
212 | // {
213 | // title: 'extractComments = false',
214 | // tests: [defaultTest],
215 | // pluginOptions: { extractComments: false },
216 | // },
217 | {
218 | title: 'removePrefix = /__fixtures__/',
219 | tests: [defaultTest],
220 | pluginOptions: { removePrefix: /[\\/]__fixtures__/u },
221 | },
222 |
223 | {
224 | title: 'removePrefix = "src.__fixtures__"',
225 | tests: [defaultTest],
226 | pluginOptions: { removePrefix: 'src.__fixtures__' },
227 | },
228 | {
229 | title: 'useKey = true',
230 | tests: [defaultTest, withKeyFlag],
231 | pluginOptions: { useKey: true },
232 | },
233 | ])
234 |
--------------------------------------------------------------------------------
/src/__tests__/index.test.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import { cases } from '../utils/testUtils'
3 |
4 | const filename = path.resolve(__dirname, '..', '__fixtures__', 'messages.js')
5 |
6 | const defaultTest = {
7 | title: 'default',
8 | code: `
9 | import { defineMessages } from 'react-intl'
10 |
11 | export default defineMessages({
12 | hello: 'hello',
13 | })
14 | `,
15 | }
16 |
17 | const multiExportTest = {
18 | title: 'multi export',
19 | code: `
20 | import { defineMessages } from 'react-intl'
21 |
22 | export const extra = defineMessages({
23 | hello: 'hello world extra'
24 | })
25 |
26 | export default defineMessages({
27 | hello: 'hello world',
28 | })
29 | `,
30 | }
31 |
32 | const leadingCommentTest = {
33 | title: 'leading comment',
34 | code: `
35 | import { defineMessages } from 'react-intl'
36 |
37 | export default defineMessages({
38 | // The main Hello of our app.
39 | hello: 'hello',
40 |
41 | // Another Hello,
42 | // multiline this time
43 | world: {
44 | id: 'hello.world',
45 | defaultMessage: 'hello world',
46 | }
47 | })
48 | `,
49 | }
50 |
51 | const leadingCommentWithDescriptionTest = {
52 | title: 'leading comment with description',
53 | code: `
54 | import { defineMessages } from 'react-intl'
55 |
56 | export default defineMessages({
57 |
58 | // This comment should not be used
59 | world: {
60 | defaultMessage: 'hello world',
61 | description: 'The hello world',
62 | }
63 | })
64 | `,
65 | }
66 |
67 | const tests = [
68 | defaultTest,
69 | {
70 | title: 'with include value',
71 | code: `
72 | import { defineMessages } from 'react-intl'
73 |
74 | defineMessages({
75 | hello: \`hello world \${1}\`,
76 | })
77 | `,
78 | },
79 |
80 | {
81 | title: 'string literal',
82 | code: `
83 | import { defineMessages } from 'react-intl'
84 |
85 | defineMessages({
86 | 'hello': 'hello world'
87 | })
88 | `,
89 | },
90 |
91 | {
92 | title: 'Object',
93 | code: `
94 | import { defineMessages } from 'react-intl'
95 |
96 | defineMessages({
97 | new: {
98 | id: 'this is id',
99 | defaultMessage: 'id',
100 | },
101 | world: {
102 | defaultMessage: 'world',
103 | },
104 | headerTitle: {
105 | defaultMessage: 'Welcome to dashboard {name}!',
106 | description: 'Message to greet the user.',
107 | },
108 | })
109 | `,
110 | },
111 |
112 | {
113 | title: 'import as',
114 | code: `
115 | import { defineMessages as m } from 'react-intl'
116 |
117 | m({
118 | hello: 'hello'
119 | })
120 |
121 | `,
122 | },
123 |
124 | {
125 | title: 'with other func',
126 | code: `
127 | import { defineMessages } from 'react-intl'
128 |
129 | defineMessages({
130 | hello: 'hello',
131 | })
132 |
133 | hello({
134 | id: 'hoge',
135 | })
136 | `,
137 | },
138 |
139 | multiExportTest,
140 | {
141 | title: 'throw error when key is NumiricLiteral',
142 | code: `
143 | import { defineMessages } from 'react-intl'
144 |
145 | export default defineMessages({
146 | 1: 'hello',
147 | })
148 | `,
149 | error: /requires Object key or string literal/u,
150 | snapshot: false,
151 | },
152 |
153 | {
154 | title: 'not transform if defineMessages is not imported',
155 | code: `
156 | import any from 'any-module'
157 |
158 | export default defineMessages({
159 | hello: 'hello'
160 | })
161 | `,
162 | },
163 |
164 | {
165 | title: 'not transform when defineMessages argumens is not object',
166 | code: `
167 | import { defineMessages } from 'react-intl'
168 |
169 | export default defineMessages(1)
170 | `,
171 | },
172 |
173 | {
174 | title: 'when using the variable',
175 | code: `
176 | import { defineMessages } from 'react-intl'
177 |
178 | const messages = {hello: 'hello'}
179 |
180 | export default defineMessages(messages)
181 | `,
182 | },
183 |
184 | {
185 | title: 'not transfrom when the variable can not be found',
186 | code: `
187 | import { defineMessages } from 'react-intl'
188 |
189 | export default defineMessages(messages)
190 | `,
191 | },
192 |
193 | {
194 | title: 'not transform when defineMessages argumens is empty',
195 | code: `
196 | import { defineMessages } from 'react-intl'
197 |
198 | export default defineMessages()
199 | `,
200 | },
201 |
202 | {
203 | title: 'not transform if callee is not identifier',
204 | code: `
205 | import { defineMessages } from 'react-intl'
206 |
207 | const m = [defineMessages]
208 |
209 | export default m[0]({
210 | hello: 'hello world'
211 | })
212 | `,
213 | },
214 |
215 | {
216 | title: 'with other specifier',
217 | code: `
218 | import { defineMessages, FormattedMessage } from 'react-intl'
219 |
220 | export default defineMessages({
221 | hello: 'hello world',
222 | })
223 | `,
224 | },
225 |
226 | leadingCommentTest,
227 | leadingCommentWithDescriptionTest,
228 | {
229 | title: 'eval string',
230 | code: `
231 | import { defineMessages } from 'react-intl'
232 |
233 | export default defineMessages({
234 | hello: 'hello' + 'world',
235 | })
236 | `,
237 | },
238 | ]
239 |
240 | const moduleSourceNameTest = {
241 | title: 'moduleSourceName',
242 | code: `
243 | import { defineMessages } from 'gatsby-plugin-intl'
244 |
245 | export default defineMessages({
246 | hello: 'hello',
247 | })
248 | `,
249 | }
250 |
251 | cases(filename, [
252 | { title: 'default', tests },
253 | {
254 | title: 'removePrefix = "src"',
255 | tests: [defaultTest],
256 | pluginOptions: { removePrefix: 'src' },
257 | },
258 |
259 | {
260 | title: 'removePrefix = "src/" -- with slash',
261 | tests: [defaultTest],
262 | pluginOptions: { removePrefix: 'src/' },
263 | },
264 |
265 | {
266 | title: 'filebase = true',
267 | tests: [defaultTest],
268 | pluginOptions: { filebase: true },
269 | },
270 |
271 | {
272 | title: 'includeExportName = true',
273 | tests: [defaultTest, multiExportTest],
274 | pluginOptions: { includeExportName: true },
275 | },
276 |
277 | {
278 | title: 'includeExportName = all',
279 | tests: [defaultTest, multiExportTest],
280 | pluginOptions: { includeExportName: 'all' },
281 | },
282 |
283 | {
284 | title: 'removePrefix = true, includeExportName = true',
285 | tests: [defaultTest, multiExportTest],
286 | pluginOptions: { removePrefix: true, includeExportName: true },
287 | },
288 |
289 | {
290 | title: 'removePrefix = false',
291 | tests: [defaultTest, multiExportTest],
292 | pluginOptions: { removePrefix: false },
293 | },
294 |
295 | {
296 | title: 'removePrefix = true, includeExportName = all',
297 | tests: [defaultTest, multiExportTest],
298 | pluginOptions: { removePrefix: true, includeExportName: 'all' },
299 | },
300 |
301 | {
302 | title: 'extractComments = false',
303 | tests: [defaultTest, leadingCommentTest, leadingCommentWithDescriptionTest],
304 | pluginOptions: { extractComments: false },
305 | },
306 |
307 | {
308 | title: 'removePrefix = /__fixtures__/',
309 | tests: [defaultTest],
310 | pluginOptions: {
311 | removePrefix: /src[\\/]__f.+?_/u,
312 | includeExportName: true,
313 | },
314 | },
315 |
316 | {
317 | title: 'removePrefix = "src.__fixtures__"',
318 | tests: [defaultTest],
319 | pluginOptions: {
320 | removePrefix: 'src.__fixtures__',
321 | },
322 | },
323 |
324 | {
325 | title: 'removePrefix = "src.__fixtures__", includeExportName = true',
326 | tests: [defaultTest, multiExportTest],
327 | pluginOptions: {
328 | removePrefix: 'src.__fixtures__',
329 | includeExportName: true,
330 | },
331 | },
332 |
333 | {
334 | title: 'moduleSourceNameTest',
335 | tests: [defaultTest, moduleSourceNameTest],
336 | pluginOptions: {
337 | moduleSourceName: 'gatsby-plugin-intl',
338 | },
339 | },
340 |
341 | {
342 | title: 'separator = ""',
343 | tests: [defaultTest, multiExportTest],
344 | pluginOptions: {
345 | separator: '',
346 | },
347 | },
348 |
349 | {
350 | title: 'separator = "_"',
351 | // tests: [defaultTest, multiExportTest],
352 | tests: [defaultTest, multiExportTest],
353 | pluginOptions: {
354 | separator: '_',
355 | },
356 | },
357 |
358 | {
359 | title: 'separator = "foo"',
360 | tests: [defaultTest, multiExportTest],
361 | pluginOptions: {
362 | separator: 'foo',
363 | },
364 | },
365 |
366 | {
367 | title: 'relativeTo = "../"',
368 | tests: [defaultTest, multiExportTest],
369 | pluginOptions: {
370 | relativeTo: '..',
371 | },
372 | },
373 |
374 | {
375 | title: 'relativeTo = ""',
376 | tests: [defaultTest, multiExportTest],
377 | pluginOptions: {
378 | relativeTo: '',
379 | },
380 | },
381 | ])
382 |
--------------------------------------------------------------------------------
/src/__tests__/injection.test.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import { cases } from '../utils/testUtils'
3 |
4 | const filename = path.resolve(__dirname, '..', '__fixtures__', 'messages.js')
5 |
6 | const defaultTest = {
7 | title: 'with Injection API HOC imported',
8 | code: `
9 | import { injectIntl } from 'react-intl';
10 |
11 | intl.formatMessage({ defaultMessage: "hello" });
12 | `,
13 | }
14 |
15 | const multiUseTest = {
16 | title: 'multiple uses',
17 | code: `
18 | import { injectIntl } from 'react-intl';
19 |
20 | intl.formatMessage({ defaultMessage: "hello" });
21 | intl.formatMessage({ defaultMessage: "hello" });
22 | intl.formatMessage({ defaultMessage: "some other message" });
23 | `,
24 | }
25 |
26 | const withValueInMessageTest = {
27 | title: 'with a value interpolated in the message',
28 | code: `
29 | import { injectIntl } from 'react-intl';
30 |
31 | intl.formatMessage({ defaultMessage: \`template string ${1 + 1}\` });
32 | `,
33 | }
34 |
35 | const withVariableMessageTest = {
36 | title: 'with a variable as the defaultMessage',
37 | code: `
38 | import { injectIntl } from 'react-intl';
39 |
40 | const message = "variable message";
41 |
42 | intl.formatMessage({ defaultMessage: message });
43 | `,
44 | }
45 |
46 | const withVariableMessageDescriptor = {
47 | title: 'with a variable as the defaultMessage',
48 | code: `
49 | import { injectIntl } from 'react-intl';
50 | import { message } from './messages';
51 |
52 | intl.formatMessage(message);
53 | `,
54 | }
55 |
56 | const withCustomProperties = {
57 | title: 'with custom properties in formatMessage call',
58 | code: `
59 | import { injectIntl } from 'react-intl';
60 |
61 | intl.formatMessage({ defaultMessage: "custom prop", other: 123 });
62 | `,
63 | }
64 |
65 | const someSupportedUseCases = {
66 | title: 'some supported use cases',
67 | code: `
68 | import { injectIntl } from 'react-intl';
69 |
70 | const Component2 = ({ intl }) => {
71 | const label = intl.formatMessage({ defaultMessage: "hello" });
72 | return (
73 |
76 | );
77 | };
78 | injectIntl(Components2);
79 | `,
80 | }
81 |
82 | const importAsTest = {
83 | title: 'with FormattedMessage imported as something else',
84 | code: `
85 | import { injectIntl as i18n } from 'react-intl';
86 |
87 | intl.formatMessage({ defaultMessage: "i18n" });
88 | `,
89 | }
90 |
91 | const throwWhenNotAnalyzableTest = {
92 | title: 'throws if defaultMessage isn’t analyzable',
93 | code: `
94 | import { injectIntl } from 'react-intl';
95 | const getMsg = () => 'hello';
96 | intl.formatMessage({
97 | defaultMessage: getMsg(),
98 | });
99 | `,
100 | error: /\[React Intl Auto\] defaultMessage must be statically evaluate-able for extraction/u,
101 | snapshot: false,
102 | }
103 |
104 | const notTransformIfNotImportedTest = {
105 | title: 'does nothing if react-intl is not imported',
106 | snapshot: false,
107 | code: `
108 | import any from 'any-module';
109 | intl.formatMessage({
110 | defaultMessage: "hello"
111 | });
112 | `,
113 | }
114 |
115 | const notTransformIfIdIsProvided = {
116 | title: 'does nothing if id is already provided',
117 | snapshot: false,
118 | code: `
119 | import { injectIntl } from 'react-intl';
120 | intl.formatMessage({
121 | id: "my.custom.id",
122 | defaultMessage: "hello"
123 | });
124 | `,
125 | }
126 |
127 | const tests = [
128 | defaultTest,
129 | multiUseTest,
130 | withValueInMessageTest,
131 | withVariableMessageTest,
132 | withVariableMessageDescriptor,
133 | withCustomProperties,
134 | someSupportedUseCases,
135 | importAsTest,
136 | throwWhenNotAnalyzableTest,
137 | notTransformIfNotImportedTest,
138 | notTransformIfIdIsProvided,
139 | ]
140 |
141 | cases(filename, [
142 | { title: 'default', tests },
143 | {
144 | title: 'removePrefix = "src"',
145 | tests: [defaultTest],
146 | pluginOptions: { removePrefix: 'src' },
147 | },
148 |
149 | {
150 | title: 'removePrefix = "src/" -- with slash',
151 | tests: [defaultTest],
152 | pluginOptions: { removePrefix: 'src/' },
153 | },
154 |
155 | {
156 | title: 'filebase = true',
157 | tests: [defaultTest],
158 | pluginOptions: { filebase: true },
159 | },
160 |
161 | // {
162 | // title: 'includeExportName = true',
163 | // tests: [defaultTest],
164 | // pluginOptions: { includeExportName: true },
165 | // },
166 | // {
167 | // title: 'includeExportName = all',
168 | // tests: [defaultTest],
169 | // pluginOptions: { includeExportName: 'all' },
170 | // },
171 | // {
172 | // title: 'removePrefix = true, includeExportName = true',
173 | // tests: [defaultTest],
174 | // pluginOptions: { removePrefix: true, includeExportName: true },
175 | // },
176 | {
177 | title: 'removePrefix = false',
178 | tests: [defaultTest],
179 | pluginOptions: { removePrefix: false },
180 | },
181 |
182 | // {
183 | // title: 'removePrefix = true, includeExportName = all',
184 | // tests: [defaultTest],
185 | // pluginOptions: { removePrefix: true, includeExportName: 'all' },
186 | // },
187 | // {
188 | // title: 'extractComments = false',
189 | // tests: [defaultTest],
190 | // pluginOptions: { extractComments: false },
191 | // },
192 | {
193 | title: 'removePrefix = /__fixtures__/',
194 | tests: [defaultTest],
195 | pluginOptions: { removePrefix: /[\\/]__fixtures__/u },
196 | },
197 |
198 | {
199 | title: 'removePrefix = "src.__fixtures__"',
200 | tests: [defaultTest],
201 | pluginOptions: { removePrefix: 'src.__fixtures__' },
202 | },
203 | ])
204 |
--------------------------------------------------------------------------------
/src/babel-plugin-tester.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'babel-plugin-tester' {
2 | function tester(args: unknown): unknown
3 | export = tester
4 | }
5 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { PluginObj } from '@babel/core'
2 | import { State } from './types'
3 | import { visitJSXElement } from './visitors/jsx'
4 | import { addIdToDefineMessage } from './visitors/addIdToDefineMessage'
5 | import { addIdToFormatMessage } from './visitors/addIdToFormatMessage'
6 |
7 | export default function () {
8 | return {
9 | name: 'react-intl-auto',
10 | visitor: {
11 | JSXElement: visitJSXElement,
12 | CallExpression(path, state: State) {
13 | addIdToFormatMessage(path, state)
14 | addIdToDefineMessage(path, state)
15 | },
16 | },
17 | } as PluginObj
18 | }
19 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import { NodePath } from '@babel/traverse'
2 |
3 | type BabelTransformationFile = {
4 | opts: {
5 | filename: string
6 | babelrc: boolean
7 | configFile: boolean
8 | passPerPreset: boolean
9 | envName: string
10 | cwd: string
11 | root: string
12 | plugins: unknown[]
13 | presets: unknown[]
14 | parserOpts: object
15 | generatorOpts: object
16 | }
17 | declarations: {}
18 | path: NodePath | null
19 | ast: {}
20 | scope: unknown
21 | metadata: {}
22 | code: string
23 | inputMap: object | null
24 | }
25 |
26 | export type Opts = {
27 | removePrefix?: boolean | string | RegExp
28 | filebase?: boolean
29 | includeExportName?: boolean | 'all'
30 | extractComments?: boolean
31 | useKey?: boolean
32 | moduleSourceName?: string
33 | separator?: string
34 | relativeTo?: string
35 | }
36 |
37 | export type State = {
38 | file: BabelTransformationFile
39 | opts: Opts
40 | }
41 |
--------------------------------------------------------------------------------
/src/utils/getPrefix.ts:
--------------------------------------------------------------------------------
1 | import { relative, dirname, sep } from 'path'
2 | import { State } from '../types'
3 | import { dotPath } from '.'
4 |
5 | const escapeRegExp = (text: string) => {
6 | return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/gu, '\\$&')
7 | }
8 |
9 | const dotPathReplace = (
10 | fomatted: string,
11 | removePrefix: string,
12 | separator?: string
13 | ) => {
14 | const exp = `^${removePrefix.replace(/\//gu, '')}\\${dotPath(
15 | sep,
16 | separator
17 | )}?`
18 | let reg: RegExp
19 |
20 | // certain separators can throw an error and need to be escaped
21 | // e.g. "_"
22 | try {
23 | reg = new RegExp(exp, 'u')
24 | } catch (error) {
25 | reg = new RegExp(escapeRegExp(exp), 'u')
26 | }
27 |
28 | return dotPath(fomatted, separator).replace(reg, '')
29 | }
30 |
31 | export const getPrefix = (
32 | {
33 | file: {
34 | opts: { filename },
35 | },
36 | opts: { removePrefix, filebase = false, separator, relativeTo },
37 | }: State,
38 | exportName: string | null
39 | ) => {
40 | if (removePrefix === true) {
41 | return exportName === null ? '' : exportName
42 | }
43 | const file = relative(relativeTo || process.cwd(), filename)
44 | const fomatted = filebase ? file.replace(/\..+$/u, '') : dirname(file)
45 | removePrefix =
46 | removePrefix === undefined || removePrefix === false ? '' : removePrefix
47 |
48 | const fixed =
49 | removePrefix instanceof RegExp
50 | ? dotPath(fomatted.replace(removePrefix, ''), separator)
51 | : dotPathReplace(fomatted, removePrefix, separator)
52 |
53 | if (exportName === null) {
54 | return fixed
55 | }
56 |
57 | if (fixed === '') {
58 | return exportName
59 | }
60 |
61 | return `${fixed}.${exportName}`
62 | }
63 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | import { sep } from 'path'
2 | import * as t from '@babel/types'
3 | import murmur from 'murmurhash3js'
4 | import { NodePath } from '@babel/core'
5 |
6 | const REG = new RegExp(`\\${sep}`, 'gu')
7 |
8 | const isObjectProperties = (
9 | properties: NodePath[]
10 | ): properties is NodePath[] =>
11 | properties.every((p) => p.isObjectProperty())
12 |
13 | export const createHash = (message: string) => `${murmur.x86.hash32(message)}`
14 |
15 | export const dotPath = (str: string, separator: string | undefined = '.') =>
16 | str.replace(REG, separator)
17 |
18 | export const objectProperty = (
19 | key: string,
20 | value: string | t.StringLiteral | t.TemplateLiteral
21 | ) => {
22 | const valueNode = typeof value === 'string' ? t.stringLiteral(value) : value
23 | return t.objectProperty(t.stringLiteral(key), valueNode)
24 | }
25 |
26 | export function getObjectProperties(path: NodePath) {
27 | if (path.isObjectExpression()) {
28 | const properties = path.get('properties')
29 | if (isObjectProperties(properties)) {
30 | return properties
31 | }
32 | } else if (path.isIdentifier()) {
33 | const binding = path.scope.getBinding(path.node.name)
34 | if (!binding) {
35 | return null
36 | }
37 | const init = binding.path.get('init')
38 | if (!Array.isArray(init) && !init.type) {
39 | return null
40 | }
41 | const properties = binding.path.get('init.properties')
42 | if (Array.isArray(properties) && isObjectProperties(properties)) {
43 | return properties
44 | }
45 | }
46 | return null
47 | }
48 |
--------------------------------------------------------------------------------
/src/utils/isImportLocalName.ts:
--------------------------------------------------------------------------------
1 | import { State } from '../types'
2 |
3 | export const isImportLocalName = (
4 | name: string | null | undefined,
5 | allowedNames: string[],
6 | { file, opts: { moduleSourceName = 'react-intl' } }: State
7 | ) => {
8 | let isImported = false
9 |
10 | if (file && file.path) {
11 | file.path.traverse({
12 | ImportDeclaration: {
13 | exit(path) {
14 | const specifiers = path.get('specifiers')
15 | isImported =
16 | path.node.source.value.includes(moduleSourceName) &&
17 | specifiers.some((specifier) => {
18 | return (
19 | specifier.isImportSpecifier() &&
20 | allowedNames.includes(specifier.node.imported.name) &&
21 | (!name || specifier.node.local.name === name)
22 | )
23 | })
24 |
25 | if (isImported) {
26 | path.stop()
27 | }
28 | },
29 | },
30 | })
31 | }
32 |
33 | return isImported
34 | }
35 |
--------------------------------------------------------------------------------
/src/utils/testUtils.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-extraneous-dependencies
2 | import pluginTester from 'babel-plugin-tester'
3 | import { Opts } from '../types'
4 | import plugin from '..'
5 |
6 | export function cases(
7 | filename: string,
8 | testCases: {
9 | title: string
10 | tests: { title: string; code: string }[]
11 | pluginOptions?: Opts
12 | }[]
13 | ) {
14 | const defaultOpts = {
15 | title: '',
16 | plugin,
17 | snapshot: true,
18 | babelOptions: { filename, parserOpts: { plugins: ['jsx'] } },
19 | formatResult: (r: any) => r,
20 | tests: [],
21 | }
22 |
23 | for (const testCase of testCases) {
24 | testCase.tests = testCase.tests.map((t) => ({ ...t, title: t.title }))
25 | pluginTester({ ...defaultOpts, ...testCase })
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/visitors/addIdToDefineMessage.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'path'
2 | import { NodePath } from '@babel/core'
3 | import * as t from '@babel/types'
4 | import { State } from '../types'
5 | import { dotPath, objectProperty, getObjectProperties } from '../utils'
6 | import { isImportLocalName } from '../utils/isImportLocalName'
7 | import { getPrefix } from '../utils/getPrefix'
8 | // import blog from 'babel-log'
9 |
10 | const getId = (path: NodePath, prefix: string, separator?: string) => {
11 | let name
12 |
13 | if (path.isStringLiteral()) {
14 | name = path.node.value
15 | } else if (path.isIdentifier()) {
16 | name = path.node.name
17 | }
18 |
19 | if (!name) {
20 | throw new Error(`requires Object key or string literal`)
21 | }
22 |
23 | return dotPath(join(prefix, name), separator)
24 | }
25 |
26 | const getLeadingComment = (prop: NodePath) => {
27 | const commentNodes = prop.node.leadingComments
28 | return commentNodes
29 | ? commentNodes.map((node) => node.value.trim()).join('\n')
30 | : null
31 | }
32 |
33 | // eslint-disable-next-line max-lines-per-function
34 | const replaceProperties = (
35 | properties: NodePath[],
36 | state: State,
37 | exportName: string | null
38 | ) => {
39 | const prefix = getPrefix(state, exportName)
40 | const {
41 | opts: { separator },
42 | } = state
43 |
44 | for (const prop of properties) {
45 | const objectValuePath = prop.get('value')
46 | const objectKeyPath = prop.get('key')
47 | if (Array.isArray(objectKeyPath)) {
48 | return
49 | }
50 |
51 | const messageDescriptorProperties: t.ObjectProperty[] = []
52 |
53 | // { defaultMessage: 'hello', description: 'this is hello' }
54 | if (objectValuePath.isObjectExpression()) {
55 | const objProps = objectValuePath.get('properties') as NodePath<
56 | t.ObjectProperty
57 | >[]
58 |
59 | // { id: 'already has id', defaultMessage: 'hello' }
60 | const isNotHaveId = objProps.every((v) => {
61 | const keyPath = v.get('key')
62 | return !Array.isArray(keyPath) && keyPath.node.name !== 'id'
63 | })
64 |
65 | if (isNotHaveId) {
66 | const id = getId(objectKeyPath, prefix, separator)
67 |
68 | messageDescriptorProperties.push(objectProperty('id', id))
69 | }
70 |
71 | messageDescriptorProperties.push(...objProps.map((v) => v.node))
72 | } else if (
73 | objectValuePath.isStringLiteral() ||
74 | objectValuePath.isTemplateLiteral()
75 | ) {
76 | // 'hello' or `hello ${user}`
77 | const id = getId(objectKeyPath, prefix, separator)
78 |
79 | messageDescriptorProperties.push(
80 | objectProperty('id', id),
81 | objectProperty('defaultMessage', objectValuePath.node)
82 | )
83 | } else {
84 | const evaluated = prop.get('value').evaluate()
85 | if (evaluated.confident && typeof evaluated.value === 'string') {
86 | const id = dotPath(join(prefix, evaluated.value), separator)
87 |
88 | messageDescriptorProperties.push(
89 | objectProperty('id', id),
90 | objectProperty('defaultMessage', evaluated.value)
91 | )
92 | }
93 | }
94 |
95 | const { extractComments = true } = state.opts
96 |
97 | if (extractComments) {
98 | const hasDescription = messageDescriptorProperties.find(
99 | (v) => v.key.name === 'description'
100 | )
101 |
102 | if (!hasDescription) {
103 | const description = getLeadingComment(prop)
104 |
105 | if (description) {
106 | messageDescriptorProperties.push(
107 | objectProperty('description', description)
108 | )
109 | }
110 | }
111 | }
112 | objectValuePath.replaceWith(t.objectExpression(messageDescriptorProperties))
113 | }
114 | }
115 |
116 | const getExportName = (
117 | path: NodePath,
118 | includeExportName: boolean | 'all' | undefined
119 | ): string | null => {
120 | const namedExport = path.findParent((v) => v.isExportNamedDeclaration())
121 | const defaultExport = path.findParent((v) => v.isExportDefaultDeclaration())
122 |
123 | if (includeExportName && namedExport) {
124 | const idPath = namedExport.get('declaration.declarations.0.id') as NodePath<
125 | t.Identifier
126 | >
127 | return idPath.node.name
128 | }
129 |
130 | if (includeExportName === 'all' && defaultExport) {
131 | return 'default'
132 | }
133 |
134 | return null
135 | }
136 |
137 | const isDefineMessagesCall = (
138 | path: NodePath,
139 | state: State
140 | ): boolean => {
141 | /**
142 | Path "Identifier"
143 | name: "defineMessages"
144 | */
145 | const callee = path.get('callee')
146 |
147 | return (
148 | callee.isIdentifier() &&
149 | isImportLocalName(callee.node.name, ['defineMessages'], state)
150 | )
151 | }
152 |
153 | export function addIdToDefineMessage(
154 | path: NodePath,
155 | state: State
156 | ) {
157 | if (!isDefineMessagesCall(path, state)) {
158 | return
159 | }
160 |
161 | if (!path.get('arguments.0')) {
162 | return
163 | }
164 |
165 | const argPath = path.get('arguments.0') as NodePath<
166 | t.ObjectExpression | t.NumericLiteral | t.Identifier
167 | >
168 |
169 | const properties = getObjectProperties(argPath)
170 |
171 | if (!properties) {
172 | return
173 | }
174 |
175 | const exportName = getExportName(path, state.opts.includeExportName || false)
176 | replaceProperties(properties, state, exportName)
177 | }
178 |
--------------------------------------------------------------------------------
/src/visitors/addIdToFormatMessage.ts:
--------------------------------------------------------------------------------
1 | import { NodePath } from '@babel/core'
2 | import * as t from '@babel/types'
3 | import { State } from '../types'
4 | import { getPrefix } from '../utils/getPrefix'
5 | import { isImportLocalName } from '../utils/isImportLocalName'
6 | import { createHash, objectProperty, getObjectProperties } from '../utils'
7 | // import blog from 'babel-log'
8 |
9 | // check if given path is related to intl.formatMessage call
10 | function isFormatMessageCall(path: NodePath, state: State) {
11 | // injectIntl or useIntl imported
12 | if (!isImportLocalName(null, ['injectIntl', 'useIntl'], state)) {
13 | return false
14 | }
15 |
16 | /*
17 | Path "MemberExpression"
18 | computed: false
19 | object: Node "Identifier"
20 | name: "intl"
21 | property: Node "Identifier"
22 | name: "formatMessage"
23 | */
24 | const callee = path.get('callee')
25 | if (!callee.isMemberExpression()) {
26 | return false
27 | }
28 |
29 | /*
30 | Path "Identifier"
31 | name: "formatMessage"
32 | */
33 | const property = callee.get('property')
34 | const isFormatMessage =
35 | !Array.isArray(property) &&
36 | property.isIdentifier() &&
37 | property.node.name === 'formatMessage'
38 |
39 | /*
40 | Path "Identifier"
41 | name: "intl"
42 | */
43 | const objectPath = callee.get('object')
44 | const isIntl =
45 | !Array.isArray(objectPath) &&
46 | objectPath.isIdentifier() &&
47 | objectPath.node.name === 'intl'
48 |
49 | return Boolean(path.get('arguments.0')) && isIntl && isFormatMessage
50 | }
51 |
52 | function findProperty(
53 | properties: NodePath[],
54 | name: string
55 | ): NodePath | undefined {
56 | return properties.find((arg) => {
57 | const keyPath = arg.get('key')
58 | return !Array.isArray(keyPath) && keyPath.node.name === name
59 | })
60 | }
61 |
62 | function extractKeyValue(
63 | properties: NodePath[]
64 | ): string | null {
65 | const prop = findProperty(properties, 'key')
66 | if (prop) {
67 | const keyPath = prop.get('key')
68 | if (!Array.isArray(keyPath) && keyPath.node.name === 'key') {
69 | const valuePath = prop.get('value')
70 | const value = valuePath.evaluate().value
71 | return value
72 | }
73 | }
74 | return null
75 | }
76 |
77 | // add automatic ID to intl.formatMessage calls
78 | export function addIdToFormatMessage(
79 | path: NodePath,
80 | state: State
81 | ) {
82 | if (!isFormatMessageCall(path, state)) {
83 | // skip path if this is not intl.formatMessage call
84 | return
85 | }
86 |
87 | // intl.formatMessage first argument is the one which we would like to modify
88 | const arg0 = path.get('arguments.0') as NodePath
89 |
90 | const properties = getObjectProperties(arg0)
91 | // blog(properties)
92 |
93 | // at least defaultMessage property is required to do anything useful
94 | if (!properties) {
95 | return
96 | }
97 |
98 | // if "id" property is already added by a developer or by this script just skip this node
99 | if (findProperty(properties, 'id')) {
100 | return
101 | }
102 |
103 | const keyValue = extractKeyValue(properties)
104 |
105 | const defaultMessageProp = findProperty(properties, 'defaultMessage')
106 | if (defaultMessageProp) {
107 | // try to statically evaluate defaultMessage to generate hash
108 | const evaluated = defaultMessageProp.get('value').evaluate()
109 |
110 | if (!evaluated.confident || typeof evaluated.value !== 'string') {
111 | throw defaultMessageProp
112 | .get('value')
113 | .buildCodeFrameError(
114 | '[React Intl Auto] defaultMessage must be statically evaluate-able for extraction.'
115 | )
116 | }
117 |
118 | const id = getPrefix(
119 | state,
120 | state.opts.useKey && keyValue ? keyValue : createHash(evaluated.value)
121 | )
122 |
123 | defaultMessageProp.insertAfter(objectProperty('id', id))
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/visitors/jsx.ts:
--------------------------------------------------------------------------------
1 | import { NodePath } from '@babel/core'
2 | import * as t from '@babel/types'
3 | import { State } from '../types'
4 | import { createHash } from '../utils'
5 | import { isImportLocalName } from '../utils/isImportLocalName'
6 | import { getPrefix } from '../utils/getPrefix'
7 | // import blog from 'babel-log'
8 |
9 | // Process react-intl components
10 | const REACT_COMPONENTS = ['FormattedMessage', 'FormattedHTMLMessage']
11 |
12 | const getElementAttributePaths = (
13 | elementPath: NodePath
14 | ) => {
15 | if (!elementPath) {
16 | return {}
17 | }
18 |
19 | const attributesPath = elementPath.get('attributes') as NodePath<
20 | t.JSXAttribute
21 | >[]
22 |
23 | const defaultMessagePath = attributesPath.find(
24 | (attrPath) =>
25 | attrPath.node.name && attrPath.node.name.name === 'defaultMessage'
26 | )
27 |
28 | const idPath = attributesPath.find(
29 | (attrPath) => attrPath.node.name && attrPath.node.name.name === 'id'
30 | )
31 |
32 | const keyPath = attributesPath.find(
33 | (attrPath) => attrPath.node.name && attrPath.node.name.name === 'key'
34 | )
35 |
36 | return { id: idPath, defaultMessage: defaultMessagePath, key: keyPath }
37 | }
38 |
39 | /**
40 | Path "JSXAttribute"
41 | name: Node "JSXIdentifier"
42 | name: "defaultMessage"
43 | value: Node "StringLiteral"
44 | extra: Object {
45 | "raw": "\"hello\"",
46 | "rawValue": "hello",
47 | }
48 | value: "hello"
49 | */
50 | const extractFromValuePath = (jsxAttributePath: NodePath) => {
51 | // blog(jsxAttributePath)
52 | const valuePath = jsxAttributePath.get('value')
53 | if (!valuePath) {
54 | return null
55 | }
56 | /**
57 | Path "StringLiteral"
58 | extra: Object {
59 | "raw": "hello",
60 | "rawValue": "hello",
61 | }
62 | value: "hello"
63 | */
64 | if (valuePath.isStringLiteral()) {
65 | return valuePath.node.value
66 | }
67 |
68 | /**
69 | Path "JSXExpressionContainer"
70 | expression: Node "CallExpression"
71 | arguments: Array []
72 | callee: Node "Identifier"
73 | name: "getMsg"
74 | */
75 | if (valuePath.isJSXExpressionContainer()) {
76 | // Evaluate the message expression to see if it yields a string
77 | const evaluated = valuePath.get('expression').evaluate()
78 | /**
79 | Object {
80 | "confident": true,
81 | "deopt": null,
82 | "value": "variable message",
83 | }
84 | */
85 | if (evaluated.confident && typeof evaluated.value === 'string') {
86 | return evaluated.value
87 | }
88 | throw valuePath.buildCodeFrameError(
89 | `[React Intl Auto] ${
90 | jsxAttributePath.get('name').node.name
91 | } must be statically evaluate-able for extraction.`
92 | )
93 | }
94 |
95 | return null
96 | }
97 |
98 | const generateId = (
99 | defaultMessage: NodePath,
100 | state: State,
101 | key: NodePath | undefined
102 | ) => {
103 | // ID = path to the file + key
104 | let suffix = key && state.opts.useKey ? extractFromValuePath(key) : ''
105 | if (!suffix) {
106 | // ID = path to the file + hash of the defaultMessage
107 | const messageValue = extractFromValuePath(defaultMessage)
108 | if (messageValue) {
109 | suffix = createHash(messageValue)
110 | }
111 | }
112 |
113 | const prefix = getPrefix(state, suffix)
114 |
115 | // Insert an id attribute before the defaultMessage attribute
116 | defaultMessage.insertBefore(
117 | t.jsxAttribute(t.jsxIdentifier('id'), t.stringLiteral(prefix))
118 | )
119 | }
120 |
121 | export const visitJSXElement = (path: NodePath, state: State) => {
122 | const jsxOpeningElement = path.get('openingElement') as NodePath<
123 | t.JSXOpeningElement
124 | >
125 |
126 | // Is this a react-intl component? Handles both:
127 | // import { FormattedMessage as T } from 'react-intl'
128 | // import { FormattedMessage } from 'react-intl'
129 | if (
130 | t.isJSXIdentifier(jsxOpeningElement.node.name) &&
131 | isImportLocalName(jsxOpeningElement.node.name.name, REACT_COMPONENTS, state)
132 | ) {
133 | // Get the attributes for the component
134 | const { id, defaultMessage, key } = getElementAttributePaths(
135 | jsxOpeningElement
136 | )
137 |
138 | // If valid message but missing ID, generate one
139 | if (!id && defaultMessage) {
140 | generateId(defaultMessage, state, state.opts.useKey ? key : undefined)
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@akameco/tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": ["src"]
7 | }
8 |
--------------------------------------------------------------------------------
/types.d.ts:
--------------------------------------------------------------------------------
1 | import { MessageDescriptor } from 'react-intl'
2 |
3 | declare module 'react-intl' {
4 | interface ExtractableMessage {
5 | [key: string]: string
6 | }
7 |
8 | export function defineMessages(
9 | messages: T
10 | ): { [K in keyof T]: MessageDescriptor }
11 | }
12 |
--------------------------------------------------------------------------------