Skip to content

feat(babel-plugin-lingui-macro): allow to configure macro packages #2196

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

Merged
merged 9 commits into from
Mar 21, 2025
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
3 changes: 0 additions & 3 deletions packages/babel-plugin-lingui-macro/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
export const EXTRACT_MARK = "i18n"
export const MACRO_LEGACY_PACKAGE = "@lingui/macro"
export const MACRO_CORE_PACKAGE = "@lingui/core/macro"
export const MACRO_REACT_PACKAGE = "@lingui/react/macro"

export enum MsgDescriptorPropKey {
id = "id",
Expand Down
75 changes: 44 additions & 31 deletions packages/babel-plugin-lingui-macro/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ import { Program, Identifier } from "@babel/types"
import { MacroJSX } from "./macroJsx"
import type { NodePath } from "@babel/traverse"
import { MacroJs } from "./macroJs"
import {
MACRO_CORE_PACKAGE,
MACRO_REACT_PACKAGE,
MACRO_LEGACY_PACKAGE,
JsMacroName,
} from "./constants"
import { JsMacroName } from "./constants"
import {
type LinguiConfigNormalized,
getConfig as loadConfig,
Expand All @@ -18,9 +13,24 @@ import {
let config: LinguiConfigNormalized

export type LinguiPluginOpts = {
// explicitly set by CLI when running extraction process
/*
* When set `true` all auxiliary data such as `comment`, `context`,
* and default message would be kept regardless of the current environment
*
* This flag explicitly set by Lingui CLI when running extraction process
*/
extract?: boolean
/**
* Setting `stripMessageField` to `true` will strip messages and comments from both development and production bundles.
* Alternatively, set it to `false` to keep the original messages in both environments.
*
* If not set value would be determined based on `process.env.NODE_ENV === "production"`
*/
stripMessageField?: boolean

/**
* Resolved and normalized Lingui Configuration
*/
linguiConfig?: LinguiConfigNormalized
}

Expand Down Expand Up @@ -61,14 +71,17 @@ type LinguiSymbol = "Trans" | "useLingui" | "i18n"
const getIdentifierPath = ((path: NodePath, node: Identifier) => {
let foundPath: NodePath

path.traverse({
Identifier: (path) => {
if (path.node === node) {
foundPath = path
path.stop()
}
path.traverse(
{
Identifier: (path) => {
if (path.node === node) {
foundPath = path
path.stop()
}
},
},
})
path.state
)

return foundPath
}) as any
Expand Down Expand Up @@ -110,14 +123,15 @@ export default function ({
}

function getMacroImports(path: NodePath<Program>) {
const linguiPackages = new Set([
...config.macro.corePackage,
...config.macro.jsxPackage,
])

return path.get("body").filter((statement) => {
return (
statement.isImportDeclaration() &&
[
MACRO_CORE_PACKAGE,
MACRO_REACT_PACKAGE,
MACRO_LEGACY_PACKAGE,
].includes(statement.get("source").node.value)
linguiPackages.has(statement.get("source").node.value)
)
})
}
Expand All @@ -138,11 +152,9 @@ export default function ({

if (macro === JsMacroName.useLingui) {
if (
identPath.referencesImport(
MACRO_REACT_PACKAGE,
JsMacroName.useLingui
) ||
identPath.referencesImport(MACRO_LEGACY_PACKAGE, JsMacroName.useLingui)
config.macro.jsxPackage.some((moduleSource) =>
identPath.referencesImport(moduleSource, JsMacroName.useLingui)
)
) {
return true
}
Expand All @@ -151,8 +163,9 @@ export default function ({
identPath = identPath || getIdentifierPath(path.getFunctionParent(), node)

if (
identPath.referencesImport(MACRO_CORE_PACKAGE, macro) ||
identPath.referencesImport(MACRO_LEGACY_PACKAGE, macro)
config.macro.corePackage.some((moduleSource) =>
identPath.referencesImport(moduleSource, macro)
)
) {
return true
}
Expand All @@ -164,6 +177,11 @@ export default function ({
visitor: {
Program: {
enter(path, state) {
state.set(
"linguiConfig",
getConfig((state.opts as LinguiPluginOpts).linguiConfig)
)

const macroImports = getMacroImports(path)

if (!macroImports.length) {
Expand All @@ -172,11 +190,6 @@ export default function ({

state.set("macroImport", macroImports[0])

state.set(
"linguiConfig",
getConfig((state.opts as LinguiPluginOpts).linguiConfig)
)

state.set("linguiIdentifiers", {
i18n: path.scope.generateUidIdentifier("i18n"),
Trans: path.scope.generateUidIdentifier("Trans"),
Expand Down
6 changes: 5 additions & 1 deletion packages/babel-plugin-lingui-macro/src/macroJsx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { MacroJSX } from "./macroJsx"
import { transformSync } from "@babel/core"
import type { NodePath } from "@babel/traverse"
import { JsxMacroName } from "./constants"
import { makeConfig } from "@lingui/conf"

const parseExpression = (expression: string) => {
let path: NodePath<JSXElement>
Expand All @@ -18,8 +19,11 @@ const parseExpression = (expression: string) => {
"@babel/plugin-syntax-jsx",
{
visitor: {
JSXElement: (d) => {
JSXElement: (d, state) => {
state.set("linguiConfig", makeConfig({}, { skipValidation: true }))

path = d
path.context.state = state
d.stop()
},
},
Expand Down
35 changes: 18 additions & 17 deletions packages/babel-plugin-lingui-macro/src/macroJsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,16 @@ import type { NodePath } from "@babel/traverse"

import { ArgToken, ElementToken, TextToken, Token } from "./icu"
import { makeCounter } from "./utils"
import {
JsxMacroName,
MACRO_REACT_PACKAGE,
MACRO_LEGACY_PACKAGE,
MsgDescriptorPropKey,
JsMacroName,
} from "./constants"
import { JsxMacroName, MsgDescriptorPropKey, JsMacroName } from "./constants"
import cleanJSXElementLiteralChild from "./utils/cleanJSXElementLiteralChild"
import { createMessageDescriptorFromTokens } from "./messageDescriptorUtils"
import {
createMacroJsContext,
MacroJsContext,
tokenizeExpression,
} from "./macroJsAst"
import { LinguiConfigNormalized } from "@lingui/conf"
import { PluginPass } from "@babel/core"

const pluralRuleRe = /(_[\d\w]+|zero|one|two|few|many|other)/
const jsx2icuExactChoice = (value: string) =>
Expand Down Expand Up @@ -387,14 +383,17 @@ export class MacroJSX {
tokenizeConditionalExpression = (
exp: NodePath<ConditionalExpression>
): ArgToken => {
exp.traverse({
JSXElement: (el) => {
if (this.isTransComponent(el) || this.isChoiceComponent(el)) {
this.replacePath(el)
el.skip()
}
exp.traverse(
{
JSXElement: (el) => {
if (this.isTransComponent(el) || this.isChoiceComponent(el)) {
this.replacePath(el)
el.skip()
}
},
},
})
exp.state
)

return this.tokenizeExpression(exp)
}
Expand All @@ -414,11 +413,13 @@ export class MacroJSX {
return false
}

const config = (path.context.state as PluginPass).get(
"linguiConfig"
) as LinguiConfigNormalized
const identifier = path.get("openingElement").get("name")

return (
identifier.referencesImport(MACRO_REACT_PACKAGE, name) ||
identifier.referencesImport(MACRO_LEGACY_PACKAGE, name)
return config.macro.jsxPackage.some((moduleSource) =>
identifier.referencesImport(moduleSource, name)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,23 @@ _i18n._(

`;

exports[`should detects macro imported from config.macro.corePackage 1`] = `
import { t } from "@my-lingui/macro";
const msg = t\`Message\`;

↓ ↓ ↓ ↓ ↓ ↓

import { i18n as _i18n } from "@lingui/core";
const msg = _i18n._(
/*i18n*/
{
id: "xDAtGP",
message: "Message",
}
);

`;

exports[`should not crash when no params passed 1`] = `
import { t } from "@lingui/core/macro";
const msg = t();
Expand All @@ -675,6 +692,23 @@ const msg = _i18n._();

`;

exports[`should respect runtimeConfigModule 1`] = `
import { t } from "@lingui/macro";
const msg = t\`Message\`;

↓ ↓ ↓ ↓ ↓ ↓

import { myI18n as _i18n } from "@my/lingui";
const msg = _i18n._(
/*i18n*/
{
id: "xDAtGP",
message: "Message",
}
);

`;

exports[`stripMessageField option - message prop is removed if stripMessageField: true 1`] = `
import { t } from "@lingui/macro";
const msg = t\`Message\`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,44 @@ import { Trans as _Trans } from "@lingui/react";

`;

exports[`should detects macro imported from config.macro.jsxPackage 1`] = `
import { Trans } from "@my-lingui/macro";
<Trans>Hello World</Trans>;

↓ ↓ ↓ ↓ ↓ ↓

import { Trans as _Trans } from "@lingui/react";
<_Trans
{
/*i18n*/
...{
id: "mY42CM",
message: "Hello World",
}
}
/>;

`;

exports[`should respect runtimeConfigModule 1`] = `
import { Trans } from "@lingui/react/macro";
<Trans>Hello World</Trans>;

↓ ↓ ↓ ↓ ↓ ↓

import { myTrans as _Trans } from "@my/lingui";
<_Trans
{
/*i18n*/
...{
id: "mY42CM",
message: "Hello World",
}
}
/>;

`;

exports[`stripMessageField option - message prop is removed if stripMessageField: true 1`] = `
import { Trans } from "@lingui/macro";
<Trans id="msg.hello">Hello World</Trans>;
Expand Down
36 changes: 36 additions & 0 deletions packages/babel-plugin-lingui-macro/test/js-t.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { macroTester } from "./macroTester"
import { makeConfig } from "@lingui/conf"

describe.skip("", () => {})

Expand Down Expand Up @@ -310,5 +311,40 @@ macroTester({
t2\`Ola! \${plural1(count, {one: "1 user", many: "# users"})} Ola!\`
`,
},
{
name: "should respect runtimeConfigModule",
macroOpts: {
linguiConfig: makeConfig(
{
runtimeConfigModule: {
i18n: ["@my/lingui", "myI18n"],
},
},
{ skipValidation: true }
),
},
code: `
import { t } from '@lingui/macro'
const msg = t\`Message\`
`,
},
{
name: "should detects macro imported from config.macro.corePackage",
macroOpts: {
linguiConfig: makeConfig(
{
macro: {
corePackage: ["@my-lingui/macro"],
},
},
{ skipValidation: true }
),
},
skipBabelMacroTest: true,
code: `
import { t } from '@my-lingui/macro'
const msg = t\`Message\`
`,
},
],
})
Loading