Skip to content

Commit ef77310

Browse files
authored
feat(cli): add --output-prefix option to customize compiled file headers (#2377)
1 parent c4f9b73 commit ef77310

File tree

6 files changed

+146
-1
lines changed

6 files changed

+146
-1
lines changed

packages/cli/src/api/compile.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,45 @@ describe("createCompiledCatalog", () => {
288288
})
289289
})
290290

291+
describe("options.outputPrefix", () => {
292+
const getCompiledCatalog = (outputPrefix?: string) =>
293+
createCompiledCatalog(
294+
"en",
295+
{
296+
Hello: "Hello",
297+
},
298+
{
299+
outputPrefix,
300+
}
301+
).source
302+
303+
it("should use default eslint-disable directive when not specified", () => {
304+
const result = getCompiledCatalog()
305+
expect(result).toContain("/*eslint-disable*/")
306+
})
307+
308+
it("should use oxlint-disable directive", () => {
309+
const result = getCompiledCatalog("/*oxlint-disable*/")
310+
expect(result).toContain("/*oxlint-disable*/")
311+
})
312+
313+
it("should use custom prefix when specified", () => {
314+
const result = getCompiledCatalog("/*biome-ignore lint: auto-generated*/")
315+
expect(result).toContain("/*biome-ignore lint: auto-generated*/")
316+
expect(result).not.toContain("eslint-disable")
317+
})
318+
319+
it("should handle empty string prefix (no header)", () => {
320+
const result = getCompiledCatalog("")
321+
expect(
322+
result.startsWith("export") ||
323+
result.startsWith("module") ||
324+
result.startsWith("window") ||
325+
result.startsWith("global")
326+
).toBeTruthy()
327+
})
328+
})
329+
291330
it("should return list of compile errors", () => {
292331
const res = createCompiledCatalog(
293332
"ru",

packages/cli/src/api/compile.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type CreateCompileCatalogOptions = {
1717
namespace?: CompiledCatalogNamespace
1818
pseudoLocale?: string
1919
compilerBabelOptions?: GeneratorOptions
20+
outputPrefix?: string
2021
}
2122

2223
export type MessageCompilationError = {
@@ -44,6 +45,7 @@ export function createCompiledCatalog(
4445
namespace = "cjs",
4546
pseudoLocale,
4647
compilerBabelOptions = {},
48+
outputPrefix = "/*eslint-disable*/",
4749
} = options
4850
const shouldPseudolocalize = locale === pseudoLocale
4951

@@ -91,7 +93,7 @@ export function createCompiledCatalog(
9193
...compilerBabelOptions,
9294
}).code
9395

94-
return { source: "/*eslint-disable*/" + code, errors }
96+
return { source: `${outputPrefix}` + code, errors }
9597
}
9698

9799
function buildExportStatement(

packages/cli/src/api/compile/compileLocale.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ async function compileAndWrite(
110110
{
111111
strict: false,
112112
namespace,
113+
outputPrefix: options.outputPrefix,
113114
pseudoLocale: config.pseudoLocale,
114115
compilerBabelOptions: config.compilerBabelOptions,
115116
}

packages/cli/src/lingui-compile.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type CliCompileOptions = {
2222
watch?: boolean
2323
namespace?: string
2424
workersOptions: WorkersOptions
25+
outputPrefix?: string
2526
}
2627

2728
export async function command(
@@ -113,6 +114,7 @@ type CliArgs = {
113114
config?: string
114115
debounce?: number
115116
workers?: number
117+
outputPrefix?: string
116118
}
117119

118120
if (require.main === module) {
@@ -135,6 +137,10 @@ if (require.main === module) {
135137
"--debounce <delay>",
136138
"Debounces compilation for given amount of milliseconds"
137139
)
140+
.option(
141+
"--output-prefix <prefix>",
142+
"Add a custom string to the beginning of compiled files (header/prefix). Useful for tools like linters or coverage directives. Defaults to '/*eslint-disable*/'"
143+
)
138144
.on("--help", function () {
139145
console.log("\n Examples:\n")
140146
console.log(
@@ -165,6 +171,7 @@ if (require.main === module) {
165171
typescript:
166172
options.typescript || config.compileNamespace === "ts" || false,
167173
namespace: options.namespace, // we want this to be undefined if user does not specify so default can be used
174+
outputPrefix: options.outputPrefix,
168175
})
169176
)
170177

packages/cli/src/test/compile.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,4 +456,72 @@ msgstr "{plural, }"
456456
})
457457
})
458458
})
459+
460+
describe("outputPrefix", () => {
461+
it("Should use custom output prefix in compiled files", async () => {
462+
const rootDir = await createFixtures({
463+
"en.po": `
464+
msgid "Hello World"
465+
msgstr "Hello World"
466+
`,
467+
"pl.po": `
468+
msgid "Hello World"
469+
msgstr "Witaj świecie"
470+
`,
471+
})
472+
473+
const config = getTestConfig(rootDir)
474+
475+
await mockConsole(async () => {
476+
const result = await command(config, {
477+
outputPrefix: "/*biome-ignore lint: auto-generated*/",
478+
workersOptions: {
479+
poolSize: 0,
480+
},
481+
})
482+
483+
const actualFiles = readFsToListing(rootDir)
484+
485+
expect(actualFiles["en.js"]).toContain(
486+
"/*biome-ignore lint: auto-generated*/"
487+
)
488+
expect(actualFiles["pl.js"]).toContain(
489+
"/*biome-ignore lint: auto-generated*/"
490+
)
491+
expect(actualFiles["en.js"]).not.toContain("eslint-disable")
492+
expect(actualFiles["pl.js"]).not.toContain("eslint-disable")
493+
expect(result).toBeTruthy()
494+
})
495+
})
496+
497+
it("Should use oxlint-disable prefix directive", async () => {
498+
const rootDir = await createFixtures({
499+
"en.po": `
500+
msgid "Test"
501+
msgstr "Test"
502+
`,
503+
"pl.po": `
504+
msgid "Test"
505+
msgstr "Test PL"
506+
`,
507+
})
508+
509+
const config = getTestConfig(rootDir)
510+
511+
await mockConsole(async () => {
512+
const result = await command(config, {
513+
outputPrefix: "/*oxlint-disable*/",
514+
workersOptions: {
515+
poolSize: 0,
516+
},
517+
})
518+
519+
const actualFiles = readFsToListing(rootDir)
520+
521+
expect(actualFiles["en.js"]).toContain("/*oxlint-disable*/")
522+
expect(actualFiles["en.js"]).not.toContain("eslint-disable")
523+
expect(result).toBeTruthy()
524+
})
525+
})
526+
})
459527
})

website/docs/ref/cli.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ lingui compile
172172
[--namespace <namespace>]
173173
[--watch [--debounce <delay>]]
174174
[--workers]
175+
[--output-prefix <prefix>]
175176
```
176177
177178
Once you have all the catalogs ready and translated, you can use this command to compile all the catalogs into minified JS/TS files. It compiles message catalogs located in the [`path`](/ref/conf#catalogs) directory and generates minified JavaScript files. The resulting file is a string that is parsed into a plain JS object using `JSON.parse`.
@@ -250,6 +251,33 @@ Worker threads can significantly improve performance on large projects. However,
250251
251252
A larger worker pool also increases memory usage. Adjust this value for your project to achieve the best performance.
252253
254+
#### `--output-prefix <prefix>` {#compile-output-prefix}
255+
256+
Adds a custom string to the beginning of compiled message catalogs (a header/prefix). By default, Lingui adds `/*eslint-disable*/` to prevent linters from reporting issues in generated files.
257+
258+
Use this option for other tools that rely on header directives (e.g., different linters, coverage tools, or formatters). Provide the full prefix exactly as it should appear in the output.
259+
260+
**Default value:** `/*eslint-disable*/`
261+
262+
**Examples:**
263+
264+
```shell
265+
# For Oxlint
266+
lingui compile --output-prefix "/*oxlint-disable*/"
267+
268+
# For Biome
269+
lingui compile --output-prefix "/*biome-ignore lint: auto-generated*/"
270+
271+
# For no prefix at all
272+
lingui compile --output-prefix ""
273+
```
274+
275+
The generated file header will look like:
276+
277+
```js
278+
/*your-prefix-here*/ export const messages = JSON.parse("{}");
279+
```
280+
253281
## Configuring the Source Locale
254282
255283
One limitation of checking for missing translations is that the English message catalog typically does not require translations since our source code is in English. This issue can be resolved by configuring the [`sourceLocale`](/ref/conf#sourcelocale) in the configuration file.

0 commit comments

Comments
 (0)