Repository: napi-rs/napi-rs
File: cli/src/api/rename.ts (TypeScript)

Code before:
import { load as yamlParse, dump as yamlStringify } from 'js-yaml'
import { isNil, merge, omitBy, pick } from 'es-toolkit'
import { findUp } from 'find-up'

import { applyDefaultRenameOptions, RenameOptions } from '../def/rename.js'
import { readConfig, readFileAsync, writeFileAsync } from '../utils/index.js'

export async function renameProject(userOptions: RenameOptions) {
  const options = applyDefaultRenameOptions(userOptions)
  const napiConfig = await readConfig(options)
  const oldName = napiConfig.binaryName

  const packageJsonPath = resolve(options.cwd, options.packageJsonPath)
  const cargoTomlPath = resolve(options.cwd, options.manifestPath)

  const packageJsonContent = await readFileAsync(packageJsonPath, 'utf8')
  const packageJsonData = JSON.parse(packageJsonContent)

  merge(
    merge(
      packageJsonData,
      // @ts-expect-error missing fields: author and license
      omitBy(
        pick(options, ['name', 'description', 'author', 'license']),
        isNil,
      ),
    ),
    {
      napi: omitBy(
        {
          binaryName: options.binaryName,
          packageName: options.packageName,
        },
        isNil,
      ),
    },
  )

  if (options.configPath) {
    const configPath = resolve(options.cwd, options.configPath)
    const configContent = await readFileAsync(configPath, 'utf8')
    const configData = JSON.parse(configContent)
    configData.binaryName = options.binaryName
    configData.packageName = options.packageName
    await writeFileAsync(configPath, JSON.stringify(configData, null, 2))
  }

  await writeFileAsync(
    packageJsonPath,
    JSON.stringify(packageJsonData, null, 2),
  )

Review comment (suggestion):
There's an extra `merge` call that creates nested merge operations. The outer `merge` call on line 24 should be removed, keeping only the inner merge operation.

Code after:
import { load as yamlParse, dump as yamlStringify } from 'js-yaml'
import { isNil, merge, omitBy, pick } from 'es-toolkit'
import { findUp } from 'find-up'

import { applyDefaultRenameOptions, RenameOptions } from '../def/rename.js'
import { readConfig, readFileAsync, writeFileAsync } from '../utils/index.js'

export async function renameProject(userOptions: RenameOptions) {
  const options = applyDefaultRenameOptions(userOptions)
  const napiConfig = await readConfig(options)
  const oldName = napiConfig.binaryName

  const packageJsonPath = resolve(options.cwd, options.packageJsonPath)
  const cargoTomlPath = resolve(options.cwd, options.manifestPath)

  const packageJsonContent = await readFileAsync(packageJsonPath, 'utf8')
  const packageJsonData = JSON.parse(packageJsonContent)

  merge(
    merge(
      packageJsonData,
      omitBy(
        // @ts-expect-error missing fields: author and license
        pick(options, ['name', 'description', 'author', 'license']),
        isNil,
      ),
    ),
    {
      napi: omitBy(
        {
          binaryName: options.binaryName,
          packageName: options.packageName,
        },
        isNil,
      ),
    },
  )

  if (options.configPath) {
    const configPath = resolve(options.cwd, options.configPath)
    const configContent = await readFileAsync(configPath, 'utf8')
    const configData = JSON.parse(configContent)
    configData.binaryName = options.binaryName
    configData.packageName = options.packageName
    await writeFileAsync(configPath, JSON.stringify(configData, null, 2))
  }

  await writeFileAsync(
    packageJsonPath,
    JSON.stringify(packageJsonData, null, 2),
  )