Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 156 additions & 116 deletions docs/005-CODE-REVIEW-IMPROVEMENTS.md

Large diffs are not rendered by default.

7 changes: 0 additions & 7 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,4 @@ export default [
},
...tseslint.configs.recommended,
prettier,
{
// Test files use Chai's expect().to.be.true style which triggers this rule
files: ["test/**/*.ts"],
rules: {
"@typescript-eslint/no-unused-expressions": "off",
},
},
];
3 changes: 1 addition & 2 deletions src/cli/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { CLI_BIN } from '../constants.js'
import type { CommandConfig } from './types.js'

const CLI_BIN = 'rn-toolbox'

/**
* Generates help text for a single command
*/
Expand Down
7 changes: 6 additions & 1 deletion src/commands/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ The template icon file should be at least 1024x1024px.`,
public async execute(parsed: ParsedArgs): Promise<void> {
const { args, flags } = parsed
const file = args.file!
const appName = flags.appName as string | undefined
const appName = typeof flags.appName === 'string' ? flags.appName : undefined

const sourceFilesExists = checkAssetFile(file)
if (!sourceFilesExists) {
Expand All @@ -89,6 +89,10 @@ The template icon file should be at least 1024x1024px.`,
for (const err of this.errors) {
this.log(` - ${err}`)
}
this.error(
`Failed to generate ${this.errors.length} asset(s)`,
ExitCode.GENERATION_ERROR
)
}

this.log(green('✔'), `Generated icons for '${cyan(appName)}' app.`)
Expand Down Expand Up @@ -145,6 +149,7 @@ The template icon file should be at least 1024x1024px.`,
private async generateAndroidIconsWithDensity(inputPath: string, outputDir: string, density: string, size: number) {
const densityFolderPath = join(outputDir, `mipmap-${density}`)

// Safe for concurrent execution - mkdir with recursive:true is idempotent
await mkdirp(densityFolderPath)

this.logVerbose(yellow('≈'), cyan('Android'), `Generating icons for density '${density}'...`)
Expand Down
6 changes: 5 additions & 1 deletion src/commands/splash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The template splashscreen file should be at least 1242x2208px.`,
public async execute(parsed: ParsedArgs): Promise<void> {
const { args, flags } = parsed
const file = args.file!
const appName = flags.appName as string | undefined
const appName = typeof flags.appName === 'string' ? flags.appName : undefined

const sourceFilesExists = checkAssetFile(file)
if (!sourceFilesExists) {
Expand All @@ -88,6 +88,10 @@ The template splashscreen file should be at least 1242x2208px.`,
for (const err of this.errors) {
this.log(` - ${err}`)
}
this.error(
`Failed to generate ${this.errors.length} asset(s)`,
ExitCode.GENERATION_ERROR
)
}

this.log(green('✔'), `Generated splashscreens for '${cyan(appName)}' app.`)
Expand Down
5 changes: 5 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

import type { IconSizeAndroid, IconSizeIOS, SplashscreenSize } from "./types.js"

/**
* CLI binary name
*/
export const CLI_BIN = 'rn-toolbox'

/**
* Android Assets Sizes
*/
Expand Down
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* Copyright (c) 2025 ForWarD Software (https://forwardsoftware.solutions/)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

export interface SplashscreenSize {
density?: string;
height: number;
Expand Down
10 changes: 10 additions & 0 deletions src/utils/app.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@

import {readFile} from 'node:fs/promises'

/**
* Extracts the app name from app.json in the current directory.
*
* @returns The app name if found and valid, undefined otherwise
* @example
* const name = await extractAppName()
* if (name) {
* console.log(`Found app: ${name}`)
* }
*/
export async function extractAppName(): Promise<string | undefined> {
try {
const content = await readFile('./app.json', {encoding: 'utf8'})
Expand Down
11 changes: 11 additions & 0 deletions src/utils/file-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,21 @@
import { existsSync } from 'node:fs'
import { mkdir } from 'node:fs/promises'

/**
* Checks if an asset file exists at the specified path.
*
* @param filePath - The path to the asset file
* @returns true if the file exists, false otherwise
*/
export function checkAssetFile(filePath: string): boolean {
return existsSync(filePath)
}

/**
* Creates a directory and all necessary parent directories.
*
* @param path - The path to create
*/
export async function mkdirp(path: string): Promise<void> {
await mkdir(path, { recursive: true })
}
2 changes: 1 addition & 1 deletion test/e2e/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ describe('CLI E2E', () => {
})

// Command completes but reports failures
assert.equal(exitCode, ExitCode.SUCCESS)
assert.equal(exitCode, ExitCode.GENERATION_ERROR)
assert.ok(stdout.includes('Warning') || stdout.includes('Failed'))
assert.ok(stdout.includes('asset') || stdout.includes('generate'))
})
Expand Down
Loading