Skip to content

Commit 263ee59

Browse files
authored
feat: configurable and extendable extractors with Lingui config (#1065)
1 parent ff7a235 commit 263ee59

8 files changed

Lines changed: 146 additions & 24 deletions

File tree

docs/ref/conf.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Default config:
2929
"fallbackLocales": {},
3030
"format": "po",
3131
"locales": [],
32+
"extractors": ["babel"],
3233
"orderBy": "messageId",
3334
"pseudoLocale": "",
3435
"rootDir": ".",
@@ -506,3 +507,26 @@ providing custom translation.
506507
The difference between :conf:`fallbackLocales` and :conf:`sourceLocale` is that
507508
:conf:`fallbackLocales` is used in translation, while :conf:`sourceLocale` is
508509
used for the message ID.
510+
511+
extractors
512+
------------
513+
514+
Default: ``[babel]``
515+
516+
Extractors it's the way to customize which extractor you want for your codebase, a long time ago Babel wasn't ready yet to work with Typescript,
517+
so we added two extractors as default ``[babel, typescript]``, but right now Babel already works good with Typescript so isn't a requirement anymore to compile two times the same code.
518+
519+
Anyway, if you want to use the typescript extractor in conjuntion with babel you can do:
520+
521+
.. code-block:: js
522+
523+
{
524+
"extractors": [
525+
require.resolve("@lingui/cli/api/extractors/babel"),
526+
require.resolve("@lingui/cli/api/extractors/typescript"),
527+
]
528+
}
529+
530+
Of course you can build your own extractor, take a look to babel and typescript extractors to see how you should do it, but basically exports two methods:
531+
- match: regex to a filename extension, should return true|false
532+
- extract: is the responsible of transforming the code and using @lingui/babel-plugin-extract-messages

packages/cli/src/api/catalog.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ export class Catalog {
155155
extract(filename, tmpDir, {
156156
verbose: options.verbose,
157157
babelOptions: this.config.extractBabelOptions,
158+
// @ts-ignore
159+
extractors: options.extractors,
158160
projectType: options.projectType,
159161
})
160162
)

packages/cli/src/api/extract.test.ts

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,20 @@ describe("extract", function () {
5353
})
5454
})
5555

56+
afterEach(() => {
57+
jest.clearAllMocks()
58+
})
5659
afterAll(() => {
5760
mockFs.restore()
5861
})
5962

6063
it("should traverse directory and call extractors", function () {
6164
extract(["src"], "locale", {
6265
ignore: ["forbidden"],
66+
extractors: [
67+
babel,
68+
typescript
69+
],
6370
babelOptions: {},
6471
})
6572

@@ -92,8 +99,8 @@ describe("extract", function () {
9299
)
93100

94101
const extractArgs = [
95-
"locale",
96-
{ babelOptions: {}, ignore: ["forbidden"]}
102+
"locale",
103+
{ extractors: [babel, typescript], babelOptions: {}, ignore: ["forbidden"]}
97104
]
98105
expect(babel.extract).toHaveBeenCalledWith(
99106
path.join("src", "components", "Babel.js"),
@@ -145,6 +152,95 @@ describe("extract", function () {
145152
...extractArgs
146153
)
147154
})
155+
156+
it("by default the traverse directory only uses babel", function () {
157+
extract(["src"], "locale", {
158+
ignore: ["forbidden"],
159+
babelOptions: {},
160+
})
161+
162+
expect(typescript.match).not.toHaveBeenCalledWith(
163+
path.join("src", "components", "Typescript.ts")
164+
)
165+
expect(babel.match).toHaveBeenCalledWith(
166+
path.join("src", "components", "Babel.js")
167+
)
168+
expect(babel.match).toHaveBeenCalledWith(
169+
path.join("src", "components", "Babel.jsx")
170+
)
171+
expect(babel.match).toHaveBeenCalledWith(
172+
path.join("src", "components", "Babel.es6")
173+
)
174+
expect(babel.match).toHaveBeenCalledWith(
175+
path.join("src", "components", "Babel.es")
176+
)
177+
expect(babel.match).toHaveBeenCalledWith(
178+
path.join("src", "components", "Babel.mjs")
179+
)
180+
181+
expect(babel.match).toHaveBeenCalledWith(
182+
path.join("src", "index.html")
183+
)
184+
185+
// This file is ignored
186+
expect(babel.extract).not.toHaveBeenCalledWith(
187+
path.join("src", "index.html")
188+
)
189+
190+
const extractArgs = [
191+
"locale",
192+
{ babelOptions: {}, ignore: ["forbidden"]}
193+
]
194+
expect(babel.extract).toHaveBeenCalledWith(
195+
path.join("src", "components", "Babel.js"),
196+
...extractArgs
197+
)
198+
expect(babel.extract).toHaveBeenCalledWith(
199+
path.join("src", "components", "Babel.jsx"),
200+
...extractArgs
201+
)
202+
expect(babel.extract).toHaveBeenCalledWith(
203+
path.join("src", "components", "Babel.es6"),
204+
...extractArgs
205+
)
206+
expect(babel.extract).toHaveBeenCalledWith(
207+
path.join("src", "components", "Babel.es"),
208+
...extractArgs
209+
)
210+
expect(babel.extract).toHaveBeenCalledWith(
211+
path.join("src", "components", "Babel.mjs"),
212+
...extractArgs
213+
)
214+
expect(babel.extract).not.toHaveBeenCalledWith(
215+
path.join("src", "components", "Typescript.ts"),
216+
...extractArgs
217+
)
218+
219+
expect(typescript.extract).not.toHaveBeenCalledWith(
220+
path.join("src", "components", "Babel.js"),
221+
...extractArgs
222+
)
223+
expect(typescript.extract).not.toHaveBeenCalledWith(
224+
path.join("src", "components", "Babel.jsx"),
225+
...extractArgs
226+
)
227+
expect(typescript.extract).not.toHaveBeenCalledWith(
228+
path.join("src", "components", "Babel.es6"),
229+
...extractArgs
230+
)
231+
expect(typescript.extract).not.toHaveBeenCalledWith(
232+
path.join("src", "components", "Babel.es"),
233+
...extractArgs
234+
)
235+
expect(typescript.extract).not.toHaveBeenCalledWith(
236+
path.join("src", "components", "Babel.mjs"),
237+
...extractArgs
238+
)
239+
expect(typescript.extract).not.toHaveBeenCalledWith(
240+
path.join("src", "components", "Typescript.ts"),
241+
...extractArgs
242+
)
243+
})
148244
})
149245

150246
describe("collect", function () {

packages/cli/src/api/extract.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ import * as R from "ramda"
66

77
import { prettyOrigin } from "./utils"
88

9-
import * as extractors from "./extractors"
10-
import { ExtractorType } from "./extractors"
11-
9+
import cliExtractor, { ExtractorType } from "./extractors"
1210

1311
type ExtractOptions = {
1412
ignore?: Array<string>
1513
verbose?: boolean
14+
extractors?: ExtractorType[]
1615
projectType?: string
1716
babelOptions?: Object
1817
}
@@ -44,7 +43,7 @@ export function extract(
4443
targetPath: string,
4544
options: ExtractOptions = {}
4645
) {
47-
const { ignore = [], verbose = false } = options
46+
const { ignore = [] } = options
4847
const ignorePattern = ignore.length ? new RegExp(ignore.join("|"), "i") : null
4948

5049
srcPaths.forEach((srcFilename) => {
@@ -64,17 +63,7 @@ export function extract(
6463
return
6564
}
6665

67-
R.values(extractors).some((ext: ExtractorType) => {
68-
if (!ext.match || !ext.match(srcFilename)) return false
69-
70-
let spinner
71-
if (verbose) spinner = ora().start(srcFilename)
72-
73-
ext.extract(srcFilename, targetPath, options)
74-
if (verbose && spinner) spinner.succeed()
75-
76-
return true
77-
})
66+
cliExtractor(srcFilename, targetPath, options)
7867
})
7968
}
8069

packages/cli/src/api/extractors/index.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import ora from "ora"
22
import babel from "./babel"
3-
import typescript from "./typescript"
4-
import * as R from "ramda"
5-
6-
const extractors = { babel, typescript }
73

4+
const DEFAULT_EXTRACTORS: ExtractorType[] = [babel]
85

96
export type BabelOptions = {
107
plugins?: Array<string>
@@ -14,6 +11,7 @@ export type BabelOptions = {
1411
export type ExtractOptions = {
1512
verbose?: boolean
1613
projectType?: string
14+
extractors?: ExtractorType[]
1715
babelOptions?: BabelOptions
1816
}
1917

@@ -27,7 +25,9 @@ export default function extract(
2725
targetPath: string,
2826
options: ExtractOptions
2927
): boolean {
30-
return R.values(extractors).some((ext: ExtractorType) => {
28+
const extractorsToExtract = options.extractors ?? DEFAULT_EXTRACTORS
29+
30+
return extractorsToExtract.some((ext) => {
3131
if (!ext.match(filename)) return false
3232

3333
let spinner
@@ -48,5 +48,3 @@ export default function extract(
4848
return true
4949
})
5050
}
51-
52-
export { babel, typescript }

packages/cli/src/lingui-extract.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export default function command(
4242
catalog.make({
4343
...options,
4444
orderBy: config.orderBy,
45+
extractors: config.extractors,
4546
projectType: detect(),
4647
})
4748

packages/conf/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@ export type DefaultLocaleObject = {
2323

2424
export declare type FallbackLocales = LocaleObject | DefaultLocaleObject
2525

26+
declare type ExtractorType = {
27+
match(filename: string): boolean;
28+
extract(filename: string, targetDir: string, options?: any): void;
29+
}
30+
2631
export declare type LinguiConfig = {
2732
catalogs: CatalogConfig[];
2833
compileNamespace: "es" | "cjs" | "ts" | string;
2934
extractBabelOptions: Record<string, unknown>;
3035
compilerBabelOptions: GeneratorOptions;
3136
fallbackLocales: FallbackLocales;
3237
format: CatalogFormat;
38+
extractors?: ExtractorType[];
3339
prevFormat: CatalogFormat;
3440
formatOptions: CatalogFormatOptions;
3541
localeDir: string;

packages/conf/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,18 @@ export type FallbackLocales = LocaleObject | DefaultLocaleObject | false
3232

3333
type ModuleSource = [string, string?]
3434

35+
type ExtractorType = {
36+
match(filename: string): boolean
37+
extract(filename: string, targetDir: string, options?: any): void
38+
}
39+
3540
export type LinguiConfig = {
3641
catalogs: CatalogConfig[]
3742
compileNamespace: "es" | "ts" | "cjs" | string
3843
extractBabelOptions: Record<string, unknown>
3944
compilerBabelOptions: GeneratorOptions
4045
fallbackLocales?: FallbackLocales
46+
extractors?: ExtractorType[]
4147
format: CatalogFormat
4248
formatOptions: CatalogFormatOptions
4349
locales: string[]

0 commit comments

Comments
 (0)