@nkzw/vite-plugin-fbtee 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +451 -0
- package/index.d.ts +11 -0
- package/index.js +53 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Nakazawa Tech & Meta Platforms, Inc. and affiliates.
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
# fbtee
|
|
2
|
+
|
|
3
|
+
**_fbtee_** is an internationalization framework for JavaScript and React. It lets you write translatable text inline, keep translator context next to the UI, and compile strings into a small runtime format for production.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
const Greeting = ({ name }) => (
|
|
7
|
+
<fbt desc="Greeting on the home screen">
|
|
8
|
+
Hello, <Name name={name} />!
|
|
9
|
+
</fbt>
|
|
10
|
+
);
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
_fbtee_ is a modern continuation of Facebook's `fbt`, rebuilt for TypeScript, ESM, React 19, Vite, Babel, SWC, Next.js & Oxc.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Inline translations for Better Developer Experience:** Embed translations directly into your code. No need to manage translation keys or wrap your code with `t()` functions. **fbtee** uses a compiler to extract strings from your code and prepare them for translation providers.
|
|
18
|
+
- **Proven in Production:** Built on Facebook's `fbt`, with over a decade of production usage serving billions of users, plus years in production at [Athena Crisis](https://athenacrisis.com).
|
|
19
|
+
- **Optimized Performance with IR:** Compiles translations into an Intermediate Representation (IR) for extracting strings, then optimizes the runtime output for performance.
|
|
20
|
+
- **Easy Setup:** Quick integration with Vite, Babel, SWC, Oxc, Next.js, and Expo.
|
|
21
|
+
|
|
22
|
+
## Getting Started
|
|
23
|
+
|
|
24
|
+
Use one of the templates if you are starting fresh:
|
|
25
|
+
|
|
26
|
+
- [Web App Template](https://github.com/nkzw-tech/web-app-template)
|
|
27
|
+
- [Expo App Template](https://github.com/nkzw-tech/expo-app-template)
|
|
28
|
+
|
|
29
|
+
For an existing app, install the runtime:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install fbtee
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
_fbtee_ requires Node 22+. React apps should use React 19+.
|
|
36
|
+
|
|
37
|
+
### Vite
|
|
38
|
+
|
|
39
|
+
Most projects should install the ready-to-use Vite plugin and the CLI:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -D @nkzw/vite-plugin-fbtee @nkzw/fbtee-cli
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Add the fbtee plugin before the React plugin:
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
import fbtee from '@nkzw/vite-plugin-fbtee';
|
|
49
|
+
import react from '@vitejs/plugin-react';
|
|
50
|
+
import { defineConfig } from 'vite';
|
|
51
|
+
|
|
52
|
+
export default defineConfig({
|
|
53
|
+
plugins: [
|
|
54
|
+
fbtee({
|
|
55
|
+
fbtCommon: {
|
|
56
|
+
Accept: 'Button label for accepting terms',
|
|
57
|
+
},
|
|
58
|
+
fbtEnumManifest: {},
|
|
59
|
+
}),
|
|
60
|
+
react(),
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Babel
|
|
66
|
+
|
|
67
|
+
Install the Babel preset and the CLI:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install -D @nkzw/babel-preset-fbtee @nkzw/fbtee-cli
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Add the preset to your Babel configuration:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
export default {
|
|
77
|
+
presets: ['@nkzw/babel-preset-fbtee'],
|
|
78
|
+
};
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### SWC
|
|
82
|
+
|
|
83
|
+
Install the SWC runtime compiler and the CLI:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install -D @nkzw/swc-plugin-fbtee @nkzw/fbtee-cli
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
For Next.js SWC plugins:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
import { createFbteePluginOptions } from '@nkzw/swc-plugin-fbtee/index.js';
|
|
93
|
+
|
|
94
|
+
export default {
|
|
95
|
+
experimental: {
|
|
96
|
+
swcPlugins: [
|
|
97
|
+
[
|
|
98
|
+
'@nkzw/swc-plugin-fbtee',
|
|
99
|
+
createFbteePluginOptions({
|
|
100
|
+
fbtCommon: {
|
|
101
|
+
Accept: 'Button label for accepting terms',
|
|
102
|
+
},
|
|
103
|
+
fbtEnumManifest: {},
|
|
104
|
+
}),
|
|
105
|
+
],
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Low-level Oxc Transform
|
|
112
|
+
|
|
113
|
+
For custom build-system integrations, install the native Oxc transform:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm install -D @nkzw/oxc-transform-fbtee @nkzw/fbtee-cli oxc-transform
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Apply the fbtee transform before the standard Oxc transform:
|
|
120
|
+
|
|
121
|
+
```js
|
|
122
|
+
import { transformSync as transformFbtee } from '@nkzw/oxc-transform-fbtee';
|
|
123
|
+
import { transformSync as transformOxc } from 'oxc-transform';
|
|
124
|
+
|
|
125
|
+
const fbteeResult = transformFbtee(filename, sourceText, {
|
|
126
|
+
fbtCommon: {
|
|
127
|
+
Accept: 'Button label for accepting terms',
|
|
128
|
+
},
|
|
129
|
+
fbtEnumManifest: {},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (fbteeResult.errors.length > 0) {
|
|
133
|
+
throw new Error(fbteeResult.errors.map(({ message }) => message).join('\n'));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const result = transformOxc(filename, fbteeResult.code, {
|
|
137
|
+
jsx: { runtime: 'automatic' },
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### TypeScript JSX Types
|
|
142
|
+
|
|
143
|
+
React TypeScript projects should include the JSX declarations once in a global type file or app entry point:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
/// <reference types="fbtee/ReactTypes.d.ts" />
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Writing Strings
|
|
150
|
+
|
|
151
|
+
Every user-facing string should be wrapped in `<fbt>`, `fbt()`, or `fbs()`. Descriptions are required because they are the translator's context.
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
<fbt desc="Empty state title when a project has no tasks">No tasks yet</fbt>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Use `fbt()` outside JSX:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { fbt } from 'fbtee';
|
|
161
|
+
|
|
162
|
+
const title = fbt(
|
|
163
|
+
'No tasks yet',
|
|
164
|
+
'Empty state title when a project has no tasks',
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Use `fbs()` when you need a plain string:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import { fbs } from 'fbtee';
|
|
172
|
+
|
|
173
|
+
<input
|
|
174
|
+
placeholder={fbs('Search projects', 'Project search input placeholder')}
|
|
175
|
+
/>;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Dynamic Content
|
|
179
|
+
|
|
180
|
+
Use `<fbt:param>` for dynamic values. Token names should describe the value, not its current English position.
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
<fbt desc="Greeting with the viewer name">
|
|
184
|
+
Hello, <fbt:param name="viewerName">{viewer.name}</fbt:param>!
|
|
185
|
+
</fbt>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
React elements inside `<fbt>` are automatically turned into implicit params:
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
<fbt desc="Greeting with a linked viewer name">
|
|
192
|
+
Hello, <UserLink user={viewer} />!
|
|
193
|
+
</fbt>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Use `fbt.sameParam()` or `<fbt:same-param>` when the same token appears more than once.
|
|
197
|
+
|
|
198
|
+
## Plurals
|
|
199
|
+
|
|
200
|
+
Use `<fbt:plural>` when a count controls grammar. _fbtee_ handles locale-specific plural rules.
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
<fbt desc="Inbox unread count">
|
|
204
|
+
You have{' '}
|
|
205
|
+
<fbt:plural
|
|
206
|
+
count={count}
|
|
207
|
+
many="unread messages"
|
|
208
|
+
name="unreadCount"
|
|
209
|
+
showCount="yes"
|
|
210
|
+
>
|
|
211
|
+
an unread message
|
|
212
|
+
</fbt:plural>
|
|
213
|
+
.
|
|
214
|
+
</fbt>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`showCount` can be `yes`, `ifMany`, or `no`.
|
|
218
|
+
|
|
219
|
+
## Lists
|
|
220
|
+
|
|
221
|
+
Use `<fbt:list>` for locale-aware lists:
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
<fbt desc="People assigned to a task">
|
|
225
|
+
Assigned to <fbt:list items={assignees} name="assigneeList" />.
|
|
226
|
+
</fbt>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The standalone `list()` helper is available for non-React code:
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import { list } from 'fbtee';
|
|
233
|
+
|
|
234
|
+
const names = list(['Alice', 'Bob', 'Charlie'], 'or', 'comma');
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Enums
|
|
238
|
+
|
|
239
|
+
Use enums when runtime values map to a fixed set of translatable labels:
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
const StatusLabels = {
|
|
243
|
+
done: 'done',
|
|
244
|
+
open: 'open',
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
<fbt desc="Task status label">
|
|
248
|
+
This task is <fbt:enum enum-range={StatusLabels} value={status} />.
|
|
249
|
+
</fbt>;
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
For shared enum modules, use the `$FbtEnum` suffix so the collector can resolve them.
|
|
253
|
+
|
|
254
|
+
## Pronouns and Gender
|
|
255
|
+
|
|
256
|
+
Use `<fbt:pronoun>` when a phrase depends on a person's gender:
|
|
257
|
+
|
|
258
|
+
```tsx
|
|
259
|
+
<fbt desc="Photo sharing notification">
|
|
260
|
+
<fbt:param name="name">{user.name}</fbt:param> shared{' '}
|
|
261
|
+
<fbt:pronoun gender={user.gender} human type="possessive" /> photo.
|
|
262
|
+
</fbt>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Supported pronoun types are `subject`, `object`, `possessive`, and `reflexive`.
|
|
266
|
+
|
|
267
|
+
## Common Strings
|
|
268
|
+
|
|
269
|
+
Common strings are reusable source strings with shared descriptions:
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
<fbt common>Save</fbt>
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Pass common strings to the compiler as `fbtCommon`:
|
|
276
|
+
|
|
277
|
+
```js
|
|
278
|
+
{
|
|
279
|
+
fbtCommon: {
|
|
280
|
+
Save: 'Button label for saving changes',
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Runtime Setup
|
|
286
|
+
|
|
287
|
+
Most React apps should use `createLocaleContext`:
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import { createLocaleContext } from 'fbtee';
|
|
291
|
+
|
|
292
|
+
const availableLanguages = new Map([
|
|
293
|
+
['en-US', 'English'],
|
|
294
|
+
['de-DE', 'Deutsch'],
|
|
295
|
+
['ja-JP', '日本語'],
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
const loadLocale = async (locale: string) => {
|
|
299
|
+
switch (locale) {
|
|
300
|
+
case 'de-DE':
|
|
301
|
+
return (await import('./translations/de-DE.json')).default['de-DE'];
|
|
302
|
+
case 'ja-JP':
|
|
303
|
+
return (await import('./translations/ja-JP.json')).default['ja-JP'];
|
|
304
|
+
default:
|
|
305
|
+
return {};
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const LocaleContext = createLocaleContext({
|
|
310
|
+
availableLanguages,
|
|
311
|
+
clientLocales: [navigator.language, ...navigator.languages],
|
|
312
|
+
loadLocale,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
export const Root = () => (
|
|
316
|
+
<LocaleContext>
|
|
317
|
+
<App />
|
|
318
|
+
</LocaleContext>
|
|
319
|
+
);
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Use `useLocaleContext` to read or change the locale:
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
import { useLocaleContext } from 'fbtee';
|
|
326
|
+
|
|
327
|
+
const LanguageButton = () => {
|
|
328
|
+
const { locale, setLocale } = useLocaleContext();
|
|
329
|
+
return <button onClick={() => setLocale('de-DE')}>{locale}</button>;
|
|
330
|
+
};
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
If you need full control, use `setupLocaleContext` or `setupFbtee` directly.
|
|
334
|
+
|
|
335
|
+
### Next.js App Router
|
|
336
|
+
|
|
337
|
+
For App Router, put the locale context in a Client Component. If you render translated strings in Server Components, call `setupFbtee` on the server and re-render the route when the locale changes. Client-only strings can switch locale fully on the client.
|
|
338
|
+
|
|
339
|
+
See the [Next.js fbtee example](https://github.com/cpojer/nextjs-fbtee-example) for a complete setup.
|
|
340
|
+
|
|
341
|
+
## Translation Workflow
|
|
342
|
+
|
|
343
|
+
Extract source strings:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
pnpm fbtee collect
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
This writes `source_strings.json`.
|
|
350
|
+
|
|
351
|
+
Prepare editable translation files:
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
pnpm fbtee prepare-translations --source-strings source_strings.json --output-dir translations --locales de-DE fr-FR ja-JP
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
`prepare-translations` merges source strings into existing locale files, preserves translated entries, and marks new work with `"status": "new"`. New files use BCP 47 locale identifiers by default, such as `de-DE.json` and `es-419.json`.
|
|
358
|
+
|
|
359
|
+
Compile translations for the app:
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
pnpm fbtee translate --source-strings source_strings.json --translations 'translations/*.json' --output-dir src/translations
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
fbtee accepts both modern BCP 47 locale identifiers (`de-DE`, `es-419`) and legacy Facebook-style identifiers (`de_DE`, `es_LA`). If both aliasing files exist, for example `translations/de_DE.json` and `translations/de-DE.json`, fbtee throws and asks you to keep one. Existing legacy files are updated in place; new generated files default to BCP 47. To force a specific output style, pass `--output-locale-style=bcp47`, `--output-locale-style=legacy`, or `--output-locale-style=preserve`.
|
|
366
|
+
|
|
367
|
+
To migrate editable translation files and generated runtime files to BCP 47 names:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
pnpm fbtee migrate-locales --to bcp47 --dir translations --dir src/translations
|
|
371
|
+
pnpm fbtee translate --output-locale-style=bcp47
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Use `--dry-run` first to preview file renames.
|
|
375
|
+
|
|
376
|
+
Commit the human-authored translation files. Ignore generated runtime output:
|
|
377
|
+
|
|
378
|
+
```gitignore
|
|
379
|
+
.enum_manifest.json
|
|
380
|
+
source_strings.json
|
|
381
|
+
src/translations/
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Translating Strings with Coding Agents
|
|
385
|
+
|
|
386
|
+
Coding agents are great at updating _fbtee_ translation files because all the context is in the repository:
|
|
387
|
+
|
|
388
|
+
1. `fbtee prepare-translations` adds every missing translation and marks it with `"status": "new"`.
|
|
389
|
+
1. The agent edits only entries with `"status": "new"`.
|
|
390
|
+
1. The agent removes `"status": "new"` after the translation is complete.
|
|
391
|
+
1. Code review shows a clean diff of the new localized strings.
|
|
392
|
+
|
|
393
|
+
This works best when the app already has translated strings. The agent can infer tone, voice, capitalization, punctuation, and product vocabulary from the existing locale files.
|
|
394
|
+
|
|
395
|
+
### Coding Agent prompt:
|
|
396
|
+
|
|
397
|
+
```md
|
|
398
|
+
Run `fbtee prepare-translations --source-strings source_strings.json --output-dir ares/translations --locales de-DE fr-FR ja-JP pl-PL ru-RU zh-CN es-ES it-IT ko-KR pt-BR uk-UA` for all the translations the app supports.
|
|
399
|
+
|
|
400
|
+
Look at all updated translation files. For every entry with `"status": "new"`, write a translation that matches the tone, voice, and language already used in the app and in the current locale.
|
|
401
|
+
|
|
402
|
+
Remove `"status": "new"` from each completed translation entry.
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## ESLint
|
|
406
|
+
|
|
407
|
+
Install the optional ESLint plugin:
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
npm install -D @nkzw/eslint-plugin-fbtee
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Use the recommended config:
|
|
414
|
+
|
|
415
|
+
```js
|
|
416
|
+
import fbtee from '@nkzw/eslint-plugin-fbtee';
|
|
417
|
+
|
|
418
|
+
export default [fbtee.configs.recommended];
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Use the strict config if you want every user-facing string to be wrapped:
|
|
422
|
+
|
|
423
|
+
```js
|
|
424
|
+
import fbtee from '@nkzw/eslint-plugin-fbtee';
|
|
425
|
+
|
|
426
|
+
export default [fbtee.configs.strict];
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Migration from fbt
|
|
430
|
+
|
|
431
|
+
_fbtee_ is compatible with the core `fbt` programming model:
|
|
432
|
+
|
|
433
|
+
1. Replace `fbt` packages with `fbtee` and the matching compiler package.
|
|
434
|
+
1. Replace imports from `fbt` with imports from `fbtee`.
|
|
435
|
+
1. Use `fbtee collect`, `fbtee prepare-translations`, and `fbtee translate`.
|
|
436
|
+
1. Convert CommonJS common strings or enum modules to ESM when needed.
|
|
437
|
+
1. Replace legacy setup calls with `setupFbtee`, `createLocaleContext`, or `setupLocaleContext`.
|
|
438
|
+
|
|
439
|
+
Some archived `fbt` options and legacy behaviors were intentionally removed. The compiler errors should point to the modern replacement when one exists.
|
|
440
|
+
|
|
441
|
+
## Examples
|
|
442
|
+
|
|
443
|
+
- [Example App](https://github.com/nkzw-tech/fbtee/tree/main/example)
|
|
444
|
+
- [Next.js App Router Example](https://github.com/cpojer/nextjs-fbtee-example)
|
|
445
|
+
- [Athena Crisis](https://github.com/nkzw-tech/athena-crisis)
|
|
446
|
+
|
|
447
|
+
## Credits
|
|
448
|
+
|
|
449
|
+
- `fbt` was originally created by [Facebook](https://github.com/facebook/fbt).
|
|
450
|
+
- The auto-import plugin was created by [@alexandernanberg](https://github.com/alexandernanberg).
|
|
451
|
+
- [Nakazawa Tech](https://nkzw.tech) rewrote `fbt` into _fbtee_ and maintains this project.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TransformOptions } from '@nkzw/oxc-transform-fbtee';
|
|
2
|
+
import type { Plugin } from 'vite';
|
|
3
|
+
|
|
4
|
+
export type FbteeVitePluginOptions = Omit<
|
|
5
|
+
TransformOptions,
|
|
6
|
+
'lang' | 'sourcemap' | 'sourceType'
|
|
7
|
+
>;
|
|
8
|
+
|
|
9
|
+
export declare const fbtee: (options?: FbteeVitePluginOptions) => Plugin;
|
|
10
|
+
|
|
11
|
+
export default fbtee;
|
package/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { transformSync } from '@nkzw/oxc-transform-fbtee';
|
|
2
|
+
|
|
3
|
+
const fbteeSourcePattern = /fbt|fbs/;
|
|
4
|
+
|
|
5
|
+
const getLanguage = (id) => {
|
|
6
|
+
const filename = id.split(/[#?]/, 1)[0];
|
|
7
|
+
if (filename.endsWith('.tsx')) {
|
|
8
|
+
return 'tsx';
|
|
9
|
+
}
|
|
10
|
+
if (filename.endsWith('.jsx')) {
|
|
11
|
+
return 'jsx';
|
|
12
|
+
}
|
|
13
|
+
if (filename.endsWith('.ts')) {
|
|
14
|
+
return 'ts';
|
|
15
|
+
}
|
|
16
|
+
return 'js';
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const fbtee = (options = {}) => ({
|
|
20
|
+
enforce: 'pre',
|
|
21
|
+
name: 'vite-plugin-fbtee',
|
|
22
|
+
transform: {
|
|
23
|
+
filter: {
|
|
24
|
+
code: fbteeSourcePattern,
|
|
25
|
+
id: {
|
|
26
|
+
exclude: '**/node_modules/**',
|
|
27
|
+
include: '**/*.{js,jsx,ts,tsx}',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
handler(source, id) {
|
|
31
|
+
// Vite and Rolldown apply the hook filter before invoking this handler.
|
|
32
|
+
// Keep the string check as a cheap fallback for compatible hosts that do
|
|
33
|
+
// not yet support hook filters. False positives, including comments, are
|
|
34
|
+
// harmless; every supported fbtee construct contains `fbt` or `fbs`.
|
|
35
|
+
if (!source.includes('fbt') && !source.includes('fbs')) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const result = transformSync(id, source, {
|
|
40
|
+
...options,
|
|
41
|
+
lang: getLanguage(id),
|
|
42
|
+
sourcemap: true,
|
|
43
|
+
sourceType: 'module',
|
|
44
|
+
});
|
|
45
|
+
if (result.errors.length > 0) {
|
|
46
|
+
throw new Error(result.errors.map(({ message }) => message).join('\n'));
|
|
47
|
+
}
|
|
48
|
+
return { code: result.code, map: result.map };
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export default fbtee;
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nkzw/vite-plugin-fbtee",
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "Vite plugin for fbtee powered by the native Oxc transform.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fbt",
|
|
7
|
+
"fbtee",
|
|
8
|
+
"i18n",
|
|
9
|
+
"oxc",
|
|
10
|
+
"vite"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/nkzw-tech/fbtee.git",
|
|
15
|
+
"directory": "packages/vite-plugin-fbtee"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./index.d.ts",
|
|
23
|
+
"import": "./index.js",
|
|
24
|
+
"default": "./index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"main": "index.js",
|
|
28
|
+
"types": "index.d.ts",
|
|
29
|
+
"files": [
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"README.md",
|
|
32
|
+
"index.d.ts",
|
|
33
|
+
"index.js"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@nkzw/oxc-transform-fbtee": "3.1.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"vite": "^8.1.5"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"vite": ">=6.3.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=22.12.0"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"test": "node test/e2e.mjs"
|
|
49
|
+
}
|
|
50
|
+
}
|