From b54de902299d64e8e450669203a61b5116e8fcb2 Mon Sep 17 00:00:00 2001 From: Dan Drory <101684796+DanDroryAu@users.noreply.github.com> Date: Fri, 15 Nov 2024 11:13:12 +1100 Subject: [PATCH] feat: added ignore flag to vocab push (#282) --- .changeset/khaki-ravens-brake.md | 5 + .changeset/late-trainers-sparkle.md | 5 + README.md | 11 ++ .../phrase/src/ignore.vocab/translations.json | 5 + package.json | 1 + packages/cli/src/index.ts | 8 ++ packages/phrase/src/pull-translations.test.ts | 20 +++- packages/phrase/src/push-translations.test.ts | 105 +++++++++++++++++- packages/phrase/src/push-translations.ts | 11 +- 9 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 .changeset/khaki-ravens-brake.md create mode 100644 .changeset/late-trainers-sparkle.md create mode 100644 fixtures/phrase/src/ignore.vocab/translations.json diff --git a/.changeset/khaki-ravens-brake.md b/.changeset/khaki-ravens-brake.md new file mode 100644 index 00000000..12f8bcb7 --- /dev/null +++ b/.changeset/khaki-ravens-brake.md @@ -0,0 +1,5 @@ +--- +'@vocab/phrase': minor +--- + +Added a new `ignore` option to the `@vocab/phrase` `push` function. \ No newline at end of file diff --git a/.changeset/late-trainers-sparkle.md b/.changeset/late-trainers-sparkle.md new file mode 100644 index 00000000..1ae71ecd --- /dev/null +++ b/.changeset/late-trainers-sparkle.md @@ -0,0 +1,5 @@ +--- +'@vocab/cli': minor +--- + +Added a new `--ignore` flag on the cli. \ No newline at end of file diff --git a/README.md b/README.md index d6e570cf..85fcf9cc 100644 --- a/README.md +++ b/README.md @@ -721,6 +721,17 @@ referenced in the upload. These keys can be deleted from Phrase by providing the vocab push --branch my-branch --delete-unused-keys ``` +#### Ignoring Files + +The `ignore` key in your [Vocab config](#configuration) allows you to ignore certain files from being validated, compiled and uploaded. +However, in some cases you may only want certain files to be compiled and validated, but not uploaded, such as those present in a build output directory. +This can be accomplished by providing the `--ignore` flag to `vocab push`. +This flag accepts an array of glob patterns to ignore. + +```sh +vocab push --branch my-branch --ignore "**/dist/**" "**/another_ignored_directory/**" +``` + [phrase]: https://developers.phrase.com/api/ #### [Tags] diff --git a/fixtures/phrase/src/ignore.vocab/translations.json b/fixtures/phrase/src/ignore.vocab/translations.json new file mode 100644 index 00000000..5d22ba66 --- /dev/null +++ b/fixtures/phrase/src/ignore.vocab/translations.json @@ -0,0 +1,5 @@ +{ + "excluded": { + "message": "this is excluded" + } +} diff --git a/package.json b/package.json index 3b588143..7c060c4d 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "lint:manypkg": "manypkg check", "lint:prettier": "prettier --cache --check .", "lint:tsc": "tsc", + "changeset": "changeset", "release": "pnpm build && pnpm copy-readme-to-packages && changeset publish", "version": "changeset version && pnpm install --lockfile-only", "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest", diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 80b425cc..13bf423c 100755 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -13,6 +13,13 @@ const branchDefinition = { default: branch || 'local-development', } as const; +const ignorePathDefinition = { + type: 'string', + array: true, + describe: 'Array of glob paths to ignore when searching for keys to push', + default: [] as string[], +} as const; + let config: UserConfig | null = null; // eslint-disable-next-line @typescript-eslint/no-unused-expressions @@ -36,6 +43,7 @@ yargsCli(process.argv.slice(2)) describe: 'Whether or not to delete unused keys after pushing', default: false, }, + ignore: ignorePathDefinition, }), handler: async (options) => { await push(options, config!); diff --git a/packages/phrase/src/pull-translations.test.ts b/packages/phrase/src/pull-translations.test.ts index bf1a168f..6a63c19b 100644 --- a/packages/phrase/src/pull-translations.test.ts +++ b/packages/phrase/src/pull-translations.test.ts @@ -77,7 +77,7 @@ describe('pull translations', () => { it('should resolve', async () => { await expect(runPhrase(options)).resolves.toBeUndefined(); - expect(jest.mocked(writeFile)).toHaveBeenCalledTimes(2); + expect(jest.mocked(writeFile)).toHaveBeenCalledTimes(4); }); it('should update keys', async () => { @@ -91,6 +91,13 @@ describe('pull translations', () => { ), ).toMatchInlineSnapshot(` [ + { + "_meta": {}, + "excluded": { + "message": "this is excluded", + }, + }, + {}, { "_meta": { "tags": [ @@ -174,7 +181,7 @@ describe('pull translations', () => { it('should resolve', async () => { await expect(runPhrase(options)).resolves.toBeUndefined(); - expect(jest.mocked(writeFile)).toHaveBeenCalledTimes(2); + expect(jest.mocked(writeFile)).toHaveBeenCalledTimes(4); }); it('should update keys', async () => { @@ -188,6 +195,13 @@ describe('pull translations', () => { ), ).toMatchInlineSnapshot(` [ + { + "_meta": {}, + "excluded": { + "message": "this is excluded", + }, + }, + {}, { "_meta": { "tags": [ @@ -310,7 +324,7 @@ describe('pull translations', () => { new Error(`Missing translation for global key thanks in language fr`), ); - expect(jest.mocked(writeFile)).toHaveBeenCalledTimes(1); + expect(jest.mocked(writeFile)).toHaveBeenCalledTimes(3); }); }); }); diff --git a/packages/phrase/src/push-translations.test.ts b/packages/phrase/src/push-translations.test.ts index a1d7472c..d1bc2dad 100644 --- a/packages/phrase/src/push-translations.test.ts +++ b/packages/phrase/src/push-translations.test.ts @@ -16,9 +16,13 @@ jest.mock('./phrase-api', () => ({ const devLanguageUploadId = '1234'; -function runPhrase(config: { deleteUnusedKeys: boolean }) { +function runPhrase(config: { deleteUnusedKeys: boolean; ignore?: string[] }) { return push( - { branch: 'tester', deleteUnusedKeys: config.deleteUnusedKeys }, + { + branch: 'tester', + deleteUnusedKeys: config.deleteUnusedKeys, + ignore: config.ignore || [], + }, { devLanguage: 'en', languages: [{ name: 'en' }, { name: 'fr' }], @@ -74,6 +78,10 @@ describe('push', () => { "tags", ], }, + "excluded.ignore": { + "message": "this is excluded", + "tags": [], + }, "hello.mytranslations": { "message": "Hello", "tags": [ @@ -170,6 +178,10 @@ describe('push', () => { "tags", ], }, + "excluded.ignore": { + "message": "this is excluded", + "tags": [], + }, "hello.mytranslations": { "message": "Hello", "tags": [ @@ -245,4 +257,93 @@ describe('push', () => { }); }); }); + + describe('when ignore is ["**/ignore.vocab/**"]', () => { + const config = { deleteUnusedKeys: false, ignore: ['**/ignore.vocab/**'] }; + + beforeEach(() => { + jest.mocked(pushTranslations).mockClear(); + jest.mocked(writeFile).mockClear(); + jest.mocked(deleteUnusedKeys).mockClear(); + + jest + .mocked(pushTranslations) + .mockImplementation(() => Promise.resolve({ devLanguageUploadId })); + }); + + it('should resolve', async () => { + await expect(runPhrase(config)).resolves.toBeUndefined(); + + expect(jest.mocked(pushTranslations)).toHaveBeenCalledTimes(1); + }); + + it('should update keys', async () => { + await expect(runPhrase(config)).resolves.toBeUndefined(); + + expect(jest.mocked(pushTranslations).mock.calls[0][0]) + .toMatchInlineSnapshot(` + { + "en": { + "app.thanks.label": { + "globalKey": "app.thanks.label", + "message": "Thanks", + "tags": [ + "every", + "key", + "gets", + "these", + "tags", + ], + }, + "hello.mytranslations": { + "message": "Hello", + "tags": [ + "only for this key", + "greeting", + "every", + "key", + "gets", + "these", + "tags", + ], + }, + "profile.mytranslations": { + "message": "profil", + "tags": [ + "every", + "key", + "gets", + "these", + "tags", + ], + }, + "world.mytranslations": { + "message": "world", + "tags": [ + "every", + "key", + "gets", + "these", + "tags", + ], + }, + }, + "fr": { + "hello.mytranslations": { + "description": undefined, + "message": "Bonjour", + }, + "profile.mytranslations": { + "description": undefined, + "message": "profil", + }, + "world.mytranslations": { + "description": undefined, + "message": "monde", + }, + }, + } + `); + }); + }); }); diff --git a/packages/phrase/src/push-translations.ts b/packages/phrase/src/push-translations.ts index b8bd8584..4a1e1e77 100644 --- a/packages/phrase/src/push-translations.ts +++ b/packages/phrase/src/push-translations.ts @@ -15,6 +15,7 @@ import { trace } from './logger'; interface PushOptions { branch: string; deleteUnusedKeys?: boolean; + ignore?: string[]; } /** @@ -22,12 +23,18 @@ interface PushOptions { * A unique namespace is appended to each key using the file path the key came from. */ export async function push( - { branch, deleteUnusedKeys }: PushOptions, + { branch, deleteUnusedKeys, ignore }: PushOptions, config: UserConfig, ) { + if (ignore) { + trace(`ignoring files on paths: ${ignore.join(', ')}`); + } const allLanguageTranslations = await loadAllTranslations( { fallbacks: 'none', includeNodeModules: false, withTags: true }, - config, + { + ...config, + ignore: [...(config.ignore || []), ...(ignore || [])], + }, ); trace(`Pushing translations to branch ${branch}`); const allLanguages = config.languages.map((v) => v.name);