Skip to content

Commit 5158916

Browse files
feat(cli): add replexica lockfile for explicit lockfile generation (lingodotdev#162)
1 parent 6b0e1fb commit 5158916

6 files changed

Lines changed: 80 additions & 20 deletions

File tree

.changeset/red-books-complain.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@replexica/cli": patch
3+
"replexica": patch
4+
---
5+
6+
Add replexica lockfile command for explicit lockfile generation

packages/cli/src/cli/i18n.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,6 @@ export default new Command()
4444
ora.succeed('Replexica configuration loaded');
4545
}
4646

47-
// ora.start('Deleting redundant i18n files');
48-
// let i18nLocales = [i18nConfig.locale.source, ...i18nConfig.locale.targets]
49-
// for (const bucketPath of Object.keys(i18nConfig.buckets)) {
50-
// let directory = bucketPath.substring(0, bucketPath.lastIndexOf('/'));
51-
// let parentDir = path.join(process.cwd(), directory);
52-
53-
// fs.readdirSync(parentDir).forEach(file => {
54-
// let fileName = file.substring(file.lastIndexOf('/') + 1);
55-
// let locale = fileName.substring(0, fileName.indexOf('.'));
56-
// const exists = i18nLocales.includes(locale);
57-
// if (!exists) {
58-
// let dir = parentDir + "/" + file;
59-
// fs.unlinkSync(dir);
60-
// }
61-
// })
62-
// }
63-
// ora.succeed('Redundant i18n files deleted')
64-
6547
ora.start('Connecting to AI localization engine');
6648
const authenticator = createAuthenticator({
6749
apiKey: settings.auth.apiKey,

packages/cli/src/cli/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import authCmd from './auth';
88
import initCmd from './init';
99
import configCmd from './show';
1010
import i18nCmd from './i18n';
11-
import path from 'path';
12-
import fs from 'fs';
11+
import lockfileCmd from './lockfile';
1312

1413
import packageJson from '../../package.json';
1514

@@ -32,6 +31,7 @@ Website: https://replexica.com
3231
.addCommand(authCmd)
3332
.addCommand(initCmd)
3433
.addCommand(configCmd)
34+
.addCommand(lockfileCmd)
3535
.parse(process.argv)
3636
;
3737

packages/cli/src/cli/init.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ export default new Command()
2020

2121
spinner.succeed('Replexica project initialized');
2222
});
23+

packages/cli/src/cli/lockfile.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Command } from 'commander';
2+
import Z from 'zod';
3+
import Ora from 'ora';
4+
import { createLockfileProcessor } from '../workers/lockfile';
5+
import { bucketTypeSchema } from '@replexica/spec';
6+
import { loadConfig } from '../workers/config';
7+
import { createBucketLoader, expandPlaceholderedGlob } from '../workers/bucket/v2';
8+
9+
export default new Command()
10+
.command('lockfile')
11+
.description('Create a lockfile if it does not exist')
12+
.helpOption('-h, --help', 'Show help')
13+
.option('-f, --force', 'Force create a lockfile')
14+
.action(async (options) => {
15+
const flags = flagsSchema.parse(options);
16+
const ora = Ora().start('Creating lockfile');
17+
18+
const lockfileProcessor = createLockfileProcessor();
19+
const lockfileExists = await lockfileProcessor.exists();
20+
if (lockfileExists && !flags.force) {
21+
ora.warn(`Lockfile won't be created because it already exists. Use --force to overwrite.`);
22+
return;
23+
} else {
24+
await lockfileProcessor.delete();
25+
}
26+
27+
const i18nConfig = await loadConfig();
28+
29+
const placeholderedPathsTuples: [Z.infer<typeof bucketTypeSchema>, string][] = [];
30+
for (const [bucketType, bucketTypeParams] of Object.entries(i18nConfig?.buckets || {})) {
31+
const includedPlaceholderedPaths = bucketTypeParams.include
32+
.map((placeholderedGlob) => expandPlaceholderedGlob(placeholderedGlob, i18nConfig!.locale.source))
33+
.flat();
34+
const excludedPlaceholderedPaths = bucketTypeParams.exclude
35+
?.map((placeholderedGlob) => expandPlaceholderedGlob(placeholderedGlob, i18nConfig!.locale.source))
36+
.flat() || [];
37+
const finalPlaceholderedPaths = includedPlaceholderedPaths.filter((path) => !excludedPlaceholderedPaths.includes(path));
38+
for (const placeholderedPath of finalPlaceholderedPaths) {
39+
placeholderedPathsTuples.push([
40+
bucketType as Z.infer<typeof bucketTypeSchema>,
41+
placeholderedPath
42+
]);
43+
}
44+
}
45+
46+
for (const [bucketType, placeholderedPath] of placeholderedPathsTuples) {
47+
const sourcePayload = await createBucketLoader({
48+
bucketType,
49+
placeholderedPath,
50+
locale: i18nConfig!.locale.source,
51+
}).load();
52+
53+
const currentChecksums = await lockfileProcessor.createChecksums(sourcePayload);
54+
await lockfileProcessor.saveChecksums(placeholderedPath, currentChecksums);
55+
}
56+
57+
ora.succeed('Lockfile created');
58+
});
59+
60+
const flagsSchema = Z.object({
61+
force: Z.boolean().default(false),
62+
});

packages/cli/src/workers/lockfile.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ export type LockfilePayload = Z.infer<typeof LockfileSchema>;
99

1010
export function createLockfileProcessor() {
1111
return {
12+
async delete(): Promise<void> {
13+
const lockfilePath = _getLockfilePath();
14+
if (fs.existsSync(lockfilePath)) {
15+
fs.unlinkSync(lockfilePath);
16+
}
17+
},
18+
async exists(): Promise<boolean> {
19+
return fs.existsSync(_getLockfilePath());
20+
},
1221
async load(): Promise<LockfilePayload> {
1322
const lockfilePath = _getLockfilePath();
1423

0 commit comments

Comments
 (0)