From 19df3ffc2a8e78b1068c1d27a0ffdbabd5e2324f Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Wed, 13 Nov 2024 15:24:20 +0100 Subject: [PATCH 1/5] feat!: generated files naming syntax convention BREAKING CHANGE: generated file naming syntax is now standard between all the commands, following the structure `..json`. Both filename and suffix are customizable and by default would be the command name and the spaceId respectively. Ex: `components.12345.json` --- README.md | 31 +++++++++ src/commands/pull-languages/actions.test.ts | 7 +- src/commands/pull-languages/actions.ts | 8 ++- src/commands/pull-languages/constants.ts | 30 +++++++++ src/commands/pull-languages/index.test.ts | 75 +++++++++++++++++++-- src/commands/pull-languages/index.ts | 11 +-- src/types/index.ts | 9 +++ 7 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 src/commands/pull-languages/constants.ts create mode 100644 src/types/index.ts diff --git a/README.md b/README.md index 4e78da0..b614027 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,37 @@ Will generate the languages in the `.storyblok/languages` directory. > [!TIP] > If you prefer to avoid pushing the `.storyblok` directory to your repository you can add it to your `.gitignore` file. +### Generated filename syntax conventions + +The generated files will now follow a more consistent naming convention. The files will be named using the following syntax: + +``` +.. +``` + +Where: + +- `` is the name of the file. Customizable by the user with the `--filename` flag +- `` is an optional suffix to differentiate the files. By default is going to be the `spaceId` and is customizable by the user with the `--suffix` flag +- `` is the file extension. By default is `json` (Not configurable) + +Example: + +```bash +storyblok pull-languages --space=12345 --filename=my-languages --suffix=dev +``` + +Will generate the languages in the following path `.storyblok/languages/my-languages.dev.json` + +If you would like to use a timestamp as the suffix you can use: + +```bash +storyblok pull-languages --space=12345 --filename=my-languages --suffix="$(date +%s)" +``` + +> [!WARNING] +> The `--filename` will be ignored in the case that `--separate-files` is used on the commands that supports it. + ## Setup First clone the repository and install the dependencies: diff --git a/src/commands/pull-languages/actions.test.ts b/src/commands/pull-languages/actions.test.ts index dbec82b..49d533d 100644 --- a/src/commands/pull-languages/actions.test.ts +++ b/src/commands/pull-languages/actions.test.ts @@ -84,7 +84,12 @@ describe('pull languages actions', () => { }, ], } - await saveLanguagesToFile('12345', mockResponse, '/temp') + await saveLanguagesToFile('12345', mockResponse, { + filename: 'languages', + path: '/temp', + verbose: false, + space: '12345', + }) const content = vol.readFileSync('/temp/languages.12345.json', 'utf8') expect(content).toBe(JSON.stringify(mockResponse, null, 2)) }) diff --git a/src/commands/pull-languages/actions.ts b/src/commands/pull-languages/actions.ts index 2ce4fa7..03b57d0 100644 --- a/src/commands/pull-languages/actions.ts +++ b/src/commands/pull-languages/actions.ts @@ -4,6 +4,7 @@ import { handleAPIError, handleFileSystemError } from '../../utils' import { ofetch } from 'ofetch' import { regionsDomain } from '../../constants' import { resolvePath, saveToFile } from '../../utils/filesystem' +import type { PullLanguagesOptions } from './constants' export interface SpaceInternationalizationOptions { languages: SpaceLanguage[] @@ -31,12 +32,13 @@ export const pullLanguages = async (space: string, token: string, region: string } } -export const saveLanguagesToFile = async (space: string, internationalizationOptions: SpaceInternationalizationOptions, path?: string) => { +export const saveLanguagesToFile = async (space: string, internationalizationOptions: SpaceInternationalizationOptions, options: PullLanguagesOptions) => { try { + const { filename = 'languages', suffix = space, path } = options const data = JSON.stringify(internationalizationOptions, null, 2) - const filename = `languages.${space}.json` + const name = `${filename}.${suffix}.json` const resolvedPath = resolvePath(path, 'languages') - const filePath = join(resolvedPath, filename) + const filePath = join(resolvedPath, name) await saveToFile(filePath, data) } diff --git a/src/commands/pull-languages/constants.ts b/src/commands/pull-languages/constants.ts new file mode 100644 index 0000000..c25a4c5 --- /dev/null +++ b/src/commands/pull-languages/constants.ts @@ -0,0 +1,30 @@ +import type { CommandOptions } from '../../types' + +/** + * Interface representing the options for the `pull-languages` command. + */ +export interface PullLanguagesOptions extends CommandOptions { + /** + * The path to save the languages file to. + * Defaults to `.storyblok/languages`. + * @default `.storyblok/languages` + */ + path?: string + /** + * The space ID. + * @required true + */ + space: string + /** + * The filename to save the file as. + * Defaults to `languages`. The file will be saved as `..json`. + * @default `languages + */ + filename?: string + /** + * The suffix to add to the filename. + * Defaults to the space ID. + * @default space + */ + suffix?: string +} diff --git a/src/commands/pull-languages/index.test.ts b/src/commands/pull-languages/index.test.ts index 1a2da18..7c2732f 100644 --- a/src/commands/pull-languages/index.test.ts +++ b/src/commands/pull-languages/index.test.ts @@ -88,7 +88,9 @@ describe('pullLanguages', () => { vi.mocked(pullLanguages).mockResolvedValue(mockResponse) await pullLanguagesCommand.parseAsync(['node', 'test', '--space', '12345']) expect(pullLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu') - expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, undefined) + expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, { + space: '12345', + }) expect(konsola.ok).toHaveBeenCalledWith(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(`.storyblok/languages/languages.12345.json`)}`) }) @@ -109,8 +111,6 @@ describe('pullLanguages', () => { } const mockError = new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`) - - console.log(pullLanguagesCommand) await pullLanguagesCommand.parseAsync(['node', 'test']) expect(konsola.error).toHaveBeenCalledWith(mockError, false) }) @@ -157,8 +157,75 @@ describe('pullLanguages', () => { vi.mocked(pullLanguages).mockResolvedValue(mockResponse) await pullLanguagesCommand.parseAsync(['node', 'test', '--space', '12345', '--path', '/tmp']) expect(pullLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu') - expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, '/tmp') + expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, { + path: '/tmp', + space: '12345', + }) expect(konsola.ok).toHaveBeenCalledWith(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(`/tmp/languages.12345.json`)}`) }) }) + + describe('--filename option', () => { + it('should save the file with the provided filename', async () => { + const mockResponse = { + default_lang_name: 'en', + languages: [ + { + code: 'ca', + name: 'Catalan', + }, + { + code: 'fr', + name: 'French', + }, + ], + } + session().state = { + isLoggedIn: true, + password: 'valid-token', + region: 'eu', + } + + vi.mocked(pullLanguages).mockResolvedValue(mockResponse) + await pullLanguagesCommand.parseAsync(['node', 'test', '--space', '12345', '--filename', 'custom-languages']) + expect(pullLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu') + expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, { + filename: 'custom-languages', + space: '12345', + }) + expect(konsola.ok).toHaveBeenCalledWith(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(`.storyblok/languages/custom-languages.12345.json`)}`) + }) + }) + + describe('--suffix option', () => { + it('should save the file with the provided suffix', async () => { + const mockResponse = { + default_lang_name: 'en', + languages: [ + { + code: 'ca', + name: 'Catalan', + }, + { + code: 'fr', + name: 'French', + }, + ], + } + session().state = { + isLoggedIn: true, + password: 'valid-token', + region: 'eu', + } + + vi.mocked(pullLanguages).mockResolvedValue(mockResponse) + await pullLanguagesCommand.parseAsync(['node', 'test', '--space', '12345', '--suffix', 'custom-suffix']) + expect(pullLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu') + expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, { + suffix: 'custom-suffix', + space: '12345', + }) + expect(konsola.ok).toHaveBeenCalledWith(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(`.storyblok/languages/languages.custom-suffix.json`)}`) + }) + }) }) diff --git a/src/commands/pull-languages/index.ts b/src/commands/pull-languages/index.ts index 727be6b..20ccefc 100644 --- a/src/commands/pull-languages/index.ts +++ b/src/commands/pull-languages/index.ts @@ -4,6 +4,7 @@ import { getProgram } from '../../program' import { session } from '../../session' import { pullLanguages, saveLanguagesToFile } from './actions' import chalk from 'chalk' +import type { PullLanguagesOptions } from './constants' const program = getProgram() // Get the shared singleton instance @@ -12,12 +13,14 @@ export const pullLanguagesCommand = program .description(`Download your space's languages schema as json`) .option('-s, --space ', 'space ID') .option('-p, --path ', 'path to save the file. Default is .storyblok/languages') - .action(async (options) => { + .option('-f, --filename ', 'filename to save the file as ..json') + .option('-su, --suffix ', 'suffix to add to the file name (e.g. languages..json). By default, the space ID is used.') + .action(async (options: PullLanguagesOptions) => { konsola.title(` ${commands.PULL_LANGUAGES} `, colorPalette.PULL_LANGUAGES, 'Pulling languages...') // Global options const verbose = program.opts().verbose // Command options - const { space, path } = options + const { space, path, filename = 'languages', suffix = options.space } = options const { state, initializeSession } = session() await initializeSession() @@ -38,8 +41,8 @@ export const pullLanguagesCommand = program konsola.warn(`No languages found in the space ${space}`) return } - await saveLanguagesToFile(space, internationalization, path) - konsola.ok(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(path ? `${path}/languages.${space}.json` : `.storyblok/languages/languages.${space}.json`)}`) + await saveLanguagesToFile(space, internationalization, options) + konsola.ok(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(path ? `${path}/${filename}.${suffix}.json` : `.storyblok/languages/${filename}.${suffix}.json`)}`) } catch (error) { handleError(error as Error, verbose) diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..05ce6d8 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,9 @@ +/** + * Interface representing the default options for a CLI command. + */ +export interface CommandOptions { + /** + * Indicates whether verbose output is enabled. + */ + verbose: boolean +} From 42b09f2fcea84ad2f8eb819a5f5890d9952a6f7d Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Wed, 27 Nov 2024 18:03:52 +0100 Subject: [PATCH 2/5] feat: remove strict write permission for generated files --- src/utils/filesystem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/filesystem.ts b/src/utils/filesystem.ts index c59244a..aed37a4 100644 --- a/src/utils/filesystem.ts +++ b/src/utils/filesystem.ts @@ -19,7 +19,7 @@ export const saveToFile = async (filePath: string, data: string) => { } try { - await writeFile(filePath, data, { mode: 0o600 }) + await writeFile(filePath, data) } catch (writeError) { handleFileSystemError('write', writeError as Error) From 65dc86c9de49fb2fecbb9bbbe29dec4fbaec5111 Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Wed, 27 Nov 2024 18:11:40 +0100 Subject: [PATCH 3/5] feat: remove unnecesary check on save files since recursive mkdir is used. --- src/utils/filesystem.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/utils/filesystem.ts b/src/utils/filesystem.ts index aed37a4..b45e60f 100644 --- a/src/utils/filesystem.ts +++ b/src/utils/filesystem.ts @@ -1,25 +1,23 @@ import { parse, resolve } from 'node:path' -import { access, constants, mkdir, writeFile } from 'node:fs/promises' +import { mkdir, writeFile } from 'node:fs/promises' import { handleFileSystemError } from './error/filesystem-error' export const saveToFile = async (filePath: string, data: string) => { - // Check if the path exists, and create it if it doesn't + // Get the directory path const resolvedPath = parse(filePath).dir + + // Ensure the directory exists try { - await access(resolvedPath, constants.F_OK) + await mkdir(resolvedPath, { recursive: true }) } - catch { - try { - await mkdir(resolvedPath, { recursive: true }) - } - catch (mkdirError) { - handleFileSystemError('mkdir', mkdirError as Error) - return // Exit early if the directory creation fails - } + catch (mkdirError) { + handleFileSystemError('mkdir', mkdirError as Error) + return // Exit early if the directory creation fails } + // Write the file try { - await writeFile(filePath, data) + await writeFile(filePath, data, { mode: 0o600 }) } catch (writeError) { handleFileSystemError('write', writeError as Error) From 36557ebe7e2bdc381a2c3f2ad2278e61d192182c Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Wed, 27 Nov 2024 18:29:33 +0100 Subject: [PATCH 4/5] feat: change argument syntax for suffix --- src/commands/pull-languages/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/pull-languages/index.ts b/src/commands/pull-languages/index.ts index 20ccefc..59fc3e8 100644 --- a/src/commands/pull-languages/index.ts +++ b/src/commands/pull-languages/index.ts @@ -14,7 +14,7 @@ export const pullLanguagesCommand = program .option('-s, --space ', 'space ID') .option('-p, --path ', 'path to save the file. Default is .storyblok/languages') .option('-f, --filename ', 'filename to save the file as ..json') - .option('-su, --suffix ', 'suffix to add to the file name (e.g. languages..json). By default, the space ID is used.') + .option('--su, --suffix ', 'suffix to add to the file name (e.g. languages..json). By default, the space ID is used.') .action(async (options: PullLanguagesOptions) => { konsola.title(` ${commands.PULL_LANGUAGES} `, colorPalette.PULL_LANGUAGES, 'Pulling languages...') // Global options From c66270c4c77ce2ea590b623132784ecad548d941 Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Fri, 29 Nov 2024 17:23:20 +0100 Subject: [PATCH 5/5] feat: remove again strict permissions for filesystem --- src/utils/filesystem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/filesystem.ts b/src/utils/filesystem.ts index b45e60f..5e9eaa0 100644 --- a/src/utils/filesystem.ts +++ b/src/utils/filesystem.ts @@ -17,7 +17,7 @@ export const saveToFile = async (filePath: string, data: string) => { // Write the file try { - await writeFile(filePath, data, { mode: 0o600 }) + await writeFile(filePath, data) } catch (writeError) { handleFileSystemError('write', writeError as Error)