From 02e24ee5381043d3f4d0de9bedb2ca32ad2cf095 Mon Sep 17 00:00:00 2001 From: Tobias Lundell Date: Thu, 17 Oct 2024 13:46:34 +0200 Subject: [PATCH] feat(macro): make message stripping configurable via Babel options --- packages/macro/src/index.ts | 21 ++++++++- packages/macro/src/macroJs.test.ts | 1 + packages/macro/src/macroJs.ts | 7 ++- packages/macro/src/macroJsx.test.ts | 6 ++- packages/macro/src/macroJsx.ts | 7 ++- packages/macro/test/js-t.ts | 69 ++++++++++++++++++++++++++--- packages/macro/test/jsx-trans.ts | 44 ++++++++++++++++++ 7 files changed, 145 insertions(+), 10 deletions(-) diff --git a/packages/macro/src/index.ts b/packages/macro/src/index.ts index ae0a41ae8..6155f6943 100644 --- a/packages/macro/src/index.ts +++ b/packages/macro/src/index.ts @@ -16,6 +16,7 @@ export type LinguiMacroOpts = { // explicitly set by CLI when running extraction process extract?: boolean linguiConfig?: LinguiConfigNormalized + stripMessageField?: boolean } const jsMacroTags = new Set([ @@ -83,12 +84,26 @@ function macro({ references, state, babel, config }: MacroParams) { const stripNonEssentialProps = process.env.NODE_ENV == "production" && !opts.extract + const stripMessageProp = (() => { + if (opts.extract) { + // never strip message during extraction process + return false + } + if (typeof opts.stripMessageField === "boolean") { + // if explicitly set in options, use it + return opts.stripMessageField + } + // default to strip message in production if no explicit option is set + return process.env.NODE_ENV === "production" + })() + const jsNodesArray = Array.from(jsNodes) jsNodesArray.filter(isRootPath(jsNodesArray)).forEach((path) => { const macro = new MacroJS(babel, { i18nImportName, stripNonEssentialProps, + stripMessageProp, nameMap, }) try { @@ -101,7 +116,11 @@ function macro({ references, state, babel, config }: MacroParams) { const jsxNodesArray = Array.from(jsxNodes) jsxNodesArray.filter(isRootPath(jsxNodesArray)).forEach((path) => { - const macro = new MacroJSX(babel, { stripNonEssentialProps, nameMap }) + const macro = new MacroJSX(babel, { + stripNonEssentialProps, + stripMessageProp, + nameMap, + }) try { macro.replacePath(path) diff --git a/packages/macro/src/macroJs.test.ts b/packages/macro/src/macroJs.test.ts index 3f746f123..472b8e734 100644 --- a/packages/macro/src/macroJs.test.ts +++ b/packages/macro/src/macroJs.test.ts @@ -9,6 +9,7 @@ function createMacro() { { i18nImportName: "i18n", stripNonEssentialProps: false, + stripMessageProp: false, nameMap: new Map(), } ) diff --git a/packages/macro/src/macroJs.ts b/packages/macro/src/macroJs.ts index 49bedba1d..700a7af6b 100644 --- a/packages/macro/src/macroJs.ts +++ b/packages/macro/src/macroJs.ts @@ -41,6 +41,7 @@ function buildICUFromTokens(tokens: Tokens) { export type MacroJsOpts = { i18nImportName: string stripNonEssentialProps: boolean + stripMessageProp: boolean nameMap: Map } @@ -51,6 +52,7 @@ export default class MacroJs { // Identifier of i18n object i18nImportName: string stripNonEssentialProps: boolean + stripMessageProp: boolean nameMap: Map nameMapReversed: Map @@ -61,6 +63,7 @@ export default class MacroJs { this.types = types this.i18nImportName = opts.i18nImportName this.stripNonEssentialProps = opts.stripNonEssentialProps + this.stripMessageProp = opts.stripMessageProp this.nameMap = opts.nameMap this.nameMapReversed = Array.from(opts.nameMap.entries()).reduce( (map, [key, value]) => map.set(value, key), @@ -218,7 +221,7 @@ export default class MacroJs { properties.push(this.createValuesProperty(values)) } - if (!this.stripNonEssentialProps) { + if (!this.stripMessageProp) { properties.push( this.createObjectProperty(MESSAGE, messageNode as Expression) ) @@ -404,7 +407,7 @@ export default class MacroJs { const properties: ObjectProperty[] = [ this.createIdProperty(message), - !this.stripNonEssentialProps + !this.stripMessageProp ? this.createObjectProperty(MESSAGE, this.types.stringLiteral(message)) : null, diff --git a/packages/macro/src/macroJsx.test.ts b/packages/macro/src/macroJsx.test.ts index 71bb6a464..de6e3ae9e 100644 --- a/packages/macro/src/macroJsx.test.ts +++ b/packages/macro/src/macroJsx.test.ts @@ -27,7 +27,11 @@ const parseExpression = (expression: string) => { function createMacro() { return new MacroJSX( { types }, - { stripNonEssentialProps: false, nameMap: new Map() } + { + stripNonEssentialProps: false, + stripMessageProp: false, + nameMap: new Map(), + } ) } diff --git a/packages/macro/src/macroJsx.ts b/packages/macro/src/macroJsx.ts index 66046967a..3b3ec2ce7 100644 --- a/packages/macro/src/macroJsx.ts +++ b/packages/macro/src/macroJsx.ts @@ -65,6 +65,7 @@ export function normalizeWhitespace(text: string): string { export type MacroJsxOpts = { stripNonEssentialProps: boolean + stripMessageProp: boolean nameMap: Map } @@ -73,12 +74,14 @@ export default class MacroJSX { expressionIndex = makeCounter() elementIndex = makeCounter() stripNonEssentialProps: boolean + stripMessageProp: boolean nameMap: Map nameMapReversed: Map constructor({ types }: { types: typeof babelTypes }, opts: MacroJsxOpts) { this.types = types this.stripNonEssentialProps = opts.stripNonEssentialProps + this.stripMessageProp = opts.stripMessageProp this.nameMap = opts.nameMap this.nameMapReversed = Array.from(opts.nameMap.entries()).reduce( (map, [key, value]) => map.set(value, key), @@ -130,11 +133,13 @@ export default class MacroJSX { ) } - if (!this.stripNonEssentialProps) { + if (!this.stripMessageProp) { if (message) { attributes.push(this.createStringJsxAttribute(MESSAGE, message)) } + } + if (!this.stripNonEssentialProps) { if (comment) { attributes.push( this.types.jsxAttribute( diff --git a/packages/macro/test/js-t.ts b/packages/macro/test/js-t.ts index e48684c81..44b9e44a6 100644 --- a/packages/macro/test/js-t.ts +++ b/packages/macro/test/js-t.ts @@ -367,6 +367,44 @@ const cases: TestCase[] = [ }); `, }, + { + name: "stripMessageField option - message prop is removed if stripMessageField: true", + macroOpts: { + stripMessageField: true, + }, + input: ` + import { t } from '@lingui/macro' + const msg = t\`Message\` + `, + expected: ` + import { i18n } from "@lingui/core"; + const msg = + i18n._(/*i18n*/ + { + id: "xDAtGP" + }); + `, + }, + { + name: "stripMessageField option - Message prop is kept during extraction process if extract: true and stripMessageField: true", + macroOpts: { + extract: true, + stripMessageField: true, + }, + input: ` + import { t } from '@lingui/macro' + const msg = t\`Message\` + `, + expected: ` + import { i18n } from "@lingui/core"; + const msg = + i18n._(/*i18n*/ + { + id: "xDAtGP", + message: "Message" + }); + `, + }, { name: "Production - only essential props are kept", production: true, @@ -486,11 +524,32 @@ const cases: TestCase[] = [ `, }, { - name: "Newlines after continuation character are removed", - filename: "js-t-continuation-character.js", - }, - { - filename: "js-t-var/js-t-var.js", + name: "Production - message prop is kept if stripMessageField: false", + production: true, + macroOpts: { + stripMessageField: false, + }, + input: ` + import { t } from '@lingui/macro'; + const msg = t({ + message: \`Hello $\{name\}\`, + id: 'msgId', + comment: 'description for translators', + context: 'My Context', + }) + `, + expected: ` + import { i18n } from "@lingui/core"; + const msg = + i18n._(/*i18n*/ + { + id: 'msgId', + values: { + name: name, + }, + message: "Hello {name}", + }); + `, }, ] diff --git a/packages/macro/test/jsx-trans.ts b/packages/macro/test/jsx-trans.ts index 1f03ada22..4a2723396 100644 --- a/packages/macro/test/jsx-trans.ts +++ b/packages/macro/test/jsx-trans.ts @@ -359,6 +359,35 @@ const cases: TestCase[] = [ {...spread} `, }, + { + name: "stripMessageField option - message prop is removed if stripMessageField: true", + macroOpts: { + stripMessageField: true, + }, + input: ` + import { Trans } from '@lingui/macro'; + Hello World + `, + expected: ` + import { Trans } from "@lingui/react"; + ; + `, + }, + { + name: "stripMessageField option - Message prop is kept during extraction process if extract: true and stripMessageField: true", + macroOpts: { + extract: true, + stripMessageField: true, + }, + input: ` + import { Trans } from '@lingui/macro'; + Hello World + `, + expected: ` + import { Trans } from "@lingui/react"; + ; + `, + }, { name: "Production - only essential props are kept", production: true, @@ -386,6 +415,21 @@ const cases: TestCase[] = [ ; `, }, + { + name: "Production - message prop is kept if stripMessageField: false", + production: true, + macroOpts: { + stripMessageField: false, + }, + input: ` + import { Trans } from '@lingui/macro'; + Hello World + `, + expected: ` + import { Trans } from "@lingui/react"; + ; + `, + }, { name: "Production - import type doesn't interference on normal import", production: true,