Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: configurable and extendable extractors with Lingui config #1065

Merged
merged 2 commits into from
May 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/ref/conf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Default config:
"fallbackLocales": {},
"format": "po",
"locales": [],
"extractors": ["babel"],
"orderBy": "messageId",
"pseudoLocale": "",
"rootDir": ".",
Expand Down Expand Up @@ -506,3 +507,26 @@ providing custom translation.
The difference between :conf:`fallbackLocales` and :conf:`sourceLocale` is that
:conf:`fallbackLocales` is used in translation, while :conf:`sourceLocale` is
used for the message ID.

extractors
------------

Default: ``[babel]``

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,
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.

Anyway, if you want to use the typescript extractor in conjuntion with babel you can do:

.. code-block:: js

{
"extractors": [
require.resolve("@lingui/cli/api/extractors/babel"),
require.resolve("@lingui/cli/api/extractors/typescript"),
]
}

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:
- match: regex to a filename extension, should return true|false
- extract: is the responsible of transforming the code and using @lingui/babel-plugin-extract-messages
2 changes: 2 additions & 0 deletions packages/cli/src/api/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ export class Catalog {
extract(filename, tmpDir, {
verbose: options.verbose,
babelOptions: this.config.extractBabelOptions,
// @ts-ignore
extractors: options.extractors,
projectType: options.projectType,
})
)
Expand Down
100 changes: 98 additions & 2 deletions packages/cli/src/api/extract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,20 @@ describe("extract", function () {
})
})

afterEach(() => {
jest.clearAllMocks()
})
afterAll(() => {
mockFs.restore()
})

it("should traverse directory and call extractors", function () {
extract(["src"], "locale", {
ignore: ["forbidden"],
extractors: [
babel,
typescript
],
babelOptions: {},
})

Expand Down Expand Up @@ -92,8 +99,8 @@ describe("extract", function () {
)

const extractArgs = [
"locale",
{ babelOptions: {}, ignore: ["forbidden"]}
"locale",
{ extractors: [babel, typescript], babelOptions: {}, ignore: ["forbidden"]}
]
expect(babel.extract).toHaveBeenCalledWith(
path.join("src", "components", "Babel.js"),
Expand Down Expand Up @@ -145,6 +152,95 @@ describe("extract", function () {
...extractArgs
)
})

it("by default the traverse directory only uses babel", function () {
extract(["src"], "locale", {
ignore: ["forbidden"],
babelOptions: {},
})

expect(typescript.match).not.toHaveBeenCalledWith(
path.join("src", "components", "Typescript.ts")
)
expect(babel.match).toHaveBeenCalledWith(
path.join("src", "components", "Babel.js")
)
expect(babel.match).toHaveBeenCalledWith(
path.join("src", "components", "Babel.jsx")
)
expect(babel.match).toHaveBeenCalledWith(
path.join("src", "components", "Babel.es6")
)
expect(babel.match).toHaveBeenCalledWith(
path.join("src", "components", "Babel.es")
)
expect(babel.match).toHaveBeenCalledWith(
path.join("src", "components", "Babel.mjs")
)

expect(babel.match).toHaveBeenCalledWith(
path.join("src", "index.html")
)

// This file is ignored
expect(babel.extract).not.toHaveBeenCalledWith(
path.join("src", "index.html")
)

const extractArgs = [
"locale",
{ babelOptions: {}, ignore: ["forbidden"]}
]
expect(babel.extract).toHaveBeenCalledWith(
path.join("src", "components", "Babel.js"),
...extractArgs
)
expect(babel.extract).toHaveBeenCalledWith(
path.join("src", "components", "Babel.jsx"),
...extractArgs
)
expect(babel.extract).toHaveBeenCalledWith(
path.join("src", "components", "Babel.es6"),
...extractArgs
)
expect(babel.extract).toHaveBeenCalledWith(
path.join("src", "components", "Babel.es"),
...extractArgs
)
expect(babel.extract).toHaveBeenCalledWith(
path.join("src", "components", "Babel.mjs"),
...extractArgs
)
expect(babel.extract).not.toHaveBeenCalledWith(
path.join("src", "components", "Typescript.ts"),
...extractArgs
)

expect(typescript.extract).not.toHaveBeenCalledWith(
path.join("src", "components", "Babel.js"),
...extractArgs
)
expect(typescript.extract).not.toHaveBeenCalledWith(
path.join("src", "components", "Babel.jsx"),
...extractArgs
)
expect(typescript.extract).not.toHaveBeenCalledWith(
path.join("src", "components", "Babel.es6"),
...extractArgs
)
expect(typescript.extract).not.toHaveBeenCalledWith(
path.join("src", "components", "Babel.es"),
...extractArgs
)
expect(typescript.extract).not.toHaveBeenCalledWith(
path.join("src", "components", "Babel.mjs"),
...extractArgs
)
expect(typescript.extract).not.toHaveBeenCalledWith(
path.join("src", "components", "Typescript.ts"),
...extractArgs
)
})
})

describe("collect", function () {
Expand Down
19 changes: 4 additions & 15 deletions packages/cli/src/api/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import * as R from "ramda"

import { prettyOrigin } from "./utils"

import * as extractors from "./extractors"
import { ExtractorType } from "./extractors"

import cliExtractor, { ExtractorType } from "./extractors"

type ExtractOptions = {
ignore?: Array<string>
verbose?: boolean
extractors?: ExtractorType[]
projectType?: string
babelOptions?: Object
}
Expand Down Expand Up @@ -44,7 +43,7 @@ export function extract(
targetPath: string,
options: ExtractOptions = {}
) {
const { ignore = [], verbose = false } = options
const { ignore = [] } = options
const ignorePattern = ignore.length ? new RegExp(ignore.join("|"), "i") : null

srcPaths.forEach((srcFilename) => {
Expand All @@ -64,17 +63,7 @@ export function extract(
return
}

R.values(extractors).some((ext: ExtractorType) => {
if (!ext.match || !ext.match(srcFilename)) return false

let spinner
if (verbose) spinner = ora().start(srcFilename)

ext.extract(srcFilename, targetPath, options)
if (verbose && spinner) spinner.succeed()

return true
})
cliExtractor(srcFilename, targetPath, options)
})
}

Expand Down
12 changes: 5 additions & 7 deletions packages/cli/src/api/extractors/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import ora from "ora"
import babel from "./babel"
import typescript from "./typescript"
import * as R from "ramda"

const extractors = { babel, typescript }

const DEFAULT_EXTRACTORS: ExtractorType[] = [babel]

export type BabelOptions = {
plugins?: Array<string>
Expand All @@ -14,6 +11,7 @@ export type BabelOptions = {
export type ExtractOptions = {
verbose?: boolean
projectType?: string
extractors?: ExtractorType[]
babelOptions?: BabelOptions
}

Expand All @@ -27,7 +25,9 @@ export default function extract(
targetPath: string,
options: ExtractOptions
): boolean {
return R.values(extractors).some((ext: ExtractorType) => {
const extractorsToExtract = options.extractors ?? DEFAULT_EXTRACTORS

return extractorsToExtract.some((ext) => {
if (!ext.match(filename)) return false

let spinner
Expand All @@ -48,5 +48,3 @@ export default function extract(
return true
})
}

export { babel, typescript }
1 change: 1 addition & 0 deletions packages/cli/src/lingui-extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default function command(
catalog.make({
...options,
orderBy: config.orderBy,
extractors: config.extractors,
projectType: detect(),
})

Expand Down
6 changes: 6 additions & 0 deletions packages/conf/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@ export type DefaultLocaleObject = {

export declare type FallbackLocales = LocaleObject | DefaultLocaleObject

declare type ExtractorType = {
match(filename: string): boolean;
extract(filename: string, targetDir: string, options?: any): void;
}

export declare type LinguiConfig = {
catalogs: CatalogConfig[];
compileNamespace: "es" | "cjs" | "ts" | string;
extractBabelOptions: Record<string, unknown>;
compilerBabelOptions: GeneratorOptions;
fallbackLocales: FallbackLocales;
format: CatalogFormat;
extractors?: ExtractorType[];
prevFormat: CatalogFormat;
formatOptions: CatalogFormatOptions;
localeDir: string;
Expand Down
6 changes: 6 additions & 0 deletions packages/conf/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@ export type FallbackLocales = LocaleObject | DefaultLocaleObject | false

type ModuleSource = [string, string?]

type ExtractorType = {
match(filename: string): boolean
extract(filename: string, targetDir: string, options?: any): void
}

export type LinguiConfig = {
catalogs: CatalogConfig[]
compileNamespace: "es" | "ts" | "cjs" | string
extractBabelOptions: Record<string, unknown>
compilerBabelOptions: GeneratorOptions
fallbackLocales?: FallbackLocales
extractors?: ExtractorType[]
format: CatalogFormat
formatOptions: CatalogFormatOptions
locales: string[]
Expand Down