From c73ba8fedcdef033bd2772c969433837bfd99b0e Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Mon, 10 Mar 2025 12:53:21 +0100 Subject: [PATCH 1/9] feat(babel-plugin-lingui-macro): allow to configure macro packages --- .../src/constants.ts | 3 - .../babel-plugin-lingui-macro/src/index.ts | 58 +++++++++++-------- .../babel-plugin-lingui-macro/src/macroJsx.ts | 35 +++++------ .../test/__snapshots__/js-t.test.ts.snap | 34 +++++++++++ .../test/__snapshots__/jsx-trans.test.ts.snap | 38 ++++++++++++ .../test/js-t.test.ts | 36 ++++++++++++ .../test/jsx-trans.test.ts | 36 ++++++++++++ .../test/macroTester.ts | 18 ++++-- packages/conf/package.json | 1 + packages/conf/src/makeConfig.ts | 14 +++-- packages/conf/src/types.ts | 34 +++++++++++ yarn.lock | 8 +++ 12 files changed, 260 insertions(+), 55 deletions(-) diff --git a/packages/babel-plugin-lingui-macro/src/constants.ts b/packages/babel-plugin-lingui-macro/src/constants.ts index e064e5ec2..ff87852a6 100644 --- a/packages/babel-plugin-lingui-macro/src/constants.ts +++ b/packages/babel-plugin-lingui-macro/src/constants.ts @@ -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", diff --git a/packages/babel-plugin-lingui-macro/src/index.ts b/packages/babel-plugin-lingui-macro/src/index.ts index 1af92bdc0..673a9ba32 100644 --- a/packages/babel-plugin-lingui-macro/src/index.ts +++ b/packages/babel-plugin-lingui-macro/src/index.ts @@ -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, @@ -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 } @@ -110,14 +120,15 @@ export default function ({ } function getMacroImports(path: NodePath) { + const linguiPackages = new Set([ + ...config.macro.corePackage, + ...config.macro.reactPackage, + ]) + 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) ) }) } @@ -138,11 +149,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.reactPackage.some((moduleSource) => + identPath.referencesImport(moduleSource, JsMacroName.useLingui) + ) ) { return true } @@ -151,8 +160,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 } @@ -164,6 +174,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) { @@ -172,11 +187,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"), diff --git a/packages/babel-plugin-lingui-macro/src/macroJsx.ts b/packages/babel-plugin-lingui-macro/src/macroJsx.ts index 903382453..322fb421d 100644 --- a/packages/babel-plugin-lingui-macro/src/macroJsx.ts +++ b/packages/babel-plugin-lingui-macro/src/macroJsx.ts @@ -18,13 +18,7 @@ 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 { @@ -32,6 +26,8 @@ import { 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) => @@ -387,14 +383,17 @@ export class MacroJSX { tokenizeConditionalExpression = ( exp: NodePath ): 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) } @@ -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.reactPackage.some((moduleSource) => + identifier.referencesImport(moduleSource, name) ) } diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/js-t.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-t.test.ts.snap index 54b5a5714..0aada704a 100644 --- a/packages/babel-plugin-lingui-macro/test/__snapshots__/js-t.test.ts.snap +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-t.test.ts.snap @@ -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(); @@ -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\`; diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap index a171f1562..037ad28b9 100644 --- a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap @@ -891,6 +891,44 @@ import { Trans as _Trans } from "@lingui/react"; `; +exports[`should detects macro imported from config.macro.reactPackage 1`] = ` +import { Trans } from "@my-lingui/macro"; +Hello World; + +↓ ↓ ↓ ↓ ↓ ↓ + +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"; +Hello World; + +↓ ↓ ↓ ↓ ↓ ↓ + +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"; Hello World; diff --git a/packages/babel-plugin-lingui-macro/test/js-t.test.ts b/packages/babel-plugin-lingui-macro/test/js-t.test.ts index 8c7dd647a..b6e63ae61 100644 --- a/packages/babel-plugin-lingui-macro/test/js-t.test.ts +++ b/packages/babel-plugin-lingui-macro/test/js-t.test.ts @@ -1,4 +1,5 @@ import { macroTester } from "./macroTester" +import { makeConfig } from "@lingui/conf" describe.skip("", () => {}) @@ -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\` + `, + }, ], }) diff --git a/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts b/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts index 0f27d8920..4e21e87d6 100644 --- a/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts +++ b/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts @@ -1,4 +1,5 @@ import { macroTester } from "./macroTester" +import { makeConfig } from "@lingui/conf" describe.skip("", () => {}) macroTester({ @@ -317,5 +318,40 @@ macroTester({ const cmp = Hello `, }, + { + name: "should respect runtimeConfigModule", + macroOpts: { + linguiConfig: makeConfig( + { + runtimeConfigModule: { + Trans: ["@my/lingui", "myTrans"], + }, + }, + { skipValidation: true } + ), + }, + code: ` + import { Trans } from '@lingui/react/macro'; + Hello World; + `, + }, + { + name: "should detects macro imported from config.macro.reactPackage", + macroOpts: { + linguiConfig: makeConfig( + { + macro: { + reactPackage: ["@my-lingui/macro"], + }, + }, + { skipValidation: true } + ), + }, + skipBabelMacroTest: true, + code: ` + import { Trans } from '@my-lingui/macro'; + Hello World; + `, + }, ], }) diff --git a/packages/babel-plugin-lingui-macro/test/macroTester.ts b/packages/babel-plugin-lingui-macro/test/macroTester.ts index fe5c93f2e..6d69c38b5 100644 --- a/packages/babel-plugin-lingui-macro/test/macroTester.ts +++ b/packages/babel-plugin-lingui-macro/test/macroTester.ts @@ -33,6 +33,10 @@ type TestCaseCommon = { macroOpts?: LinguiPluginOpts only?: boolean skip?: boolean + /** + * Will not execute test using babel-macro-plugin + */ + skipBabelMacroTest?: boolean } export type MacroTesterOptions = { @@ -97,13 +101,15 @@ export function macroTester(opts: MacroTesterOptions) { getDefaultBabelOptions("plugin", macroOpts, useTypescriptPreset) ).code.trim() - const actualMacro = transformSync( - testCase.code, - getDefaultBabelOptions("macro", macroOpts, useTypescriptPreset) - ).code.trim() + if (!testCase.skipBabelMacroTest) { + const actualMacro = transformSync( + testCase.code, + getDefaultBabelOptions("macro", macroOpts, useTypescriptPreset) + ).code.trim() - // output from plugin transformation should be the same to macro transformation - expect(actualPlugin).toBe(actualMacro) + // output from plugin transformation should be the same to macro transformation + expect(actualPlugin).toBe(actualMacro) + } if (testCase.expected) { expect(clean(actualPlugin)).toEqual(clean(testCase.expected)) diff --git a/packages/conf/package.json b/packages/conf/package.json index fe868c17e..82986b93b 100644 --- a/packages/conf/package.json +++ b/packages/conf/package.json @@ -28,6 +28,7 @@ "@babel/runtime": "^7.20.13", "chalk": "^4.1.0", "cosmiconfig": "^8.0.0", + "defu": "^6.1.4", "jest-validate": "^29.4.3", "jiti": "^1.17.1", "lodash.get": "^4.4.2" diff --git a/packages/conf/src/makeConfig.ts b/packages/conf/src/makeConfig.ts index 665c07ad3..2107b359d 100644 --- a/packages/conf/src/makeConfig.ts +++ b/packages/conf/src/makeConfig.ts @@ -10,6 +10,7 @@ import { setCldrParentLocales } from "./migrations/setCldrParentLocales" import { pathJoinPosix } from "./utils/pathJoinPosix" import { normalizeRuntimeConfigModule } from "./migrations/normalizeRuntimeConfigModule" import { ExperimentalExtractorOptions } from "./types" +import { defu } from "defu" export function makeConfig( userConfig: Partial, @@ -17,10 +18,7 @@ export function makeConfig( skipValidation?: boolean } = {} ): LinguiConfigNormalized { - let config: LinguiConfig = { - ...defaultConfig, - ...userConfig, - } + let config: LinguiConfig = defu(userConfig, defaultConfig) if (!opts.skipValidation) { validate(config, configValidation) @@ -66,6 +64,13 @@ export const defaultConfig: LinguiConfig = { pseudoLocale: "", rootDir: ".", runtimeConfigModule: ["@lingui/core", "i18n"], + /** + * Allow you to set macro options + */ + macro: { + corePackage: ["@lingui/macro", "@lingui/core/macro"], + reactPackage: ["@lingui/macro", "@lingui/react/macro"], + }, sourceLocale: "", service: { name: "", apiKey: "" }, } @@ -88,7 +93,6 @@ export const exampleConfig = { flow: false, tsExperimentalDecorators: false, }, - experimental: { extractor: { entries: [], diff --git a/packages/conf/src/types.ts b/packages/conf/src/types.ts index 29c2d658f..5f11589e9 100644 --- a/packages/conf/src/types.ts +++ b/packages/conf/src/types.ts @@ -214,6 +214,40 @@ export type LinguiConfig = { | Partial> sourceLocale?: string service?: CatalogService + macro?: { + /** + * Allows customizing the Core Macro package name that the Lingui macro detects. + * + * ```ts + * // lingui.config + * macro.macro.corePackage = ['@lingui/myMacro'] + * + * // app.tsx + * import { msg } from '@lingui/myMacro' + * + * msg`Hello` // <-- would be correctly picked up by macro + * ``` + * + * @default ["@lingui/macro", "@lingui/core/macro"] + */ + corePackage?: string[] + /** + * Allows customizing the React Macro package name that the Lingui macro detects. + * + * ```ts + * // lingui.config + * macro.macro.reactPackage = ['@lingui/myMacro'] + * + * // app.tsx + * import { Trans } from '@lingui/myMacro' + * + * Hello // <-- would be correctly picked up by macro + * ``` + * + * @default ["@lingui/macro", "@lingui/react/macro"] + */ + reactPackage?: string[] + } experimental?: { extractor?: ExperimentalExtractorOptions } diff --git a/yarn.lock b/yarn.lock index 5745f17d8..b6ae573cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2698,6 +2698,7 @@ __metadata: "@lingui/jest-mocks": "*" chalk: ^4.1.0 cosmiconfig: ^8.0.0 + defu: ^6.1.4 jest-validate: ^29.4.3 jiti: ^1.17.1 lodash.get: ^4.4.2 @@ -6743,6 +6744,13 @@ __metadata: languageName: node linkType: hard +"defu@npm:^6.1.4": + version: 6.1.4 + resolution: "defu@npm:6.1.4" + checksum: 40e3af6338f195ac1564f53d1887fa2d0429ac7e8c081204bc4d29191180059d3952b5f4e08fe5df8d59eb873aa26e9c88b56d4fac699673d4a372c93620b229 + languageName: node + linkType: hard + "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" From c865030ce11ccc994f81228d31b7b27c55b63083 Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Mon, 10 Mar 2025 13:03:08 +0100 Subject: [PATCH 2/9] fix tests --- packages/conf/src/__snapshots__/index.test.ts.snap | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/conf/src/__snapshots__/index.test.ts.snap b/packages/conf/src/__snapshots__/index.test.ts.snap index 0df35e5c5..99941854c 100644 --- a/packages/conf/src/__snapshots__/index.test.ts.snap +++ b/packages/conf/src/__snapshots__/index.test.ts.snap @@ -36,6 +36,16 @@ exports[`@lingui/conf should return default config 1`] = ` locales: [ en-gb, ], + macro: { + corePackage: [ + @lingui/macro, + @lingui/core/macro, + ], + reactPackage: [ + @lingui/macro, + @lingui/react/macro, + ], + }, orderBy: message, pseudoLocale: , rootDir: ., From ce4a188e65104f2bf3261999166ec489aa6ae975 Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Mon, 10 Mar 2025 13:42:13 +0100 Subject: [PATCH 3/9] fix tests --- packages/babel-plugin-lingui-macro/src/index.ts | 17 ++++++++++------- .../src/macroJsx.test.ts | 6 +++++- packages/conf/package.json | 1 - packages/conf/src/makeConfig.ts | 10 ++++++++-- yarn.lock | 8 -------- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/babel-plugin-lingui-macro/src/index.ts b/packages/babel-plugin-lingui-macro/src/index.ts index 673a9ba32..5954a0203 100644 --- a/packages/babel-plugin-lingui-macro/src/index.ts +++ b/packages/babel-plugin-lingui-macro/src/index.ts @@ -71,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 diff --git a/packages/babel-plugin-lingui-macro/src/macroJsx.test.ts b/packages/babel-plugin-lingui-macro/src/macroJsx.test.ts index d74905513..a3a746396 100644 --- a/packages/babel-plugin-lingui-macro/src/macroJsx.test.ts +++ b/packages/babel-plugin-lingui-macro/src/macroJsx.test.ts @@ -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 @@ -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() }, }, diff --git a/packages/conf/package.json b/packages/conf/package.json index 82986b93b..fe868c17e 100644 --- a/packages/conf/package.json +++ b/packages/conf/package.json @@ -28,7 +28,6 @@ "@babel/runtime": "^7.20.13", "chalk": "^4.1.0", "cosmiconfig": "^8.0.0", - "defu": "^6.1.4", "jest-validate": "^29.4.3", "jiti": "^1.17.1", "lodash.get": "^4.4.2" diff --git a/packages/conf/src/makeConfig.ts b/packages/conf/src/makeConfig.ts index 2107b359d..3c1dd1a15 100644 --- a/packages/conf/src/makeConfig.ts +++ b/packages/conf/src/makeConfig.ts @@ -10,7 +10,6 @@ import { setCldrParentLocales } from "./migrations/setCldrParentLocales" import { pathJoinPosix } from "./utils/pathJoinPosix" import { normalizeRuntimeConfigModule } from "./migrations/normalizeRuntimeConfigModule" import { ExperimentalExtractorOptions } from "./types" -import { defu } from "defu" export function makeConfig( userConfig: Partial, @@ -18,7 +17,14 @@ export function makeConfig( skipValidation?: boolean } = {} ): LinguiConfigNormalized { - let config: LinguiConfig = defu(userConfig, defaultConfig) + let config: LinguiConfig = { + ...defaultConfig, + ...userConfig, + macro: { + ...defaultConfig.macro, + ...userConfig.macro, + }, + } if (!opts.skipValidation) { validate(config, configValidation) diff --git a/yarn.lock b/yarn.lock index b6ae573cb..5745f17d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2698,7 +2698,6 @@ __metadata: "@lingui/jest-mocks": "*" chalk: ^4.1.0 cosmiconfig: ^8.0.0 - defu: ^6.1.4 jest-validate: ^29.4.3 jiti: ^1.17.1 lodash.get: ^4.4.2 @@ -6744,13 +6743,6 @@ __metadata: languageName: node linkType: hard -"defu@npm:^6.1.4": - version: 6.1.4 - resolution: "defu@npm:6.1.4" - checksum: 40e3af6338f195ac1564f53d1887fa2d0429ac7e8c081204bc4d29191180059d3952b5f4e08fe5df8d59eb873aa26e9c88b56d4fac699673d4a372c93620b229 - languageName: node - linkType: hard - "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" From 8eb984a1caef26690abe3786d572206cf4056f50 Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Mon, 10 Mar 2025 13:55:08 +0100 Subject: [PATCH 4/9] support macro config in vite plugin --- packages/vite-plugin/src/index.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts index a06442b03..450acbb61 100644 --- a/packages/vite-plugin/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -19,16 +19,17 @@ type LinguiConfigOpts = { export function lingui(linguiConfig: LinguiConfigOpts = {}): Plugin[] { const config = getConfig(linguiConfig) + const macroIds = new Set([ + ...config.macro.corePackage, + ...config.macro.reactPackage, + ]) + return [ { name: "vite-plugin-lingui-report-macro-error", enforce: "pre", resolveId(id) { - if ( - id.includes("@lingui/macro") || - id.includes("@lingui/core/macro") || - id.includes("@lingui/react/macro") - ) { + if (macroIds.has(id)) { throw new Error( `The macro you imported from "${id}" is being executed outside the context of compilation. \n` + `This indicates that you don't configured correctly one of the "babel-plugin-macros" / "@lingui/swc-plugin" / "babel-plugin-lingui-macro"` + @@ -46,9 +47,10 @@ export function lingui(linguiConfig: LinguiConfigOpts = {}): Plugin[] { config.optimizeDeps = {} } config.optimizeDeps.exclude = config.optimizeDeps.exclude || [] - config.optimizeDeps.exclude.push("@lingui/macro") - config.optimizeDeps.exclude.push("@lingui/core/macro") - config.optimizeDeps.exclude.push("@lingui/react/macro") + + for (const macroId of macroIds) { + config.optimizeDeps.exclude.push(macroId) + } }, async transform(src, id) { if (fileRegex.test(id)) { From b2c6afe04d59ef3aa7cf89af93cc7257e85f20bd Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Wed, 19 Mar 2025 08:30:51 +0100 Subject: [PATCH 5/9] add docs --- .../babel-plugin-lingui-macro/src/index.ts | 4 +- .../babel-plugin-lingui-macro/src/macroJsx.ts | 2 +- .../test/jsx-trans.test.ts | 4 +- packages/conf/src/makeConfig.ts | 5 +- packages/conf/src/types.ts | 96 ++++++++++++++++++- website/docs/ref/conf.md | 44 +++++++++ 6 files changed, 141 insertions(+), 14 deletions(-) diff --git a/packages/babel-plugin-lingui-macro/src/index.ts b/packages/babel-plugin-lingui-macro/src/index.ts index 5954a0203..ea4dcb882 100644 --- a/packages/babel-plugin-lingui-macro/src/index.ts +++ b/packages/babel-plugin-lingui-macro/src/index.ts @@ -125,7 +125,7 @@ export default function ({ function getMacroImports(path: NodePath) { const linguiPackages = new Set([ ...config.macro.corePackage, - ...config.macro.reactPackage, + ...config.macro.jsxPackage, ]) return path.get("body").filter((statement) => { @@ -152,7 +152,7 @@ export default function ({ if (macro === JsMacroName.useLingui) { if ( - config.macro.reactPackage.some((moduleSource) => + config.macro.jsxPackage.some((moduleSource) => identPath.referencesImport(moduleSource, JsMacroName.useLingui) ) ) { diff --git a/packages/babel-plugin-lingui-macro/src/macroJsx.ts b/packages/babel-plugin-lingui-macro/src/macroJsx.ts index 322fb421d..61fbfac84 100644 --- a/packages/babel-plugin-lingui-macro/src/macroJsx.ts +++ b/packages/babel-plugin-lingui-macro/src/macroJsx.ts @@ -418,7 +418,7 @@ export class MacroJSX { ) as LinguiConfigNormalized const identifier = path.get("openingElement").get("name") - return config.macro.reactPackage.some((moduleSource) => + return config.macro.jsxPackage.some((moduleSource) => identifier.referencesImport(moduleSource, name) ) } diff --git a/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts b/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts index 4e21e87d6..e1283cc36 100644 --- a/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts +++ b/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts @@ -336,12 +336,12 @@ macroTester({ `, }, { - name: "should detects macro imported from config.macro.reactPackage", + name: "should detects macro imported from config.macro.jsxPackage", macroOpts: { linguiConfig: makeConfig( { macro: { - reactPackage: ["@my-lingui/macro"], + jsxPackage: ["@my-lingui/macro"], }, }, { skipValidation: true } diff --git a/packages/conf/src/makeConfig.ts b/packages/conf/src/makeConfig.ts index 3c1dd1a15..1d6e40ec1 100644 --- a/packages/conf/src/makeConfig.ts +++ b/packages/conf/src/makeConfig.ts @@ -70,12 +70,9 @@ export const defaultConfig: LinguiConfig = { pseudoLocale: "", rootDir: ".", runtimeConfigModule: ["@lingui/core", "i18n"], - /** - * Allow you to set macro options - */ macro: { corePackage: ["@lingui/macro", "@lingui/core/macro"], - reactPackage: ["@lingui/macro", "@lingui/react/macro"], + jsxPackage: ["@lingui/macro", "@lingui/react/macro"], }, sourceLocale: "", service: { name: "", apiKey: "" }, diff --git a/packages/conf/src/types.ts b/packages/conf/src/types.ts index 5f11589e9..9a55f888f 100644 --- a/packages/conf/src/types.ts +++ b/packages/conf/src/types.ts @@ -182,8 +182,17 @@ export type ExperimentalExtractorOptions = { } export type LinguiConfig = { + /** + * The catalogs configuration defines the location of message catalogs and specifies + * which files are included when the extract command scans for messages. + * + * https://lingui.dev/ref/conf#catalogs + */ catalogs?: CatalogConfig[] compileNamespace?: "es" | "ts" | "cjs" | string + /** + * Specify additional options used to parse source files when extracting messages. + */ extractorParserOptions?: { /** * default false @@ -199,28 +208,101 @@ export type LinguiConfig = { } compilerBabelOptions?: any fallbackLocales?: FallbackLocales | false + /** + * Specifies custom message extractor implementations + * + * https://lingui.dev/guides/custom-extractor + */ extractors?: ExtractorType[] prevFormat?: CatalogFormat - localeDir?: string + /** + * Message catalog format. The po formatter is used by default. Other formatters are available as separate packages. + * + * @default "po" + */ format?: CatalogFormat | CatalogFormatter formatOptions?: CatalogFormatOptions + /** + * The locale tags used in the project. The `extract` and `compile` commands write a catalog for each locale specified. + * + * Each locale should be a valid BCP-47 code: + * @example + * + * ```js + * locales: ["en", "cs"] + * ``` + */ locales: string[] + /** + * Define the path where translated catalogs are merged into a single file per locale during the compile process. + * + * https://lingui.dev/ref/conf#catalogsmergepath + */ catalogsMergePath?: string + /** + * Order of messages in catalog + * + * @default "message" + */ orderBy?: OrderBy + /** + * Locale used for pseudolocalization. For example, when you set `pseudoLocale: "en"`, all messages in the en catalog will be pseudo-localized. + * The locale must be included in the locales config. + * + * https://lingui.dev/guides/pseudolocalization + */ pseudoLocale?: string + /** + * This is the directory where the Lingui CLI scans for messages in your source files during the extraction process. + * + * Note that using as a string token in any other path-based config settings will refer back to this value. + * + * @defaul: The root of the directory containing your Lingui configuration file or the package.json. + */ rootDir?: string + /** + * This setting specifies the module path for the exported `i18n` object and `Trans` component. + * + * @example + * + * ```js + * { + * "runtimeConfigModule": { + * "Trans": ["./myTrans", "Trans"], + * "useLingui": ["./myUseLingui", "useLingui"] + * "i18n": ["./nyI18n", "I18n"] + * } + * } + * ``` + */ runtimeConfigModule?: | ModuleSource | Partial> + /** + * Specifies the default language of message IDs in your source files. + * + * The catalog for sourceLocale doesn't need actual translations since message IDs are used as-is by default. + * However, you can still override any message ID by providing a custom translation. + * + * The main difference between `sourceLocale` and `fallbackLocales` is their purpose: `sourceLocale` defines the language used for message IDs, + * while `fallbackLocales` provides alternative translations when specific messages are missing for a particular locale. + */ sourceLocale?: string service?: CatalogService + /** + * Allow you to set macro options + */ macro?: { /** * Allows customizing the Core Macro package name that the Lingui macro detects. * * ```ts * // lingui.config - * macro.macro.corePackage = ['@lingui/myMacro'] + * { + * macro: { + * corePackage: ['@lingui/myMacro'] + * } + * } * * // app.tsx * import { msg } from '@lingui/myMacro' @@ -232,11 +314,15 @@ export type LinguiConfig = { */ corePackage?: string[] /** - * Allows customizing the React Macro package name that the Lingui macro detects. + * Allows customizing the JSX Macro package name that the Lingui macro detects. * * ```ts * // lingui.config - * macro.macro.reactPackage = ['@lingui/myMacro'] + * { + * macro: { + * jsxPackage: ["@lingui/myMacro"]; + * } + * } * * // app.tsx * import { Trans } from '@lingui/myMacro' @@ -246,7 +332,7 @@ export type LinguiConfig = { * * @default ["@lingui/macro", "@lingui/react/macro"] */ - reactPackage?: string[] + jsxPackage?: string[] } experimental?: { extractor?: ExperimentalExtractorOptions diff --git a/website/docs/ref/conf.md b/website/docs/ref/conf.md index bc48496b0..d55ea8b03 100644 --- a/website/docs/ref/conf.md +++ b/website/docs/ref/conf.md @@ -480,3 +480,47 @@ Extractors it's the way to customize which extractor you want for your codebase. ``` See the [Custom Extractor](/guides/custom-extractor) guide for instructions on creating your own extractor. + +## macro.corePackage + +Default value: `["@lingui/macro", "@lingui/core/macro"]` + +Allows customizing the Core Macro package name that the Lingui macro detects. + +```js +// lingui.config +{ + macro: { + corePackage: ["@lingui/myMacro"]; + } +} + +// app.tsx +import { msg } from "@lingui/myMacro"; + +msg`Hello`; // <-- would be correctly picked up by macro +``` + +This setting mostly useful for external framework integrations. + +## macro.jsxPackage + +Default value: `["@lingui/macro", "@lingui/react/macro"]` + +Allows customizing the JSX Macro package name that the Lingui macro detects. + +```jsx +// lingui.config +{ + macro: { + jsxPackage: ["@lingui/myMacro"]; + } +} + +// app.tsx +import { Trans } from "@lingui/myMacro"; + +Hello; // <-- would be correctly picked up by macro +``` + +This setting mostly useful for external framework integrations. From 0658bac809255bf80090d67e8ff080289fd1337f Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Wed, 19 Mar 2025 08:35:08 +0100 Subject: [PATCH 6/9] fix --- .../test/__snapshots__/jsx-trans.test.ts.snap | 19 +++++++++++++++++++ .../conf/src/__snapshots__/index.test.ts.snap | 2 +- packages/vite-plugin/src/index.ts | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap index 037ad28b9..beb33256d 100644 --- a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap @@ -891,6 +891,25 @@ import { Trans as _Trans } from "@lingui/react"; `; +exports[`should detects macro imported from config.macro.jsxPackage 1`] = ` +import { Trans } from "@my-lingui/macro"; +Hello World; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + { + /*i18n*/ + ...{ + id: "mY42CM", + message: "Hello World", + } + } +/>; + +`; + exports[`should detects macro imported from config.macro.reactPackage 1`] = ` import { Trans } from "@my-lingui/macro"; Hello World; diff --git a/packages/conf/src/__snapshots__/index.test.ts.snap b/packages/conf/src/__snapshots__/index.test.ts.snap index 99941854c..c995ab6df 100644 --- a/packages/conf/src/__snapshots__/index.test.ts.snap +++ b/packages/conf/src/__snapshots__/index.test.ts.snap @@ -41,7 +41,7 @@ exports[`@lingui/conf should return default config 1`] = ` @lingui/macro, @lingui/core/macro, ], - reactPackage: [ + jsxPackage: [ @lingui/macro, @lingui/react/macro, ], diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts index 450acbb61..b1b3059f4 100644 --- a/packages/vite-plugin/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -21,7 +21,7 @@ export function lingui(linguiConfig: LinguiConfigOpts = {}): Plugin[] { const macroIds = new Set([ ...config.macro.corePackage, - ...config.macro.reactPackage, + ...config.macro.jsxPackage, ]) return [ From cfc969a374b23b8420cfa4f043618a962593416d Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Wed, 19 Mar 2025 08:39:42 +0100 Subject: [PATCH 7/9] fix --- .../test/__snapshots__/jsx-trans.test.ts.snap | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap index beb33256d..94760ae8c 100644 --- a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap @@ -910,25 +910,6 @@ import { Trans as _Trans } from "@lingui/react"; `; -exports[`should detects macro imported from config.macro.reactPackage 1`] = ` -import { Trans } from "@my-lingui/macro"; -Hello World; - -↓ ↓ ↓ ↓ ↓ ↓ - -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"; Hello World; From ed19555f697daa6f1fef02022c3c738b63e4f0c7 Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Wed, 19 Mar 2025 08:46:36 +0100 Subject: [PATCH 8/9] remove editorconfig-checker --- website/.ecrc | 10 ---------- website/package.json | 3 +-- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 website/.ecrc diff --git a/website/.ecrc b/website/.ecrc deleted file mode 100644 index a87f3c475..000000000 --- a/website/.ecrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "exclude": [ - ".docusaurus", - ".idea", - "build", - "build-linkcheck", - "node_modules", - "*.md" - ] -} diff --git a/website/package.json b/website/package.json index 137d51f5e..21b4654eb 100644 --- a/website/package.json +++ b/website/package.json @@ -9,7 +9,7 @@ "swizzle": "docusaurus swizzle", "clear": "docusaurus clear", "serve": "docusaurus serve", - "lint": "eslint src *.js *.ts && remark . --ext md,mdx --quiet --frail --rc-path .remarkrc.mjs && editorconfig-checker", + "lint": "eslint src *.js *.ts && remark . --ext md,mdx --quiet --frail --rc-path .remarkrc.mjs", "lintFix": "eslint --fix src *.js *.ts", "checkFormat": "prettier --check .", "fixFormat": "prettier --write ." @@ -42,7 +42,6 @@ "@types/react-router-dom": "5.3.3", "@typescript-eslint/eslint-plugin": "8.18.2", "@typescript-eslint/parser": "8.18.2", - "editorconfig-checker": "6.0.0", "eslint": "9.17.0", "eslint-plugin-react": "7.37.3", "eslint-plugin-react-hooks": "5.1.0", From 3c47902d9dd4d8b6a88e6342924fce3cbac84329 Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Wed, 19 Mar 2025 08:48:25 +0100 Subject: [PATCH 9/9] remove editorconfig-checker --- website/yarn.lock | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index 1415367b3..49adcb3bc 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -8548,16 +8548,6 @@ __metadata: languageName: node linkType: hard -"editorconfig-checker@npm:6.0.0": - version: 6.0.0 - resolution: "editorconfig-checker@npm:6.0.0" - bin: - ec: dist/index.js - editorconfig-checker: dist/index.js - checksum: 32baee909d87e7f5d72df9e678a846d566e6988fc9597bf29ef97dfafd11888c3addcf9a26190c03a0a205e2dc7dcdf2466133b6e643f423ec4f68132c28a6be - languageName: node - linkType: hard - "ee-first@npm:1.1.1": version: 1.1.1 resolution: "ee-first@npm:1.1.1" @@ -11531,7 +11521,6 @@ __metadata: "@typescript-eslint/parser": 8.18.2 clsx: 2.1.1 docusaurus-plugin-sass: ^0.2.6 - editorconfig-checker: 6.0.0 eslint: 9.17.0 eslint-plugin-react: 7.37.3 eslint-plugin-react-hooks: 5.1.0