Skip to content

Commit 91ca6da

Browse files
committed
feat: add unused locales
1 parent 3478f63 commit 91ca6da

File tree

7 files changed

+115
-16
lines changed

7 files changed

+115
-16
lines changed

packages/unused-i18n/src/lib/__tests__/search.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as fs from 'fs'
22
import * as path from 'path'
33
import { describe, expect, it, vi } from 'vitest'
4-
import { searchFilesRecursively } from "../search"
4+
import { searchFilesRecursively } from '../search'
55

66
vi.mock('fs')
77

@@ -22,16 +22,16 @@ describe('searchFilesRecursively', () => {
2222
})),
2323
readFileSync: vi.fn(filePath => {
2424
if (filePath === path.join(baseDir, 'file1.js')) {
25-
return `
25+
return `
2626
import { useI18n } from '@scaleway/use-i18n'
27-
`
28-
}
27+
`
28+
}
2929
if (filePath === path.join(baseDir, 'file2.js')) return 'no match here'
3030
if (filePath === path.join(baseDir, 'subdir', 'file3.js')) {
31-
return `
31+
return `
3232
import { useI18n } from '@scaleway/use-i18n'
33-
`
34-
}
33+
`
34+
}
3535

3636
return ''
3737
}),

packages/unused-i18n/src/lib/scopedNamespace/__tests__/extractNamespaceTranslation.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from 'vitest'
2-
import { extractNamespaceTranslation } from "../extractNamespaceTranslation"
2+
import { extractNamespaceTranslation } from '../extractNamespaceTranslation'
33

44
describe('extractNamespaceTranslation', () => {
55
it('should extract namespace translations correctly', () => {

packages/unused-i18n/src/lib/scopedNamespace/__tests__/extractScopedTs.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from 'vitest'
2-
import { extractScopedTs } from "../extractScopedTs"
2+
import { extractScopedTs } from '../extractScopedTs'
33

44
describe('extractScopedTs', () => {
55
it('should extract scoped translations correctly', () => {

packages/unused-i18n/src/utils/__tests__/getMissingTranslations.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, it, vi } from 'vitest'
2-
import { getMissingTranslations } from "../missingTranslations"
3-
import { shouldExclude } from "../shouldExclude"
2+
import { getMissingTranslations } from '../missingTranslations'
3+
import { shouldExclude } from '../shouldExclude'
44

55
vi.mock('../../utils/shouldExclude', () => ({
66
shouldExclude: vi.fn(),
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import * as fs from 'fs'
2+
import * as path from 'path'
3+
import { type BuildResult, build } from 'esbuild'
4+
import { beforeEach, describe, expect, it, vi } from 'vitest'
5+
import { loadConfig } from '../loadConfig'
6+
7+
vi.mock('fs')
8+
vi.mock('esbuild', () => ({
9+
build: vi.fn(),
10+
}))
11+
vi.mock('path')
12+
13+
describe('loadConfig', () => {
14+
beforeEach(() => {
15+
vi.resetAllMocks()
16+
vi.spyOn(process, 'cwd').mockReturnValue('/mocked/cwd')
17+
})
18+
19+
afterEach(() => {
20+
vi.restoreAllMocks()
21+
})
22+
23+
it('should load JSON config file correctly', async () => {
24+
const configPath = '/mocked/cwd/unused-i18n.config.json'
25+
const configContent = '{"test": "value"}'
26+
const existsSyncImpl = (filePath: string) => filePath === configPath
27+
28+
vi.mocked(() => path.resolve()).mockImplementation((...args: string[]) =>
29+
args.join('/'),
30+
)
31+
vi.mocked((arg: string) => path.extname(arg)).mockReturnValue('.json')
32+
33+
vi.mocked(fs.existsSync).mockImplementation(
34+
existsSyncImpl as typeof fs.existsSync,
35+
)
36+
vi.mocked(fs.readFileSync).mockReturnValue(configContent)
37+
38+
const config = await loadConfig()
39+
40+
expect(config).toEqual({ test: 'value' })
41+
expect(fs.existsSync).toHaveBeenCalledWith(configPath)
42+
expect(fs.readFileSync).toHaveBeenCalledWith(configPath, 'utf-8')
43+
})
44+
it('should load TypeScript config file correctly', async () => {
45+
const configPath = '/mocked/cwd/unused-i18n.config.ts'
46+
const jsCode = 'export default { test: "value" }'
47+
const buildResult = { outputFiles: [{ text: jsCode }] }
48+
const existsSyncImpl = (filePath: string) => filePath === configPath
49+
50+
vi.mocked(() => path.resolve()).mockImplementation((...args: string[]) =>
51+
args.join('/'),
52+
)
53+
vi.mocked((arg: string) => path.extname(arg)).mockReturnValue('.ts')
54+
55+
vi.mocked(fs.existsSync).mockImplementation(
56+
existsSyncImpl as typeof fs.existsSync,
57+
)
58+
vi.mocked(build).mockResolvedValue(buildResult as BuildResult)
59+
60+
const config = await loadConfig()
61+
expect(config).toEqual({ test: 'value' })
62+
expect(fs.existsSync).toHaveBeenCalledWith(configPath)
63+
expect(build).toHaveBeenCalledWith({
64+
entryPoints: [configPath],
65+
outfile: 'config.js',
66+
platform: 'node',
67+
format: 'esm',
68+
bundle: true,
69+
write: false,
70+
})
71+
})
72+
73+
it('should throw an error if no config file is found', async () => {
74+
vi.mocked(() => path.resolve()).mockImplementation((...args: string[]) =>
75+
args.join('/'),
76+
)
77+
78+
vi.mocked(fs.existsSync).mockReturnValue(false)
79+
80+
await expect(loadConfig()).rejects.toThrow(
81+
'Configuration file unused-i18n.config not found. Supported extensions: .json, .js, .cjs, .ts.',
82+
)
83+
84+
expect(fs.existsSync).toHaveBeenCalledTimes(4)
85+
expect(fs.existsSync).toHaveBeenCalledWith(
86+
'/mocked/cwd/unused-i18n.config.json',
87+
)
88+
expect(fs.existsSync).toHaveBeenCalledWith(
89+
'/mocked/cwd/unused-i18n.config.js',
90+
)
91+
expect(fs.existsSync).toHaveBeenCalledWith(
92+
'/mocked/cwd/unused-i18n.config.cjs',
93+
)
94+
expect(fs.existsSync).toHaveBeenCalledWith(
95+
'/mocked/cwd/unused-i18n.config.ts',
96+
)
97+
})
98+
})

packages/unused-i18n/src/utils/loadConfig.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as fs from 'fs'
2-
import * as path from 'path'
2+
import { extname, resolve } from 'path'
33
import { build } from 'esbuild'
44
import type { Config } from '../types'
55

@@ -10,7 +10,7 @@ export const loadConfig = async (): Promise<Config> => {
1010
let configPath = ''
1111

1212
for (const ext of supportedExtensions) {
13-
const potentialPath = path.resolve(cwd, `unused-i18n.config${ext}`)
13+
const potentialPath = resolve(cwd, `unused-i18n.config${ext}`)
1414
if (fs.existsSync(potentialPath)) {
1515
configPath = potentialPath
1616
break
@@ -23,13 +23,14 @@ export const loadConfig = async (): Promise<Config> => {
2323
)
2424
}
2525

26-
const extension = path.extname(configPath)
26+
const extension = extname(configPath)
2727

2828
if (extension === '.json') {
2929
const configContent = fs.readFileSync(configPath, 'utf-8')
3030

3131
return JSON.parse(configContent) as Config
3232
}
33+
3334
if (extension === '.ts') {
3435
const result = await build({
3536
entryPoints: [configPath],
@@ -41,7 +42,6 @@ export const loadConfig = async (): Promise<Config> => {
4142
})
4243

4344
const jsCode = result.outputFiles[0]?.text ?? ''
44-
4545
const module = (await import(
4646
`data:application/javascript,${encodeURIComponent(jsCode)}`
4747
)) as { default: Config }

packages/unused-i18n/src/utils/missingTranslations.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ export const getMissingTranslations = ({
66
localLines,
77
excludeKey,
88
extractedTranslations,
9-
}: GetMissingTranslationsArgs): string[] => localLines.filter(line => {
9+
}: GetMissingTranslationsArgs): string[] =>
10+
localLines.filter(line => {
1011
if (shouldExclude({ line, excludeKey })) {
1112
return false
1213
}

0 commit comments

Comments
 (0)