diff --git a/src/commands/add.ts b/src/commands/add.ts index 3cee185..a3c3668 100644 --- a/src/commands/add.ts +++ b/src/commands/add.ts @@ -1,27 +1,36 @@ -import cli from 'cli-ux'; -import { Command } from '@oclif/command'; -import kleur from 'kleur'; +import { Command, flags } from '@oclif/command'; import { MasterFile } from '../models'; import { MasterFileAlreadyExistsError } from '../errors'; +import cli from 'cli-ux'; +import kleur from 'kleur'; export default class Add extends Command { static description = 'create and push a new master language file'; static args = [{ name: 'masterFile' }]; + static flags = { + configPath: flags.string({ + name: 'configPath', + required: false, + description: 'Path to wti-config.json file. If not provided, default to git root directory.' + }) + }; + static usage = '$ wti add locales/en/translation.json'; async run() { const { args: { masterFile }, + flags: { configPath } } = this.parse(Add); if (masterFile) { cli.action.start(`Adding ${masterFile} as a master file...`); try { - await MasterFile.create(masterFile); + await MasterFile.create(masterFile, configPath); cli.action.stop(); diff --git a/src/commands/addLocale.ts b/src/commands/addLocale.ts index aa15e84..d11cba6 100644 --- a/src/commands/addLocale.ts +++ b/src/commands/addLocale.ts @@ -1,26 +1,35 @@ -import cli from 'cli-ux'; -import { Command } from '@oclif/command'; -import kleur from 'kleur'; +import { Command, flags } from '@oclif/command'; import { Locale } from '../models'; +import cli from 'cli-ux'; +import kleur from 'kleur'; export default class AddLocale extends Command { static description = 'add a new locale to the project'; static args = [{ name: 'locale' }]; + static flags = { + configPath: flags.string({ + name: 'configPath', + required: false, + description: 'Path to wti-config.json file. If not provided, default to git root directory.' + }) + }; + static usage = '$ wti addLocale fr'; async run() { const { args: { locale }, + flags: { configPath } } = this.parse(AddLocale); if (locale) { cli.action.start(`Adding ${locale} as a locale...`); try { - await new Locale(locale).create(); + await new Locale(locale).create(configPath); cli.action.stop( `${kleur.green('[SUCCESS]')} Locale ${locale} is being added` diff --git a/src/commands/init.ts b/src/commands/init.ts index 141a9a4..29d3dfa 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -1,22 +1,31 @@ -import cli from 'cli-ux'; -import { Command } from '@oclif/command'; +import { Command, flags } from '@oclif/command'; +import { getConfig, initialConfig, updateConfig } from '../helpers'; import { ConfigNotFoundError } from '../errors'; -import { getConfig, updateConfig, initialConfig } from '../helpers'; +import cli from 'cli-ux'; export default class Init extends Command { static description = 'configure the project to sync with'; + static flags = { + configPath: flags.string({ + name: 'configPath', + required: false, + description: 'Path to wti-config.json file. If not provided, default to git root directory.' + }) + }; + async run() { + const { flags: { configPath } } = this.parse(Init); let config = initialConfig; try { // get config object - config = await getConfig(); + config = await getConfig(configPath); } catch (err) { // if it doesn't exist, create an empty one if (err instanceof ConfigNotFoundError) { - await updateConfig(config); + await updateConfig(config, configPath); } } finally { // if apiKey is set, ask if it's ok to override it @@ -39,7 +48,7 @@ export default class Init extends Command { project: { apiKey, }, - }); + }, configPath); cli.action.stop(); } diff --git a/src/commands/pull.ts b/src/commands/pull.ts index 0ea934f..1ba3b3a 100644 --- a/src/commands/pull.ts +++ b/src/commands/pull.ts @@ -1,14 +1,23 @@ -import { Command } from '@oclif/command'; -import kleur from 'kleur'; -import Listr from 'listr'; +import { Command, flags } from '@oclif/command'; +import { MasterFile, Project } from '../models'; -import { Project, MasterFile } from '../models'; +import Listr from 'listr'; +import kleur from 'kleur'; export default class Pull extends Command { static description = 'pull target language file(s)'; + static flags = { + configPath: flags.string({ + name: 'configPath', + required: false, + description: 'Path to wti-config.json file. If not provided, default to git root directory.' + }) + }; + async run() { - const { projectFiles } = await Project.init(); + const { flags: { configPath } } = this.parse(Pull); + const { projectFiles } = await Project.init(configPath); let masterFile = projectFiles.filter( (file) => file.master_project_file_id === null @@ -20,7 +29,7 @@ export default class Pull extends Command { (acc, current) => acc.concat({ title: `Pulling ${current.name}`, - task: async () => await new MasterFile(masterFileId).show(current), + task: async () => await new MasterFile(masterFileId).show(current, configPath), }), [] as Listr.ListrTask[] ) diff --git a/src/commands/push.ts b/src/commands/push.ts index 9193120..c894569 100644 --- a/src/commands/push.ts +++ b/src/commands/push.ts @@ -1,7 +1,7 @@ -import cli from 'cli-ux'; import { Command, flags } from '@oclif/command'; +import { MasterFile, Project } from '../models'; -import { Project, MasterFile } from '../models'; +import cli from 'cli-ux'; export default class Push extends Command { static description = 'push master language file'; @@ -22,11 +22,16 @@ export default class Push extends Command { // may be reversed with --no-ignore-missing to obsolete missing strings allowNo: true, }), + configPath: flags.string({ + name: 'configPath', + required: false, + description: 'Path to wti-config.json file. If not provided, default to git root directory.' + }) }; async run() { - const { projectFiles } = await Project.init(); - const { flags } = this.parse(Push); + const { flags : { configPath, ...flags } } = this.parse(Push); + const { projectFiles } = await Project.init(configPath); let masterFile = projectFiles.filter( (file) => file.master_project_file_id === null @@ -37,7 +42,8 @@ export default class Push extends Command { await new MasterFile(masterFile.id).update( masterFile.name, masterFile.locale_code, - flags + flags, + configPath ); cli.action.stop( diff --git a/src/commands/rm.ts b/src/commands/rm.ts index be8c239..596ceb2 100644 --- a/src/commands/rm.ts +++ b/src/commands/rm.ts @@ -1,19 +1,28 @@ +import { Command, flags } from '@oclif/command'; +import { MasterFile, Project } from '../models'; + import cli from 'cli-ux'; -import { Command } from '@oclif/command'; import kleur from 'kleur'; -import { MasterFile, Project } from '../models'; - export default class Rm extends Command { static description = 'delete a master language file from a project'; static args = [{ name: 'masterFile' }]; + static flags = { + configPath: flags.string({ + name: 'configPath', + required: false, + description: 'Path to wti-config.json file. If not provided, default to git root directory.' + }) + }; + static usage = '$ wti rm locales/en/translation.json'; async run() { const { args: { masterFile }, + flags: { configPath } } = this.parse(Rm); // TODO: inquirer to list master language files @@ -24,7 +33,7 @@ export default class Rm extends Command { if (remove) { cli.action.start(`Removing ${masterFile}..`); - const { projectMasterFiles } = await Project.init(); + const { projectMasterFiles } = await Project.init(configPath); const masterFileResult = projectMasterFiles.find( (file) => file.name === masterFile @@ -41,7 +50,7 @@ export default class Rm extends Command { process.exit(1); } - await new MasterFile(masterFileResult.id).remove(); + await new MasterFile(masterFileResult.id).remove(configPath); cli.action.stop(); } diff --git a/src/commands/rmLocale.ts b/src/commands/rmLocale.ts index 7ccf988..befa7e7 100644 --- a/src/commands/rmLocale.ts +++ b/src/commands/rmLocale.ts @@ -1,26 +1,35 @@ -import cli from 'cli-ux'; -import { Command } from '@oclif/command'; -import kleur from 'kleur'; +import { Command, flags } from '@oclif/command'; import { Locale } from '../models'; +import cli from 'cli-ux'; +import kleur from 'kleur'; export default class RmLocale extends Command { static description = 'delete a locale from a project'; static args = [{ name: 'locale' }]; + static flags = { + configPath: flags.string({ + name: 'configPath', + required: false, + description: 'Path to wti-config.json file. If not provided, default to git root directory.' + }) + }; + static usage = '$ wti rmLocale fr'; async run() { const { args: { locale }, + flags: { configPath } } = this.parse(RmLocale); if (locale) { cli.action.start(`Removing ${locale} as a locale...`); try { - await new Locale(locale).remove(); + await new Locale(locale).remove(configPath); cli.action.stop( `${kleur.green('[SUCCESS]')} Locale ${locale} is being removed` diff --git a/src/commands/status.ts b/src/commands/status.ts index 991930e..a101e7f 100644 --- a/src/commands/status.ts +++ b/src/commands/status.ts @@ -1,19 +1,28 @@ -import cli from 'cli-ux'; -import { Command } from '@oclif/command'; -import kleur from 'kleur'; +import { Command, flags } from '@oclif/command'; import { Status as StatusModel } from '../models'; +import cli from 'cli-ux'; +import kleur from 'kleur'; export default class Status extends Command { static description = 'fetch and display project statistics'; + static flags = { + configPath: flags.string({ + name: 'configPath', + required: false, + description: 'Path to wti-config.json file. If not provided, default to git root directory.' + }) + }; + static usage = '$ wti status'; async run() { cli.action.start(`Loading statistics...`); + const { flags: { configPath } } = this.parse(Status); try { - const stats = await new StatusModel().fetch(); + const stats = await new StatusModel().fetch(configPath); cli.action.stop(); const locales = Object.keys(stats); diff --git a/src/helpers/config.ts b/src/helpers/config.ts index e340064..e960801 100644 --- a/src/helpers/config.ts +++ b/src/helpers/config.ts @@ -1,13 +1,13 @@ -import fs from 'fs'; -import path from 'path'; -import gitRootDir from 'git-root-dir'; - import { ConfigNotFoundError, - NotGitDirectoryError, ConfigUpdateError, + NotGitDirectoryError, } from '../errors'; +import fs from 'fs'; +import gitRootDir from 'git-root-dir'; +import path from 'path'; + interface Config { project: { apiKey: string; @@ -22,14 +22,18 @@ export const initialConfig: Config = { /** * Return the path of the config file + * + * @param pathParam path to the config file if defined in the command. Overrides the default git root dir */ -export const getConfigPath = async () => { +export const getConfigPath = async (pathParam?: string) => { + if (pathParam) { + return path.join(process.cwd(), pathParam); + } + const rootDir = await gitRootDir(process.cwd()); if (rootDir) { - const configFilePath = path.join(rootDir, CONFIG_NAME); - - return Promise.resolve(configFilePath); + return path.join(rootDir, CONFIG_NAME); } throw new NotGitDirectoryError(); @@ -37,9 +41,11 @@ export const getConfigPath = async () => { /** * Return the config as a JSON object + * + * @param pathParam path to the config file if defined in the command. Overrides the default git root dir */ -export const getConfig = async () => { - const configPath = await getConfigPath(); +export const getConfig = async (pathParam?: string) => { + const configPath = await getConfigPath(pathParam); if (fs.existsSync(configPath)) { return (await require(configPath)) as Config; @@ -52,9 +58,10 @@ export const getConfig = async () => { * Update the config object * * @param newConfig new config object to write to file + * @param pathParam path to the config file if defined in the command. Overrides the default git root dir */ -export const updateConfig = async (newConfig: Config) => { - const configPath = await getConfigPath(); +export const updateConfig = async (newConfig: Config, pathParam?: string) => { + const configPath = await getConfigPath(pathParam); fs.writeFile(configPath, JSON.stringify(newConfig, null, 2), (err) => { if (err) throw new ConfigUpdateError(String(err)); diff --git a/src/helpers/wti.ts b/src/helpers/wti.ts index 0f59b2e..9357011 100644 --- a/src/helpers/wti.ts +++ b/src/helpers/wti.ts @@ -11,9 +11,10 @@ export type WtiErrorResponse = { * Send a GET request to the WTI API * * @param path path of the API call + * @param pathParam path where the config in located if defined in the command. Overrides the default git root dir */ -export const wtiGet = async (path: string) => { - const config = await getConfig(); +export const wtiGet = async (path: string, pathParam?: string) => { + const config = await getConfig(pathParam); return await fetch( `${WTI_API_URL}/${config.project.apiKey}${path}` @@ -25,9 +26,10 @@ export const wtiGet = async (path: string) => { * * @param path path of the API call * @param data data to be sent in the request body + * @param pathParam path where the config in located if defined in the command. Overrides the default git root dir */ -export const wtiPost = async (path: string, data: BodyInit) => { - const config = await getConfig(); +export const wtiPost = async (path: string, data: BodyInit, pathParam?: string) => { + const config = await getConfig(pathParam); return fetch( `${WTI_API_URL}/${config.project.apiKey}${path}`, @@ -43,9 +45,10 @@ export const wtiPost = async (path: string, data: BodyInit) => { * * @param path path of the API call * @param data data to be sent in the request body + * @param pathParam path where the config in located if defined in the command. Overrides the default git root dir */ -export const wtiPut = async (path: string, data: BodyInit) => { - const config = await getConfig(); +export const wtiPut = async (path: string, data: BodyInit, pathParam?: string) => { + const config = await getConfig(pathParam); return fetch( `${WTI_API_URL}/${config.project.apiKey}${path}`, @@ -60,9 +63,10 @@ export const wtiPut = async (path: string, data: BodyInit) => { * Send a DELETE request to the WTI API * * @param path path of the API call + * @param pathParam path where the config in located if defined in the command. Overrides the default git root dir */ -export const wtiDelete = async (path: string) => { - const config = await getConfig(); +export const wtiDelete = async (path: string, pathParam?: string) => { + const config = await getConfig(pathParam); return fetch( `${WTI_API_URL}/${config.project.apiKey}${path}`, diff --git a/src/models/Locale.ts b/src/models/Locale.ts index fd23bd4..e44a680 100644 --- a/src/models/Locale.ts +++ b/src/models/Locale.ts @@ -11,14 +11,14 @@ export class Locale { this._suffixDescription = suffixDescription || ''; } - async create() { + async create(pathParam?: string) { try { const formData = new FormData(); formData.append('id', this._locale); formData.append('suffix_code', this._suffixCode); formData.append('suffix_description', this._suffixDescription); - const response = await wtiPost('/locales', formData); + const response = await wtiPost('/locales', formData, pathParam); if (response.status !== 202) { const parsedResponse = (await response.json()) as WtiErrorResponse; @@ -33,11 +33,11 @@ export class Locale { } } - async remove() { + async remove(pathParam?: string) { try { // return 202 if success with empty body // return 404 with WtiErrorResponse if file not found - const response = await wtiDelete(`/locales/${this._locale}`); + const response = await wtiDelete(`/locales/${this._locale}`, pathParam); if (response.status !== 202) { const parsedResponse = (await response.json()) as WtiErrorResponse; diff --git a/src/models/MasterFile.ts b/src/models/MasterFile.ts index 851530d..3f294ba 100644 --- a/src/models/MasterFile.ts +++ b/src/models/MasterFile.ts @@ -49,7 +49,7 @@ export class MasterFile { this._masterFileId = masterFileId; } - public static async create(name: string) { + public static async create(name: string, pathParam?: string) { try { const file = fs.readFileSync(process.cwd() + `/${name}`); @@ -57,7 +57,7 @@ export class MasterFile { formData.append('file', new Blob([file]), name); formData.append('name', name); - const response = await wtiPost('/files', formData); + const response = await wtiPost('/files', formData, pathParam); if (!response.ok) { throw new Error(`Failed to push new master file: ${await response.text()}`); } @@ -77,10 +77,11 @@ export class MasterFile { } } - async show(file: WTIProjectFile) { + async show(file: WTIProjectFile, pathParam?: string) { try { const response = await wtiGet( - `/files/${this._masterFileId}/locales/${file.locale_code}` + `/files/${this._masterFileId}/locales/${file.locale_code}`, + pathParam ); if (!response.ok) { throw new Error(`Failed to get master file: ${await response.text()}`); @@ -105,7 +106,8 @@ export class MasterFile { merge: boolean; 'ignore-missing': boolean; label?: string; - } + }, + pathParam?: string ) { try { const file = fs.readFileSync(process.cwd() + `/${name}`); @@ -123,7 +125,7 @@ export class MasterFile { formData.append('label', options.label); } - const response = await wtiPut(`/files/${this._masterFileId}/locales/${locale}`, formData); + const response = await wtiPut(`/files/${this._masterFileId}/locales/${locale}`, formData, pathParam); if (!response.ok) { throw new Error(`Failed to get master file: ${await response.text()}`); } @@ -133,11 +135,11 @@ export class MasterFile { } } - async remove() { + async remove(pathParam?: string) { try { // return 202 if success with empty body // return 404 with WtiErrorResponse if file not found - const response = await wtiDelete(`/files/${this._masterFileId}`); + const response = await wtiDelete(`/files/${this._masterFileId}`, pathParam); if (response.status !== 202) { const parsedResponse = (await response.json()) as WtiErrorResponse; diff --git a/src/models/Project.ts b/src/models/Project.ts index 164790f..8fc4fed 100644 --- a/src/models/Project.ts +++ b/src/models/Project.ts @@ -1,4 +1,5 @@ -import { wtiGet, WtiErrorResponse } from '../helpers'; +import { WtiErrorResponse, wtiGet } from '../helpers'; + import type { WTIProject } from './types'; export class Project { @@ -10,10 +11,10 @@ export class Project { this._project = project; } - public static async init() { + public static async init(pathParam?: string) { if (!Project.instance) { try { - const response = await wtiGet('.json'); + const response = await wtiGet('.json', pathParam); // Project exists if (response.status === 200) { diff --git a/src/models/Status.ts b/src/models/Status.ts index 26d909d..9f22c8c 100644 --- a/src/models/Status.ts +++ b/src/models/Status.ts @@ -1,10 +1,11 @@ -import type { Statistic } from '../models/types'; -import { wtiGet, WtiErrorResponse } from '../helpers'; +import { WtiErrorResponse, wtiGet } from '../helpers'; + +import type { Statistic } from './types'; export class Status { - async fetch() { + async fetch(pathParam?: string) { try { - const response = await wtiGet('/stats.json'); + const response = await wtiGet('/stats.json', pathParam); if (response.status !== 200) { const parsedResponse = (await response.json()) as WtiErrorResponse;