{"version":3,"file":"packageManager.mjs","names":[],"sources":["../../../../src/init/utils/packageManager.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { compareVersions } from '@intlayer/config/utils';\n\n/** Package managers supported for dependency installation. */\nexport type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Configuration for the syncJSON plugin injected into intlayer.config\n * when a compat i18n library is detected.\n */\nexport type CompatSyncConfig = {\n  /**\n   * Which sync plugin ingests the catalogs:\n   * - `'json'` → `syncJSON` from `@intlayer/sync-json-plugin` (default).\n   * - `'po'`   → `syncPO` from `@intlayer/sync-po-plugin` (lingui's default\n   *   format).\n   */\n  plugin?: 'json' | 'po';\n  /**\n   * JSON format matching the compat library's conventions. Ignored when\n   * `plugin` is `'po'` (PO catalogs are always serialized as gettext).\n   */\n  format: 'icu' | 'i18next' | 'vue-i18n';\n  /**\n   * Source path template using ${locale} and ${key} placeholders.\n   * Rendered as a template literal in the generated config.\n   */\n  sourceTemplate: string;\n  /**\n   * Force `splitKeys: true` in the generated `syncJSON(...)` call so each\n   * top-level key of a single per-locale file becomes its own dictionary.\n   *\n   * Set for libraries whose single `messages/${locale}.json` file groups\n   * namespaces by its first-level keys (`next-intl` / `use-intl`, where\n   * `useTranslations('Hero')` resolves to the `Hero` dictionary). Left\n   * undefined for libraries whose top-level keys are plain message keys\n   * (e.g. `i18next`, `react-intl`); for those, syncJSON's auto-detection\n   * (split only when the source has no `${key}` segment) stays in control.\n   *\n   * Only meaningful for flat templates (no `${key}` segment); it is dropped\n   * automatically when the resolved template addresses one namespace per file.\n   */\n  splitKeys?: boolean;\n};\n\n/**\n * Configuration for injecting a compat vite plugin into vite.config.\n * The plugin replaces the generic `intlayer` plugin for libraries that\n * require alias injection (e.g. `vue-i18n` → `@intlayer/vue-i18n`).\n */\nexport type CompatVitePluginConfig = {\n  /** Exported function name from the plugin package, e.g. `'vueI18nVitePlugin'`. */\n  pluginFunctionName: string;\n  /** Import path for the plugin package, e.g. `'@intlayer/vue-i18n/plugin'`. */\n  pluginPackageSource: string;\n  /**\n   * Set when the compat plugin is a drop-in replacement for an i18n library's\n   * own Vite plugin (e.g. lingui ships `@lingui/vite-plugin`). When the original\n   * import is present, init rewrites only that import's module source to\n   * `pluginPackageSource` — keeping the binding and its call site — instead of\n   * injecting a second import and appending another plugin to the array.\n   */\n  replacesVitePlugin?: {\n    /** Imported binding to keep, e.g. `'lingui'`. */\n    importName: string;\n    /** Original package source to rewrite, e.g. `'@lingui/vite-plugin'`. */\n    fromPackageSource: string;\n  };\n};\n\n/** Result of analyzing project dependencies for intlayer package gaps. */\nexport type IntlayerPackageAnalysis = {\n  /** Intlayer packages that are referenced but not yet installed. */\n  packagesToInstall: string[];\n  /** Intlayer dev packages that are referenced but not yet installed. */\n  devPackagesToInstall: string[];\n  /**\n   * syncJSON plugin configuration to inject when a compat i18n library is\n   * detected. Undefined when no compat library is present or format is not\n   * yet implemented.\n   */\n  compatSyncConfig: CompatSyncConfig | undefined;\n  /**\n   * Vite config plugin to inject when a vite-based compat library is\n   * detected. Undefined for Next.js/Nuxt-only compat libs or when no compat\n   * library requires alias injection.\n   */\n  compatVitePluginConfig: CompatVitePluginConfig | undefined;\n};\n\n/**\n * Detects the package manager in use by checking for lock files in the\n * project root. Falls back to npm when no lock file is found.\n */\nexport const detectPackageManager = (rootDir: string): PackageManager => {\n  if (\n    existsSync(join(rootDir, 'bun.lock')) ||\n    existsSync(join(rootDir, 'bun.lockb'))\n  ) {\n    return 'bun';\n  }\n  if (existsSync(join(rootDir, 'pnpm-lock.yaml'))) {\n    return 'pnpm';\n  }\n  if (existsSync(join(rootDir, 'yarn.lock'))) {\n    return 'yarn';\n  }\n  return 'npm';\n};\n\n/**\n * Returns the install command for the given package manager and package list.\n */\nconst buildInstallCommand = (\n  packageManager: PackageManager,\n  packages: string[],\n  isDev: boolean = false\n): string => {\n  const packageList = packages.join(' ');\n  switch (packageManager) {\n    case 'bun':\n      return `bun add ${isDev ? '-d ' : ''}${packageList}`;\n    case 'pnpm':\n      return `pnpm add ${isDev ? '-D ' : ''}${packageList}`;\n    case 'yarn':\n      return `yarn add ${isDev ? '-D ' : ''}${packageList}`;\n    case 'npm':\n      return `npm install ${isDev ? '-D ' : ''}${packageList}`;\n  }\n};\n\n/**\n * Analyzes existing project dependencies to determine which intlayer packages\n * are missing and what syncJSON configuration to inject when compat i18n\n * libraries are present.\n */\n/** Extra signals (from a filesystem scan) that refine compat detection. */\nexport type DetectMissingPackagesOptions = {\n  /**\n   * Catalog format detected for a lingui project (`'po'` or `'json'`), or\n   * `null`/undefined when none was found. Decides which sync plugin + dev\n   * dependency the lingui compat setup uses.\n   */\n  linguiCatalogFormat?: 'po' | 'json' | null;\n};\n\n/** An existing i18n library Intlayer ships a compat adapter for. */\nexport type CompatI18nLibrary = {\n  /** Human-readable name, used to report the detection back to the user. */\n  label: string;\n  /**\n   * Dependency names that reveal the library — both the upstream packages and\n   * the Intlayer adapters, so a project that already ran `init` is still\n   * recognized.\n   */\n  packages: readonly string[];\n};\n\n/**\n * Existing i18n libraries Intlayer can adapt, keyed by the dependencies that\n * reveal them. Mirrors the compat branches of\n * {@link detectMissingIntlayerPackages}: a library listed here is one that\n * detection will wire up on its own once its package is in `package.json`.\n */\nexport const COMPAT_I18N_LIBRARIES: readonly CompatI18nLibrary[] = [\n  {\n    label: 'i18next / react-i18next',\n    packages: [\n      'i18next',\n      'react-i18next',\n      '@intlayer/i18next',\n      '@intlayer/react-i18next',\n    ],\n  },\n  {\n    label: 'next-intl / use-intl',\n    packages: [\n      'next-intl',\n      'use-intl',\n      '@intlayer/next-intl',\n      '@intlayer/use-intl',\n    ],\n  },\n  {\n    label: 'vue-i18n',\n    packages: ['vue-i18n', '@intlayer/vue-i18n'],\n  },\n  {\n    label: '@nuxtjs/i18n',\n    packages: ['@nuxtjs/i18n', '@intlayer/nuxtjs-i18n'],\n  },\n  {\n    label: 'next-i18next',\n    packages: ['next-i18next', '@intlayer/next-i18next'],\n  },\n  {\n    label: 'next-translate',\n    packages: ['next-translate', '@intlayer/next-translate'],\n  },\n  {\n    label: 'react-intl',\n    packages: ['react-intl', '@intlayer/react-intl'],\n  },\n  {\n    label: 'Lingui',\n    packages: ['@lingui/core', '@lingui/react', '@intlayer/lingui'],\n  },\n  {\n    label: 'svelte-i18n',\n    packages: ['svelte-i18n', '@intlayer/svelte-i18n'],\n  },\n  {\n    label: '@ngneat/transloco',\n    packages: ['@ngneat/transloco', '@intlayer/transloco'],\n  },\n  {\n    label: '@ngx-translate/core',\n    packages: ['@ngx-translate/core', '@intlayer/ngx-translate'],\n  },\n  {\n    label: 'node-polyglot',\n    packages: ['node-polyglot', '@intlayer/polyglot'],\n  },\n  {\n    label: 'i18n-js',\n    packages: ['i18n-js', '@intlayer/i18n-js'],\n  },\n];\n\n/**\n * Returns the labels of the compat i18n libraries present in `dependencies`.\n *\n * Lets the init flow report (and branch on) the libraries it found without\n * asking the user, since {@link detectMissingIntlayerPackages} already derives\n * the adapters, sync plugin and config from the very same dependency map.\n *\n * @param dependencies - Merged dependencies of the target project.\n */\nexport const detectCompatI18nLibraries = (\n  dependencies: Record<string, string>\n): string[] =>\n  COMPAT_I18N_LIBRARIES.filter((library) =>\n    library.packages.some((packageName) => Boolean(dependencies[packageName]))\n  ).map((library) => library.label);\n\n/**\n * True when the project already lints, and so has something to plug the\n * Intlayer lint rules into.\n *\n * `eslint-plugin-intlayer` loads in both ESLint and oxlint, so either linter is\n * enough. Shared with the init flow so that installing the plugin and wiring up\n * its configuration are gated on exactly the same condition — a project that\n * does not lint is left alone entirely.\n *\n * @param dependencies - Merged dependencies of the target project.\n */\nexport const hasLintTooling = (dependencies: Record<string, string>): boolean =>\n  Boolean(dependencies.eslint) || Boolean(dependencies.oxlint);\n\nexport const detectMissingIntlayerPackages = (\n  allDependencies: Record<string, string>,\n  options: DetectMissingPackagesOptions = {}\n): IntlayerPackageAnalysis => {\n  const packagesToInstall: string[] = [];\n  const devPackagesToInstall: string[] = [];\n  let compatSyncConfig: CompatSyncConfig | undefined;\n  let compatVitePluginConfig: CompatVitePluginConfig | undefined;\n\n  const isInstalled = (packageName: string): boolean =>\n    Boolean(allDependencies[packageName]);\n\n  const addIfMissing = (packageName: string): void => {\n    if (!isInstalled(packageName)) {\n      packagesToInstall.push(packageName);\n    }\n  };\n\n  const addDevIfMissing = (packageName: string): void => {\n    if (!isInstalled(packageName)) {\n      devPackagesToInstall.push(packageName);\n    }\n  };\n\n  // Core package — always required\n  addIfMissing('intlayer');\n\n  const isReactNativeProject =\n    isInstalled('react-native') || isInstalled('expo');\n\n  // Framework-specific runtime integrations\n  if (isInstalled('next')) {\n    addIfMissing('next-intlayer');\n  } else if (isReactNativeProject) {\n    addIfMissing('react-native-intlayer');\n  } else if (isInstalled('react')) {\n    addIfMissing('react-intlayer');\n  }\n\n  if (isInstalled('svelte')) {\n    addIfMissing('svelte-intlayer');\n  }\n\n  if (isInstalled('solid-js')) {\n    addIfMissing('solid-intlayer');\n  }\n\n  if (isInstalled('@angular/core')) {\n    addIfMissing('angular-intlayer');\n  }\n\n  if (isInstalled('vue')) {\n    addIfMissing('vue-intlayer');\n  }\n\n  if (isInstalled('vite')) {\n    addIfMissing('vite-intlayer');\n  }\n\n  // Lint rules — only when the project already lints.\n  if (hasLintTooling(allDependencies)) {\n    addDevIfMissing('eslint-plugin-intlayer');\n  }\n\n  // -------------------------------------------------------------------------\n  // Compat adapters for existing i18n libraries.\n  //\n  // Detection order matters: more specific libraries are checked first so that\n  // `compatSyncConfig ??=` and `compatVitePluginConfig ??=` capture the most\n  // relevant match.  Libraries that only affect the Next.js or Nuxt config do\n  // not set `compatVitePluginConfig` (handled separately in init/index.ts).\n  // Libraries whose JSON format is not yet supported leave `compatSyncConfig`\n  // undefined so no syncJSON plugin is injected.\n  // -------------------------------------------------------------------------\n\n  // next-intl — next.js only, ICU format. Default layout is a single file per\n  // locale (`messages/${locale}.json`) whose top-level keys are namespaces;\n  // syncJSON `splitKeys` auto-detection (no `${key}` segment) turns each\n  // top-level key into its own dictionary. The exact path is refined from\n  // `i18n/request.ts` in init/index.ts when present.\n  if (isInstalled('next-intl') || isInstalled('@intlayer/next-intl')) {\n    addIfMissing('@intlayer/next-intl');\n    addIfMissing('next-intl');\n    compatSyncConfig ??= {\n      format: 'icu',\n      sourceTemplate: './messages/${locale}.json',\n      // next-intl groups namespaces by the first-level keys of one file.\n      splitKeys: true,\n    };\n    // next config handled via updateNextConfigForNextIntl in init/index.ts\n  }\n\n  // next-i18next — next.js only, i18next JSON format\n  if (isInstalled('next-i18next') || isInstalled('@intlayer/next-i18next')) {\n    addIfMissing('@intlayer/next-i18next');\n    addIfMissing('next-i18next');\n    compatSyncConfig ??= {\n      format: 'i18next',\n      sourceTemplate: './src/locales/${locale}/${key}.json',\n    };\n    // next config handled via updateNextConfigForNextI18next in init/index.ts\n  }\n\n  // next-translate — next.js only, i18next-style flat-namespace JSON\n  if (\n    isInstalled('next-translate') ||\n    isInstalled('@intlayer/next-translate')\n  ) {\n    addIfMissing('@intlayer/next-translate');\n    addIfMissing('next-translate');\n    compatSyncConfig ??= {\n      format: 'i18next',\n      sourceTemplate: './locales/${locale}/${key}.json',\n    };\n    // next config handled via updateNextConfigForNextTranslate in init/index.ts\n  }\n\n  // i18next — vite alias injection (`i18next` → `@intlayer/i18next`) so existing\n  // `import … from 'i18next'` is served by Intlayer without touching call sites.\n  if (isInstalled('i18next') || isInstalled('@intlayer/i18next')) {\n    addIfMissing('@intlayer/i18next');\n    // Ensure the required peer dependency is installed\n    addIfMissing('i18next');\n    compatSyncConfig ??= {\n      format: 'i18next',\n      sourceTemplate: './src/locales/${locale}/${key}.json',\n    };\n    // When react-i18next is also present, its wrapper plugin aliases both\n    // `react-i18next` and `i18next`, so it must win — only inject the bare\n    // i18next plugin when no React wrapper is installed.\n    if (\n      !isInstalled('react-i18next') &&\n      !isInstalled('@intlayer/react-i18next')\n    ) {\n      compatVitePluginConfig ??= {\n        pluginFunctionName: 'i18nextVitePlugin',\n        pluginPackageSource: '@intlayer/i18next/plugin',\n      };\n    }\n  }\n\n  // react-i18next — vite alias injection (`react-i18next` → `@intlayer/react-i18next`\n  // and `i18next` → `@intlayer/i18next`) so components keep importing from\n  // `react-i18next` unchanged.\n  if (isInstalled('react-i18next') || isInstalled('@intlayer/react-i18next')) {\n    addIfMissing('@intlayer/react-i18next');\n    // Ensure the required peer dependency is installed\n    addIfMissing('react-i18next');\n    compatSyncConfig ??= {\n      format: 'i18next',\n      sourceTemplate: './src/locales/${locale}/${key}.json',\n    };\n    compatVitePluginConfig ??= {\n      pluginFunctionName: 'reactI18nextVitePlugin',\n      pluginPackageSource: '@intlayer/react-i18next/plugin',\n    };\n  }\n\n  // vue-i18n — vite alias injection required\n  if (isInstalled('vue-i18n') || isInstalled('@intlayer/vue-i18n')) {\n    addIfMissing('@intlayer/vue-i18n');\n    addIfMissing('vue-i18n');\n    compatSyncConfig ??= {\n      format: 'vue-i18n',\n      sourceTemplate: './locales/${locale}/${key}.json',\n    };\n    compatVitePluginConfig ??= {\n      pluginFunctionName: 'vueI18nVitePlugin',\n      pluginPackageSource: '@intlayer/vue-i18n/plugin',\n    };\n  }\n\n  // use-intl — framework-agnostic React core of next-intl; vite alias\n  // injection required, ICU format. Commonly a single `messages/${locale}.json`\n  // file whose top-level keys are namespaces, handled by syncJSON `splitKeys`\n  // auto-detection (no `${key}` segment in the source template).\n  if (isInstalled('use-intl') || isInstalled('@intlayer/use-intl')) {\n    addIfMissing('@intlayer/use-intl');\n    addIfMissing('use-intl');\n    compatSyncConfig ??= {\n      format: 'icu',\n      sourceTemplate: './messages/${locale}.json',\n      // use-intl (the core of next-intl) uses the same single-file namespace model.\n      splitKeys: true,\n    };\n    compatVitePluginConfig ??= {\n      pluginFunctionName: 'useIntlVitePlugin',\n      pluginPackageSource: '@intlayer/use-intl/plugin',\n    };\n  }\n\n  // react-intl — vite alias injection required, ICU format\n  if (isInstalled('react-intl') || isInstalled('@intlayer/react-intl')) {\n    addIfMissing('@intlayer/react-intl');\n    addIfMissing('react-intl');\n    compatSyncConfig ??= {\n      format: 'icu',\n      sourceTemplate: './src/i18n/${locale}.json',\n    };\n    compatVitePluginConfig ??= {\n      pluginFunctionName: 'reactIntlVitePlugin',\n      pluginPackageSource: '@intlayer/react-intl/plugin',\n    };\n  }\n\n  // @ngneat/transloco — vite alias injection required\n  // @todo syncJSON format not yet implemented for transloco\n  if (isInstalled('@ngneat/transloco') || isInstalled('@intlayer/transloco')) {\n    addIfMissing('@intlayer/transloco');\n    addIfMissing('@ngneat/transloco');\n    compatVitePluginConfig ??= {\n      pluginFunctionName: 'translocoVitePlugin',\n      pluginPackageSource: '@intlayer/transloco/plugin',\n    };\n  }\n\n  // svelte-i18n — vite alias injection required, flat JSON (i18next-compatible)\n  if (isInstalled('svelte-i18n') || isInstalled('@intlayer/svelte-i18n')) {\n    addIfMissing('@intlayer/svelte-i18n');\n    addIfMissing('svelte-i18n');\n    compatSyncConfig ??= {\n      format: 'i18next',\n      sourceTemplate: './src/locales/${locale}.json',\n    };\n    compatVitePluginConfig ??= {\n      pluginFunctionName: 'svelteI18nVitePlugin',\n      pluginPackageSource: '@intlayer/svelte-i18n/plugin',\n    };\n  }\n\n  // node-polyglot — vite alias injection required\n  // @todo syncJSON format not yet implemented for polyglot\n  if (isInstalled('node-polyglot') || isInstalled('@intlayer/polyglot')) {\n    addIfMissing('@intlayer/polyglot');\n    addIfMissing('node-polyglot');\n    compatVitePluginConfig ??= {\n      pluginFunctionName: 'polyglotVitePlugin',\n      pluginPackageSource: '@intlayer/polyglot/plugin',\n    };\n  }\n\n  // @nuxtjs/i18n — nuxt module (no vite plugin), vue-i18n JSON format\n  if (isInstalled('@nuxtjs/i18n') || isInstalled('@intlayer/nuxtjs-i18n')) {\n    addIfMissing('@intlayer/nuxtjs-i18n');\n    addIfMissing('@nuxtjs/i18n');\n    compatSyncConfig ??= {\n      format: 'vue-i18n',\n      sourceTemplate: './locales/${locale}/${key}.json',\n    };\n    // nuxt config handled via updateNuxtConfigForNuxtjsI18n in init/index.ts\n  }\n\n  // @ngx-translate/core — vite alias injection required, flat JSON (i18next)\n  if (\n    isInstalled('@ngx-translate/core') ||\n    isInstalled('@intlayer/ngx-translate')\n  ) {\n    addIfMissing('@intlayer/ngx-translate');\n    addIfMissing('@ngx-translate/core');\n    compatSyncConfig ??= {\n      format: 'i18next',\n      sourceTemplate: './assets/i18n/${locale}.json',\n    };\n    compatVitePluginConfig ??= {\n      pluginFunctionName: 'ngxTranslateVitePlugin',\n      pluginPackageSource: '@intlayer/ngx-translate/plugin',\n    };\n  }\n\n  // @lingui/core — vite alias injection required.\n  // lingui keeps one catalog file per locale (default name `messages`), as\n  // `.po` (its default) or `.json`. The catalog filename is captured by the\n  // `${key}` segment so the produced dictionary key is `messages`, matching the\n  // fixed `messages` namespace the lingui compat runtime reads from.\n  if (\n    isInstalled('@lingui/core') ||\n    isInstalled('@lingui/react') ||\n    isInstalled('@intlayer/lingui')\n  ) {\n    addIfMissing('@intlayer/lingui');\n    addIfMissing('@lingui/core');\n\n    const linguiUsesPo = options.linguiCatalogFormat === 'po';\n    compatSyncConfig ??= linguiUsesPo\n      ? {\n          plugin: 'po',\n          format: 'icu',\n          sourceTemplate: './src/locales/${locale}/${key}.po',\n        }\n      : {\n          plugin: 'json',\n          format: 'icu',\n          sourceTemplate: './src/locales/${locale}/${key}.json',\n        };\n\n    compatVitePluginConfig ??= {\n      // `@intlayer/lingui/plugin` exports `lingui` as a drop-in replacement for\n      // `@lingui/vite-plugin`, so a fresh project gets `lingui()` injected and a\n      // project already using `@lingui/vite-plugin` only has its import source\n      // rewritten (see `replacesVitePlugin`).\n      pluginFunctionName: 'lingui',\n      pluginPackageSource: '@intlayer/lingui/plugin',\n      replacesVitePlugin: {\n        importName: 'lingui',\n        fromPackageSource: '@lingui/vite-plugin',\n      },\n    };\n  }\n\n  // i18n-js — vite alias injection required\n  // @todo syncJSON format not yet implemented for i18n-js\n  if (isInstalled('i18n-js') || isInstalled('@intlayer/i18n-js')) {\n    addIfMissing('@intlayer/i18n-js');\n    addIfMissing('i18n-js');\n    compatVitePluginConfig ??= {\n      pluginFunctionName: 'i18nJsVitePlugin',\n      pluginPackageSource: '@intlayer/i18n-js/plugin',\n    };\n  }\n\n  if (compatSyncConfig) {\n    addDevIfMissing(\n      compatSyncConfig.plugin === 'po'\n        ? '@intlayer/sync-po-plugin'\n        : '@intlayer/sync-json-plugin'\n    );\n  }\n\n  return {\n    packagesToInstall,\n    devPackagesToInstall,\n    compatSyncConfig,\n    compatVitePluginConfig,\n  };\n};\n\n/**\n * Runs the package install command synchronously.\n * Throws if the install process exits with a non-zero code.\n */\nexport const installPackages = (\n  rootDir: string,\n  packages: string[],\n  packageManager: PackageManager,\n  isDev: boolean = false\n): void => {\n  const command = buildInstallCommand(packageManager, packages, isDev);\n  execSync(command, { cwd: rootDir, stdio: 'inherit' });\n};\n\n/**\n * Determines whether a dependency name belongs to the Intlayer ecosystem.\n *\n * Matches the core `intlayer` package, every scoped `@intlayer/*` package\n * (including compat adapters such as `@intlayer/next-intl`) and the framework\n * runtime integrations that follow the `<framework>-intlayer` convention\n * (e.g. `next-intlayer`, `react-intlayer`, `express-intlayer`).\n */\nexport const isIntlayerPackageName = (packageName: string): boolean =>\n  packageName === 'intlayer' ||\n  packageName.startsWith('@intlayer/') ||\n  /-intlayer$/.test(packageName);\n\n/**\n * Reduces a semver range or full version to its `major.minor.patch` core,\n * stripping range prefixes (`^`, `~`), pre-release identifiers and build\n * metadata. Returns `null` when no `major.minor.patch` can be extracted.\n *\n * @example normalizeVersion('^9.0.0-canary.3') // '9.0.0'\n */\nexport const normalizeVersion = (version?: string): string | null => {\n  if (!version || typeof version !== 'string') return null;\n  const match = version.match(/(\\d+)\\.(\\d+)\\.(\\d+)/);\n  return match ? `${match[1]}.${match[2]}.${match[3]}` : null;\n};\n\n/**\n * Reads the installed version of a package from its `package.json` inside the\n * project's `node_modules`. Returns `null` when the package is not installed or\n * its manifest cannot be read.\n */\nexport const getInstalledPackageVersion = (\n  rootDir: string,\n  packageName: string\n): string | null => {\n  try {\n    const manifestPath = join(\n      rootDir,\n      'node_modules',\n      packageName,\n      'package.json'\n    );\n    if (!existsSync(manifestPath)) return null;\n    const { version } = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n    return typeof version === 'string' ? version : null;\n  } catch {\n    return null;\n  }\n};\n\n/**\n * Returns the Intlayer packages from `dependencies` whose installed version is\n * behind `targetVersion` (compared on `major.minor.patch`). Packages that are\n * not installed yet are ignored — those are handled by\n * {@link detectMissingIntlayerPackages}.\n */\nexport const detectOutdatedIntlayerPackages = (\n  rootDir: string,\n  dependencies: Record<string, string>,\n  targetVersion: string\n): string[] => {\n  const normalizedTarget = normalizeVersion(targetVersion);\n  if (!normalizedTarget) return [];\n\n  return Object.keys(dependencies)\n    .filter(isIntlayerPackageName)\n    .filter((packageName) => {\n      const installedVersion = getInstalledPackageVersion(rootDir, packageName);\n      const normalizedInstalled = normalizeVersion(\n        installedVersion ?? undefined\n      );\n      if (!normalizedInstalled) return false;\n      return compareVersions(normalizedInstalled, '<', normalizedTarget);\n    });\n};\n\n/**\n * Upgrades the given packages to `targetVersion` synchronously, preserving the\n * dependency type via the `isDev` flag. Throws if the install process exits\n * with a non-zero code.\n */\nexport const upgradePackages = (\n  rootDir: string,\n  packages: string[],\n  packageManager: PackageManager,\n  targetVersion: string,\n  isDev: boolean = false\n): void => {\n  if (packages.length === 0) return;\n  const versionedPackages = packages.map(\n    (packageName) => `${packageName}@${targetVersion}`\n  );\n  const command = buildInstallCommand(packageManager, versionedPackages, isDev);\n  execSync(command, { cwd: rootDir, stdio: 'inherit' });\n};\n"],"mappings":"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"}