Skip to content

Commit 86fbcd8

Browse files
feat(cli): support multithreading in all cli commands (#2320)
Co-authored-by: Zxilly <[email protected]>
1 parent c4ddc61 commit 86fbcd8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2053
-716
lines changed

CONTRIBUTING.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ There are two documented ways to do this: first is a generic way described in th
117117
YARN_NPM_REGISTRY_SERVER=http://0.0.0.0:4873/ yarn up "@lingui/*"
118118
```
119119

120+
with PNPM:
121+
122+
```sh
123+
pnpm -r up "@lingui/*" --latest --registry=http://0.0.0.0:4873/
124+
```
125+
120126
5. After you make some changes, you need to run the same process. (Releasing + yarn upgrade)
121127

122128
6. When finished testing, restore default registry (only for NPM)

packages/cli/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,23 @@
7676
"esbuild": "^0.25.1",
7777
"glob": "^11.0.0",
7878
"micromatch": "^4.0.7",
79+
"ms": "^2.1.3",
7980
"normalize-path": "^3.0.0",
8081
"ora": "^5.1.0",
8182
"picocolors": "^1.1.1",
8283
"pofile": "^1.1.4",
8384
"pseudolocale": "^2.0.0",
84-
"source-map": "^0.8.0-beta.0"
85+
"source-map": "^0.8.0-beta.0",
86+
"threads": "^1.7.0"
8587
},
8688
"devDependencies": {
8789
"@lingui/jest-mocks": "*",
8890
"@types/convert-source-map": "^2.0.0",
8991
"@types/micromatch": "^4.0.1",
92+
"@types/ms": "^2.1.0",
9093
"@types/normalize-path": "^3.0.0",
9194
"mock-fs": "^5.2.0",
92-
"mockdate": "^3.0.5"
95+
"mockdate": "^3.0.5",
96+
"ts-node": "^10.9.2"
9397
}
9498
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export class ProgramExit extends Error {
2+
constructor() {
3+
super()
4+
5+
this.name = "ProgramExit"
6+
}
7+
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import mockFs from "mock-fs"
44
import { mockConsole } from "@lingui/jest-mocks"
55
import { LinguiConfig, makeConfig } from "@lingui/conf"
66

7-
import { Catalog, cleanObsolete, order } from "./catalog"
7+
import { Catalog, cleanObsolete, order, writeCompiled } from "./catalog"
88
import { createCompiledCatalog } from "./compile"
99

1010
import {
@@ -688,9 +688,9 @@ describe("writeCompiled", () => {
688688
async ({ namespace, extension }) => {
689689
const { source } = createCompiledCatalog("en", {}, { namespace })
690690
// Test that the file extension of the compiled catalog is `.mjs`
691-
expect(await catalog.writeCompiled("en", source, namespace)).toMatch(
692-
extension
693-
)
691+
expect(
692+
await writeCompiled(catalog.path, "en", source, namespace)
693+
).toMatch(extension)
694694
}
695695
)
696696
})

packages/cli/src/api/catalog.ts

Lines changed: 56 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@ import normalize from "normalize-path"
66
import { LinguiConfigNormalized, OrderBy } from "@lingui/conf"
77

88
import { FormatterWrapper } from "./formats"
9-
import { CliExtractOptions } from "../lingui-extract"
10-
import { CliExtractTemplateOptions } from "../lingui-extract-template"
119
import { CompiledCatalogNamespace } from "./compile"
1210
import {
1311
getTranslationsForCatalog,
1412
GetTranslationsOptions,
15-
TranslationMissingEvent,
1613
} from "./catalog/getTranslationsForCatalog"
1714
import { mergeCatalog } from "./catalog/mergeCatalog"
18-
import { extractFromFiles } from "./catalog/extractFromFiles"
15+
import {
16+
extractFromFiles,
17+
extractFromFilesWithWorkerPool,
18+
} from "./catalog/extractFromFiles"
1919
import {
2020
isDirectory,
2121
makePathRegexSafe,
@@ -24,16 +24,24 @@ import {
2424
writeFile,
2525
} from "./utils"
2626
import { AllCatalogsType, CatalogType, ExtractedCatalogType } from "./types"
27+
import { ExtractWorkerPool } from "./extractWorkerPool"
2728

2829
const LOCALE = "{locale}"
2930
const LOCALE_SUFFIX_RE = /\{locale\}.*$/
3031

31-
export type MakeOptions = CliExtractOptions & {
32+
export type MakeOptions = {
33+
files?: string[]
34+
clean: boolean
35+
overwrite: boolean
36+
locale: string[]
3237
orderBy?: OrderBy
38+
workerPool?: ExtractWorkerPool
3339
}
3440

35-
export type MakeTemplateOptions = CliExtractTemplateOptions & {
41+
export type MakeTemplateOptions = {
42+
files?: string[]
3643
orderBy?: OrderBy
44+
workerPool?: ExtractWorkerPool
3745
}
3846

3947
export type MergeOptions = {
@@ -81,7 +89,7 @@ export class Catalog {
8189

8290
async make(options: MakeOptions): Promise<AllCatalogsType | false> {
8391
const [nextCatalog, prevCatalogs] = await Promise.all([
84-
this.collect({ files: options.files }),
92+
this.collect({ files: options.files, workerPool: options.workerPool }),
8593
this.readAll(),
8694
])
8795

@@ -116,7 +124,10 @@ export class Catalog {
116124
async makeTemplate(
117125
options: MakeTemplateOptions
118126
): Promise<CatalogType | false> {
119-
const catalog = await this.collect({ files: options.files })
127+
const catalog = await this.collect({
128+
files: options.files,
129+
workerPool: options.workerPool,
130+
})
120131
if (!catalog) return false
121132
const sorted = order(options.orderBy, catalog as CatalogType)
122133

@@ -128,7 +139,7 @@ export class Catalog {
128139
* Collect messages from source paths. Return a raw message catalog as JSON.
129140
*/
130141
async collect(
131-
options: { files?: string[] } = {}
142+
options: { files?: string[]; workerPool?: ExtractWorkerPool } = {}
132143
): Promise<ExtractedCatalogType | undefined> {
133144
let paths = this.sourcePaths
134145
if (options.files) {
@@ -140,6 +151,14 @@ export class Catalog {
140151
paths = paths.filter((path: string) => regex.test(normalize(path)))
141152
}
142153

154+
if (options.workerPool) {
155+
return await extractFromFilesWithWorkerPool(
156+
options.workerPool,
157+
paths,
158+
this.config
159+
)
160+
}
161+
143162
return await extractFromFiles(paths, this.config)
144163
}
145164

@@ -181,23 +200,8 @@ export class Catalog {
181200
)
182201
}
183202

184-
async getTranslations(
185-
locale: string,
186-
options: Omit<GetTranslationsOptions, "onMissing">
187-
) {
188-
const missing: TranslationMissingEvent[] = []
189-
190-
const messages = await getTranslationsForCatalog(this, locale, {
191-
...options,
192-
onMissing: (event) => {
193-
missing.push(event)
194-
},
195-
})
196-
197-
return {
198-
missing,
199-
messages,
200-
}
203+
async getTranslations(locale: string, options: GetTranslationsOptions) {
204+
return await getTranslationsForCatalog(this, locale, options)
201205
}
202206

203207
async write(
@@ -217,40 +221,15 @@ export class Catalog {
217221
await this.format.write(filename, messages, undefined)
218222
}
219223

220-
async writeCompiled(
221-
locale: string,
222-
compiledCatalog: string,
223-
namespace?: CompiledCatalogNamespace
224-
) {
225-
let ext: string
226-
switch (namespace) {
227-
case "es":
228-
ext = "mjs"
229-
break
230-
case "ts":
231-
case "json":
232-
ext = namespace
233-
break
234-
default:
235-
ext = "js"
236-
}
237-
238-
const filename = `${replacePlaceholders(this.path, { locale })}.${ext}`
239-
await writeFile(filename, compiledCatalog)
240-
return filename
241-
}
242-
243224
async read(locale: string): Promise<CatalogType> {
244225
return await this.format.read(this.getFilename(locale), locale)
245226
}
246227

247-
async readAll(): Promise<AllCatalogsType> {
228+
async readAll(locales: string[] = this.locales): Promise<AllCatalogsType> {
248229
const res: AllCatalogsType = {}
249230

250231
await Promise.all(
251-
this.locales.map(
252-
async (locale) => (res[locale] = await this.read(locale))
253-
)
232+
locales.map(async (locale) => (res[locale] = await this.read(locale)))
254233
)
255234

256235
// statement above will save locales in object in undetermined order
@@ -364,6 +343,30 @@ function orderByOrigin<T extends ExtractedCatalogType>(messages: T): T {
364343
}, {} as T)
365344
}
366345

346+
export async function writeCompiled(
347+
path: string,
348+
locale: string,
349+
compiledCatalog: string,
350+
namespace?: CompiledCatalogNamespace
351+
) {
352+
let ext: string
353+
switch (namespace) {
354+
case "es":
355+
ext = "mjs"
356+
break
357+
case "ts":
358+
case "json":
359+
ext = namespace
360+
break
361+
default:
362+
ext = "js"
363+
}
364+
365+
const filename = `${replacePlaceholders(path, { locale })}.${ext}`
366+
await writeFile(filename, compiledCatalog)
367+
return filename
368+
}
369+
367370
export function orderByMessage<T extends ExtractedCatalogType>(messages: T): T {
368371
// hardcoded en-US locale to have consistent sorting
369372
// @see https://github.com/lingui/js-lingui/pull/1808

0 commit comments

Comments
 (0)