mirror of
https://github.com/logto-io/logto.git
synced 2025-03-10 22:22:45 -05:00
feat(cli): add option to skip core check in translate cli commands (#5085)
* feat(cli): add option to skip core check in translate cli commands * chore(cli): add changeset * feat(cli): support any string value package name in translate command * chore: update changeset
This commit is contained in:
parent
6b2e1c8908
commit
e4c73e7bb7
6 changed files with 61 additions and 40 deletions
5
.changeset/few-turtles-crash.md
Normal file
5
.changeset/few-turtles-crash.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@logto/cli": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add "--skip-core-check" option in translate cli commands, in order to support any arbitrary package name other than "phrases" and "phrases-experience".
|
|
@ -16,6 +16,11 @@ const translate: CommandModule = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
describe: 'The path to your Logto instance directory',
|
describe: 'The path to your Logto instance directory',
|
||||||
})
|
})
|
||||||
|
.option('skip-core-check', {
|
||||||
|
alias: 'sc',
|
||||||
|
type: 'boolean',
|
||||||
|
describe: 'Skip checking if the core package is existed',
|
||||||
|
})
|
||||||
.command(create)
|
.command(create)
|
||||||
.command(listTags)
|
.command(listTags)
|
||||||
.command(sync)
|
.command(sync)
|
||||||
|
|
|
@ -10,8 +10,15 @@ import { consoleLog, inquireInstancePath, lintLocaleFiles } from '../../../utils
|
||||||
import { parseLocaleFiles, syncPhraseKeysAndFileStructure } from './utils.js';
|
import { parseLocaleFiles, syncPhraseKeysAndFileStructure } from './utils.js';
|
||||||
|
|
||||||
const syncKeys: CommandModule<
|
const syncKeys: CommandModule<
|
||||||
{ path?: string },
|
{ path?: string; skipCoreCheck?: boolean },
|
||||||
{ path?: string; baseline: string; target: string; skipLint?: boolean; package: string }
|
{
|
||||||
|
path?: string;
|
||||||
|
skipCoreCheck?: boolean;
|
||||||
|
baseline: string;
|
||||||
|
target: string;
|
||||||
|
skipLint?: boolean;
|
||||||
|
package: string;
|
||||||
|
}
|
||||||
> = {
|
> = {
|
||||||
command: ['sync-keys', 'sk'],
|
command: ['sync-keys', 'sk'],
|
||||||
describe: [
|
describe: [
|
||||||
|
@ -31,7 +38,7 @@ const syncKeys: CommandModule<
|
||||||
.option('package', {
|
.option('package', {
|
||||||
alias: 'pkg',
|
alias: 'pkg',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
describe: 'The package name of the phrases, one of `phrases` or `phrases-experience`',
|
describe: 'The package name of the phrases, e.g. `phrases` or `phrases-experience`',
|
||||||
default: 'phrases',
|
default: 'phrases',
|
||||||
})
|
})
|
||||||
.option('target', {
|
.option('target', {
|
||||||
|
@ -40,13 +47,14 @@ const syncKeys: CommandModule<
|
||||||
describe: 'The target language tag, or `all` to sync all languages',
|
describe: 'The target language tag, or `all` to sync all languages',
|
||||||
})
|
})
|
||||||
.option('skip-lint', {
|
.option('skip-lint', {
|
||||||
alias: 's',
|
alias: 'sl',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
describe: 'Skip running `eslint --fix` for locales after syncing',
|
describe: 'Skip running `eslint --fix` for locales after syncing',
|
||||||
})
|
})
|
||||||
.demandOption(['baseline', 'target']),
|
.demandOption(['baseline', 'target']),
|
||||||
handler: async ({
|
handler: async ({
|
||||||
path: inputPath,
|
path: inputPath,
|
||||||
|
skipCoreCheck,
|
||||||
baseline: baselineTag,
|
baseline: baselineTag,
|
||||||
target: targetTag,
|
target: targetTag,
|
||||||
skipLint,
|
skipLint,
|
||||||
|
@ -64,11 +72,7 @@ const syncKeys: CommandModule<
|
||||||
consoleLog.fatal('Baseline and target cannot be the same');
|
consoleLog.fatal('Baseline and target cannot be the same');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packageName !== 'phrases' && packageName !== 'phrases-experience') {
|
const instancePath = await inquireInstancePath(inputPath, skipCoreCheck);
|
||||||
consoleLog.fatal('Invalid package name, expected `phrases` or `phrases-experience`');
|
|
||||||
}
|
|
||||||
|
|
||||||
const instancePath = await inquireInstancePath(inputPath);
|
|
||||||
const phrasesPath = path.join(instancePath, 'packages', packageName);
|
const phrasesPath = path.join(instancePath, 'packages', packageName);
|
||||||
const localesPath = path.join(phrasesPath, 'src/locales');
|
const localesPath = path.join(phrasesPath, 'src/locales');
|
||||||
const entrypoint = path.join(localesPath, baselineTag.toLowerCase(), 'index.ts');
|
const entrypoint = path.join(localesPath, baselineTag.toLowerCase(), 'index.ts');
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { languages } from '@logto/language-kit';
|
import { languages } from '@logto/language-kit';
|
||||||
import { isBuiltInLanguageTag as isPhrasesBuiltInLanguageTag } from '@logto/phrases';
|
import { isBuiltInLanguageTag as isPhrasesBuiltInLanguageTag } from '@logto/phrases';
|
||||||
import { isBuiltInLanguageTag as isPhrasesUiBuiltInLanguageTag } from '@logto/phrases-experience';
|
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
import type { CommandModule } from 'yargs';
|
import type { CommandModule } from 'yargs';
|
||||||
|
|
||||||
|
@ -8,13 +7,24 @@ import { inquireInstancePath, lintLocaleFiles } from '../../utils.js';
|
||||||
|
|
||||||
import { type TranslationOptions, baseLanguage, syncTranslation } from './utils.js';
|
import { type TranslationOptions, baseLanguage, syncTranslation } from './utils.js';
|
||||||
|
|
||||||
const sync: CommandModule<{ path?: string }, { path?: string }> = {
|
const sync: CommandModule<
|
||||||
|
{ path?: string; skipCoreCheck?: boolean },
|
||||||
|
{ path?: string; skipCoreCheck?: boolean; package: string }
|
||||||
|
> = {
|
||||||
command: ['sync'],
|
command: ['sync'],
|
||||||
describe:
|
describe:
|
||||||
'Translate all untranslated phrases using ChatGPT. Note the environment variable `OPENAI_API_KEY` is required to work.',
|
'Translate all untranslated phrases using ChatGPT. Note the environment variable `OPENAI_API_KEY` is required to work.',
|
||||||
handler: async ({ path: inputPath }) => {
|
builder: (yargs) =>
|
||||||
|
yargs.option('package', {
|
||||||
|
alias: 'pkg',
|
||||||
|
type: 'string',
|
||||||
|
describe: 'The package name of the phrases, e.g. `phrases` or `phrases-experience`',
|
||||||
|
default: 'phrases',
|
||||||
|
}),
|
||||||
|
handler: async ({ path: inputPath, skipCoreCheck, package: packageName }) => {
|
||||||
const queue = new PQueue({ concurrency: 5 });
|
const queue = new PQueue({ concurrency: 5 });
|
||||||
const instancePath = await inquireInstancePath(inputPath);
|
const instancePath = await inquireInstancePath(inputPath, skipCoreCheck);
|
||||||
|
const packages = packageName ? [packageName] : ['phrases', 'phrases-experience'];
|
||||||
|
|
||||||
for (const languageTag of Object.keys(languages)) {
|
for (const languageTag of Object.keys(languages)) {
|
||||||
if (languageTag === baseLanguage) {
|
if (languageTag === baseLanguage) {
|
||||||
|
@ -27,28 +37,22 @@ const sync: CommandModule<{ path?: string }, { path?: string }> = {
|
||||||
queue,
|
queue,
|
||||||
} satisfies Partial<TranslationOptions>;
|
} satisfies Partial<TranslationOptions>;
|
||||||
|
|
||||||
/* eslint-disable no-await-in-loop */
|
for (const packageName of packages) {
|
||||||
if (isPhrasesBuiltInLanguageTag(languageTag)) {
|
/* eslint-disable no-await-in-loop */
|
||||||
await syncTranslation({
|
if (isPhrasesBuiltInLanguageTag(languageTag)) {
|
||||||
...baseOptions,
|
await syncTranslation({
|
||||||
packageName: 'phrases',
|
...baseOptions,
|
||||||
languageTag,
|
packageName,
|
||||||
});
|
languageTag,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/* eslint-enable no-await-in-loop */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPhrasesUiBuiltInLanguageTag(languageTag)) {
|
|
||||||
await syncTranslation({
|
|
||||||
...baseOptions,
|
|
||||||
packageName: 'phrases-experience',
|
|
||||||
languageTag,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/* eslint-enable no-await-in-loop */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await queue.onIdle();
|
await queue.onIdle();
|
||||||
|
|
||||||
void lintLocaleFiles(instancePath);
|
void lintLocaleFiles(instancePath, packageName);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ export const readBaseLocaleFiles = async (directory: string): Promise<string[]>
|
||||||
|
|
||||||
export type TranslationOptions = {
|
export type TranslationOptions = {
|
||||||
instancePath: string;
|
instancePath: string;
|
||||||
packageName: 'phrases' | 'phrases-experience';
|
packageName: string;
|
||||||
languageTag: LanguageTag;
|
languageTag: LanguageTag;
|
||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
queue?: PQueue;
|
queue?: PQueue;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { promisify } from 'node:util';
|
||||||
|
|
||||||
import { ConsoleLog } from '@logto/shared';
|
import { ConsoleLog } from '@logto/shared';
|
||||||
import type { Optional } from '@silverhand/essentials';
|
import type { Optional } from '@silverhand/essentials';
|
||||||
import { assert, conditionalString } from '@silverhand/essentials';
|
import { assert, conditional, conditionalString } from '@silverhand/essentials';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import type { Progress } from 'got';
|
import type { Progress } from 'got';
|
||||||
import { got } from 'got';
|
import { got } from 'got';
|
||||||
|
@ -197,9 +197,9 @@ const validatePath = async (value: string) => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const inquireInstancePath = async (initialPath?: string) => {
|
export const inquireInstancePath = async (initialPath?: string, skipCoreCheck?: boolean) => {
|
||||||
const inquire = async () => {
|
const inquire = async () => {
|
||||||
if (!initialPath && (await validatePath('.')) === true) {
|
if (!initialPath && (skipCoreCheck ?? (await validatePath('.')) === true)) {
|
||||||
return path.resolve('.');
|
return path.resolve('.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ export const inquireInstancePath = async (initialPath?: string) => {
|
||||||
type: 'input',
|
type: 'input',
|
||||||
default: defaultPath,
|
default: defaultPath,
|
||||||
filter: (value: string) => value.trim(),
|
filter: (value: string) => value.trim(),
|
||||||
validate: validatePath,
|
validate: conditional(!skipCoreCheck && validatePath),
|
||||||
},
|
},
|
||||||
{ instancePath: initialPath }
|
{ instancePath: initialPath }
|
||||||
);
|
);
|
||||||
|
@ -225,10 +225,13 @@ export const inquireInstancePath = async (initialPath?: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const instancePath = await inquire();
|
const instancePath = await inquire();
|
||||||
const validated = await validatePath(instancePath);
|
|
||||||
|
|
||||||
if (validated !== true) {
|
if (!skipCoreCheck) {
|
||||||
consoleLog.fatal(validated);
|
const validated = await validatePath(instancePath);
|
||||||
|
|
||||||
|
if (validated !== true) {
|
||||||
|
consoleLog.fatal(validated);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return instancePath;
|
return instancePath;
|
||||||
|
@ -274,8 +277,8 @@ const execPromise = promisify(execFile);
|
||||||
export const lintLocaleFiles = async (
|
export const lintLocaleFiles = async (
|
||||||
/** Logto instance path */
|
/** Logto instance path */
|
||||||
instancePath: string,
|
instancePath: string,
|
||||||
/** Target package name, ignore to lint both packages */
|
/** Target package name, ignore to lint both `phrases` and `phrases-experience` packages */
|
||||||
packageName?: 'phrases' | 'phrases-experience'
|
packageName?: string
|
||||||
) => {
|
) => {
|
||||||
const spinner = ora({
|
const spinner = ora({
|
||||||
text: 'Running `eslint --fix` for locales',
|
text: 'Running `eslint --fix` for locales',
|
||||||
|
|
Loading…
Add table
Reference in a new issue