|
1 | | -import type { PluginObj, Visitor, PluginPass } from "@babel/core" |
| 1 | +import type { PluginObj, PluginPass, Visitor } from "@babel/core" |
2 | 2 | import type * as babelTypes from "@babel/types" |
3 | | -import { Program, Identifier } from "@babel/types" |
| 3 | +import { Identifier, Program } from "@babel/types" |
4 | 4 | import { MacroJSX } from "./macroJsx" |
5 | 5 | import type { NodePath } from "@babel/traverse" |
6 | 6 | import { MacroJs } from "./macroJs" |
7 | 7 | import { JsMacroName } from "./constants" |
8 | 8 | import { |
9 | | - type LinguiConfigNormalized, |
10 | 9 | getConfig as loadConfig, |
11 | 10 | LinguiConfig, |
| 11 | + type LinguiConfigNormalized, |
12 | 12 | } from "@lingui/conf" |
| 13 | +import { ResolvedDescriptorFields } from "./messageDescriptorUtils" |
13 | 14 |
|
14 | 15 | let config: LinguiConfigNormalized |
15 | 16 |
|
16 | 17 | export type LinguiPluginOpts = { |
17 | | - /* |
18 | | - * When set `true` all auxiliary data such as `comment`, `context`, |
19 | | - * and default message would be kept regardless of the current environment |
20 | | - * |
21 | | - * This flag explicitly set by Lingui CLI when running extraction process |
22 | | - */ |
23 | | - extract?: boolean |
24 | 18 | /** |
25 | | - * Setting `stripMessageField` to `true` will strip messages and comments from both development and production bundles. |
26 | | - * Alternatively, set it to `false` to keep the original messages in both environments. |
| 19 | + * Controls which descriptor fields are preserved in the transformed code. |
27 | 20 | * |
28 | | - * If not set value would be determined based on `process.env.NODE_ENV === "production"` |
| 21 | + * - `"auto"` (default): In production (`NODE_ENV === "production"`), keeps only the `id`. |
| 22 | + * Otherwise, behaves like `"all"`. |
| 23 | + * - `"all"`: Keeps every field: `id`, `message`, `context`, and `comment`. |
| 24 | + * Used by Lingui CLI during extraction. |
| 25 | + * - `"id-only"`: Strips everything except the `id`. Most optimized for production. |
| 26 | + * - `"message"`: Keeps `id`, `message`, and `context` (but not `comment`). |
| 27 | + * Use when you need runtime access to message and context. |
| 28 | + * |
| 29 | + * @default "auto" |
29 | 30 | */ |
30 | | - stripMessageField?: boolean |
| 31 | + descriptorFields?: "auto" | "all" | "id-only" | "message" |
31 | 32 |
|
32 | 33 | /** |
33 | 34 | * Resolved and normalized Lingui Configuration |
@@ -58,13 +59,45 @@ function reportUnsupportedSyntax(path: NodePath, e: Error) { |
58 | 59 | throw codeFrameError |
59 | 60 | } |
60 | 61 |
|
61 | | -function shouldStripMessageProp(opts: LinguiPluginOpts) { |
62 | | - if (typeof opts.stripMessageField === "boolean") { |
63 | | - // if explicitly set in options, use it |
64 | | - return opts.stripMessageField |
| 62 | +const VALID_DESCRIPTOR_FIELDS = [ |
| 63 | + "auto", |
| 64 | + "all", |
| 65 | + "id-only", |
| 66 | + "message", |
| 67 | +] as LinguiPluginOpts["descriptorFields"][] |
| 68 | + |
| 69 | +const REMOVED_OPTIONS: Record<string, string> = { |
| 70 | + extract: |
| 71 | + 'Use `descriptorFields: "all"` instead of `extract: true` to preserve all fields during extraction.', |
| 72 | + stripMessageField: |
| 73 | + 'Use `descriptorFields: "id-only"` instead of `stripMessageField: true`, ' + |
| 74 | + 'or `descriptorFields: "message"` to keep message and context.', |
| 75 | +} |
| 76 | + |
| 77 | +function resolveDescriptorFields( |
| 78 | + opts: LinguiPluginOpts, |
| 79 | +): ResolvedDescriptorFields { |
| 80 | + // introduced in v6, remove these hints in V7 |
| 81 | + for (const [key, hint] of Object.entries(REMOVED_OPTIONS)) { |
| 82 | + if (key in (opts as Record<string, unknown>)) { |
| 83 | + throw new Error(`[lingui] Option "${key}" has been removed. ${hint}`) |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + const mode = opts.descriptorFields ?? "auto" |
| 88 | + |
| 89 | + if (!VALID_DESCRIPTOR_FIELDS.includes(mode)) { |
| 90 | + throw new Error( |
| 91 | + `[lingui] Invalid descriptorFields value: "${mode}". ` + |
| 92 | + `Expected one of: ${VALID_DESCRIPTOR_FIELDS.join(", ")}.`, |
| 93 | + ) |
| 94 | + } |
| 95 | + |
| 96 | + if (mode !== "auto") { |
| 97 | + return mode as ResolvedDescriptorFields |
65 | 98 | } |
66 | | - // default to strip message in production if no explicit option is set and not during extract |
67 | | - return process.env.NODE_ENV === "production" && !opts.extract |
| 99 | + // "auto": production → "id-only", otherwise → "all" |
| 100 | + return process.env.NODE_ENV === "production" ? "id-only" : "all" |
68 | 101 | } |
69 | 102 |
|
70 | 103 | type LinguiSymbol = "Trans" | "useLingui" | "i18n" |
@@ -236,10 +269,7 @@ export default function ({ |
236 | 269 | { types: t }, |
237 | 270 | { |
238 | 271 | transImportName: getSymbolIdentifier(state, "Trans").name, |
239 | | - stripNonEssentialProps: |
240 | | - process.env.NODE_ENV == "production" && |
241 | | - !(state.opts as LinguiPluginOpts).extract, |
242 | | - stripMessageProp: shouldStripMessageProp( |
| 272 | + descriptorFields: resolveDescriptorFields( |
243 | 273 | state.opts as LinguiPluginOpts, |
244 | 274 | ), |
245 | 275 | isLinguiIdentifier: (node: Identifier, macro) => |
@@ -269,10 +299,7 @@ export default function ({ |
269 | 299 | state: PluginPass, |
270 | 300 | ) { |
271 | 301 | const macro = new MacroJs({ |
272 | | - stripNonEssentialProps: |
273 | | - process.env.NODE_ENV == "production" && |
274 | | - !(state.opts as LinguiPluginOpts).extract, |
275 | | - stripMessageProp: shouldStripMessageProp( |
| 302 | + descriptorFields: resolveDescriptorFields( |
276 | 303 | state.opts as LinguiPluginOpts, |
277 | 304 | ), |
278 | 305 | i18nImportName: getSymbolIdentifier(state, "i18n").name, |
|
0 commit comments