@intlayer/engine 9.6.0 → 9.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/build.cjs +12 -2
- package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs +12 -11
- package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/index.cjs +4 -1
- package/dist/cjs/buildIntlayerDictionary/rebuildUnmergedDictionaries.cjs +23 -0
- package/dist/cjs/buildIntlayerDictionary/rebuildUnmergedDictionaries.cjs.map +1 -0
- package/dist/cjs/cli.cjs +10 -0
- package/dist/cjs/fetchDistantDictionaries.cjs.map +1 -1
- package/dist/cjs/handleAdditionalContentDeclarationFile.cjs +1 -1
- package/dist/cjs/handleContentDeclarationFileChange.cjs +1 -1
- package/dist/cjs/handleUnlinkedContentDeclarationFile.cjs +1 -1
- package/dist/cjs/init/cms.cjs +18 -0
- package/dist/cjs/init/cms.cjs.map +1 -1
- package/dist/cjs/init/index.cjs +11 -8
- package/dist/cjs/init/index.cjs.map +1 -1
- package/dist/cjs/init/utils/configManipulation.cjs +28 -0
- package/dist/cjs/init/utils/configManipulation.cjs.map +1 -1
- package/dist/cjs/init/utils/contentLayout.cjs +61 -0
- package/dist/cjs/init/utils/contentLayout.cjs.map +1 -0
- package/dist/cjs/init/utils/index.cjs +1 -0
- package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
- package/dist/cjs/listDictionariesPath.cjs +2 -5
- package/dist/cjs/listDictionariesPath.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/index.cjs +1 -1
- package/dist/cjs/loadDictionaries/loadDictionaries.cjs +1 -1
- package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs.map +1 -1
- package/dist/cjs/utils/buildComponentFilesList.cjs +18 -30
- package/dist/cjs/utils/buildComponentFilesList.cjs.map +1 -1
- package/dist/cjs/utils/chunkJSON.cjs +1 -1
- package/dist/cjs/utils/chunkJSON.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +2 -0
- package/dist/cjs/utils/isDirectoryExcluded.cjs +36 -0
- package/dist/cjs/utils/isDirectoryExcluded.cjs.map +1 -0
- package/dist/cjs/watcher.cjs +5 -2
- package/dist/cjs/watcher.cjs.map +1 -1
- package/dist/cjs/writeConfiguration/index.cjs +23 -0
- package/dist/cjs/writeConfiguration/index.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/index.cjs +6 -0
- package/dist/cjs/writeContentDeclaration/processContentDeclarationContent.cjs +20 -13
- package/dist/cjs/writeContentDeclaration/processContentDeclarationContent.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/renderContentDeclaration.cjs +213 -0
- package/dist/cjs/writeContentDeclaration/renderContentDeclaration.cjs.map +1 -0
- package/dist/cjs/writeContentDeclaration/writeContentDeclaration.cjs +5 -51
- package/dist/cjs/writeContentDeclaration/writeContentDeclaration.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs +2 -31
- package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/writeYamlFile.cjs +2 -8
- package/dist/cjs/writeContentDeclaration/writeYamlFile.cjs.map +1 -1
- package/dist/esm/build.mjs +7 -5
- package/dist/esm/buildIntlayerDictionary/buildIntlayerDictionary.mjs +12 -12
- package/dist/esm/buildIntlayerDictionary/buildIntlayerDictionary.mjs.map +1 -1
- package/dist/esm/buildIntlayerDictionary/index.mjs +3 -2
- package/dist/esm/buildIntlayerDictionary/rebuildUnmergedDictionaries.mjs +22 -0
- package/dist/esm/buildIntlayerDictionary/rebuildUnmergedDictionaries.mjs.map +1 -0
- package/dist/esm/cli.mjs +3 -1
- package/dist/esm/fetchDistantDictionaries.mjs.map +1 -1
- package/dist/esm/handleAdditionalContentDeclarationFile.mjs +1 -1
- package/dist/esm/handleContentDeclarationFileChange.mjs +1 -1
- package/dist/esm/handleUnlinkedContentDeclarationFile.mjs +1 -1
- package/dist/esm/init/cms.mjs +19 -2
- package/dist/esm/init/cms.mjs.map +1 -1
- package/dist/esm/init/index.mjs +12 -9
- package/dist/esm/init/index.mjs.map +1 -1
- package/dist/esm/init/utils/configManipulation.mjs +28 -1
- package/dist/esm/init/utils/configManipulation.mjs.map +1 -1
- package/dist/esm/init/utils/contentLayout.mjs +58 -0
- package/dist/esm/init/utils/contentLayout.mjs.map +1 -0
- package/dist/esm/init/utils/index.mjs +2 -2
- package/dist/esm/init/utils/packageManager.mjs.map +1 -1
- package/dist/esm/listDictionariesPath.mjs +2 -5
- package/dist/esm/listDictionariesPath.mjs.map +1 -1
- package/dist/esm/loadDictionaries/index.mjs +1 -1
- package/dist/esm/loadDictionaries/loadDictionaries.mjs +1 -1
- package/dist/esm/loadDictionaries/loadRemoteDictionaries.mjs.map +1 -1
- package/dist/esm/utils/buildComponentFilesList.mjs +19 -31
- package/dist/esm/utils/buildComponentFilesList.mjs.map +1 -1
- package/dist/esm/utils/chunkJSON.mjs +1 -1
- package/dist/esm/utils/chunkJSON.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +2 -1
- package/dist/esm/utils/isDirectoryExcluded.mjs +35 -0
- package/dist/esm/utils/isDirectoryExcluded.mjs.map +1 -0
- package/dist/esm/watcher.mjs +5 -2
- package/dist/esm/watcher.mjs.map +1 -1
- package/dist/esm/writeConfiguration/index.mjs +23 -1
- package/dist/esm/writeConfiguration/index.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/index.mjs +2 -1
- package/dist/esm/writeContentDeclaration/processContentDeclarationContent.mjs +20 -13
- package/dist/esm/writeContentDeclaration/processContentDeclarationContent.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/renderContentDeclaration.mjs +208 -0
- package/dist/esm/writeContentDeclaration/renderContentDeclaration.mjs.map +1 -0
- package/dist/esm/writeContentDeclaration/writeContentDeclaration.mjs +5 -51
- package/dist/esm/writeContentDeclaration/writeContentDeclaration.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs +2 -31
- package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/writeYamlFile.mjs +2 -8
- package/dist/esm/writeContentDeclaration/writeYamlFile.mjs.map +1 -1
- package/dist/types/build.d.ts +5 -3
- package/dist/types/buildIntlayerDictionary/buildIntlayerDictionary.d.ts +6 -0
- package/dist/types/buildIntlayerDictionary/buildIntlayerDictionary.d.ts.map +1 -1
- package/dist/types/buildIntlayerDictionary/index.d.ts +3 -2
- package/dist/types/buildIntlayerDictionary/rebuildUnmergedDictionaries.d.ts +14 -0
- package/dist/types/buildIntlayerDictionary/rebuildUnmergedDictionaries.d.ts.map +1 -0
- package/dist/types/cli.d.ts +4 -2
- package/dist/types/fetchDistantDictionaries.d.ts +1 -1
- package/dist/types/fetchDistantDictionaries.d.ts.map +1 -1
- package/dist/types/init/cms.d.ts +6 -0
- package/dist/types/init/cms.d.ts.map +1 -1
- package/dist/types/init/index.d.ts +15 -14
- package/dist/types/init/index.d.ts.map +1 -1
- package/dist/types/init/utils/configManipulation.d.ts +8 -0
- package/dist/types/init/utils/configManipulation.d.ts.map +1 -1
- package/dist/types/init/utils/contentLayout.d.ts +42 -0
- package/dist/types/init/utils/contentLayout.d.ts.map +1 -0
- package/dist/types/init/utils/index.d.ts +2 -2
- package/dist/types/init/utils/packageManager.d.ts +4 -3
- package/dist/types/init/utils/packageManager.d.ts.map +1 -1
- package/dist/types/listDictionariesPath.d.ts +1 -1
- package/dist/types/listDictionariesPath.d.ts.map +1 -1
- package/dist/types/loadDictionaries/loadRemoteDictionaries.d.ts +1 -1
- package/dist/types/loadDictionaries/loadRemoteDictionaries.d.ts.map +1 -1
- package/dist/types/utils/buildComponentFilesList.d.ts +6 -6
- package/dist/types/utils/buildComponentFilesList.d.ts.map +1 -1
- package/dist/types/utils/chunkJSON.d.ts.map +1 -1
- package/dist/types/utils/index.d.ts +2 -1
- package/dist/types/utils/isDirectoryExcluded.d.ts +15 -0
- package/dist/types/utils/isDirectoryExcluded.d.ts.map +1 -0
- package/dist/types/watcher.d.ts.map +1 -1
- package/dist/types/writeConfiguration/index.d.ts +13 -0
- package/dist/types/writeConfiguration/index.d.ts.map +1 -1
- package/dist/types/writeContentDeclaration/index.d.ts +2 -1
- package/dist/types/writeContentDeclaration/processContentDeclarationContent.d.ts +8 -2
- package/dist/types/writeContentDeclaration/processContentDeclarationContent.d.ts.map +1 -1
- package/dist/types/writeContentDeclaration/renderContentDeclaration.d.ts +61 -0
- package/dist/types/writeContentDeclaration/renderContentDeclaration.d.ts.map +1 -0
- package/dist/types/writeContentDeclaration/writeContentDeclaration.d.ts.map +1 -1
- package/dist/types/writeContentDeclaration/writeMarkdownFile.d.ts.map +1 -1
- package/dist/types/writeContentDeclaration/writeYamlFile.d.ts.map +1 -1
- package/package.json +13 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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 { dirname, join, resolve } from 'node:path';\nimport { isVersionAtLeast } from './version';\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 /** Compat packages in dependencies that got replaced and should be moved to devDependencies. */\n packagesToMoveToDev: 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 * Compat adapters the project would need but that are not published yet\n * (see {@link PENDING_COMPAT_ADAPTERS}). The original library is kept as is;\n * only its catalogs are synced.\n */\n pendingCompatAdapters: string[];\n};\n\n/**\n * Compat adapters that live in `compat-comming/` and are not published to npm\n * yet. Installing one fails the whole install command, so init keeps the\n * original library, skips the adapter and its bundler / framework wiring, and\n * only syncs the catalogs. Remove an entry once its package is released.\n */\nexport const PENDING_COMPAT_ADAPTERS: ReadonlySet<string> = new Set([\n '@intlayer/nuxtjs-i18n',\n '@intlayer/svelte-i18n',\n '@intlayer/ngx-translate',\n]);\n\n/** Lock files revealing the package manager, in detection priority order. */\nconst LOCK_FILE_PACKAGE_MANAGERS: ReadonlyArray<\n readonly [lockFile: string, packageManager: PackageManager]\n> = [\n ['bun.lock', 'bun'],\n ['bun.lockb', 'bun'],\n ['pnpm-lock.yaml', 'pnpm'],\n ['yarn.lock', 'yarn'],\n ['package-lock.json', 'npm'],\n];\n\n/** Package managers accepted in the `packageManager` field of `package.json`. */\nconst PACKAGE_MANAGERS: readonly PackageManager[] = [\n 'bun',\n 'pnpm',\n 'yarn',\n 'npm',\n];\n\n/**\n * Reads the package manager pinned by the `packageManager` field of the\n * `package.json` in `directory` (e.g. `\"pnpm@9.0.0\"`), if any.\n */\nconst readPackageManagerField = (directory: string): PackageManager | null => {\n try {\n const { packageManager } = JSON.parse(\n readFileSync(join(directory, 'package.json'), 'utf8')\n ) as { packageManager?: unknown };\n\n if (typeof packageManager !== 'string') return null;\n\n const name = packageManager.split('@')[0] as PackageManager;\n return PACKAGE_MANAGERS.includes(name) ? name : null;\n } catch {\n return null;\n }\n};\n\n/**\n * Detects the package manager in use by checking for lock files in the\n * project root, then the `packageManager` field of its `package.json`. Falls\n * back to npm when neither is found.\n */\nexport const detectPackageManager = (rootDir: string): PackageManager =>\n LOCK_FILE_PACKAGE_MANAGERS.find(([lockFile]) =>\n existsSync(join(rootDir, lockFile))\n )?.[1] ??\n readPackageManagerField(rootDir) ??\n 'npm';\n\n/**\n * Walks up from `startDir` to the nearest directory holding a lock file — the\n * workspace root an install must run from. Stops at the repository root\n * (`.git`) and returns `null` when no lock file is found.\n */\nexport const findLockFileDir = (startDir: string): string | null => {\n let currentDir = resolve(startDir);\n\n while (true) {\n const hasLockFile = LOCK_FILE_PACKAGE_MANAGERS.some(([lockFile]) =>\n existsSync(join(currentDir, lockFile))\n );\n if (hasLockFile) return currentDir;\n if (existsSync(join(currentDir, '.git'))) return null;\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) return null;\n currentDir = parentDir;\n }\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 * The project's runtime dependencies (from `package.json` `dependencies`).\n * When provided, compat packages found here that are replaced by `@intlayer/*`\n * are reported in `packagesToMoveToDev`.\n */\n dependencies?: Record<string, string>;\n /**\n * The project's development dependencies (from `package.json` `devDependencies`).\n */\n devDependencies?: Record<string, string>;\n /**\n * Skip `eslint-plugin-intlayer`, even when the project lints. Set when the\n * lint step is not part of the setup.\n */\n skipLintPlugin?: boolean;\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 * Upstream third-party packages replaced by Intlayer compat adapters at runtime,\n * mapped to their adapter package name.\n */\n replacements: Record<string, 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 replacements: {\n i18next: '@intlayer/i18next',\n 'react-i18next': '@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 replacements: {\n 'next-intl': '@intlayer/next-intl',\n 'use-intl': '@intlayer/use-intl',\n },\n },\n {\n label: 'vue-i18n',\n packages: ['vue-i18n', '@intlayer/vue-i18n'],\n replacements: {\n 'vue-i18n': '@intlayer/vue-i18n',\n },\n },\n {\n label: '@nuxtjs/i18n',\n packages: ['@nuxtjs/i18n', '@intlayer/nuxtjs-i18n'],\n replacements: {\n '@nuxtjs/i18n': '@intlayer/nuxtjs-i18n',\n },\n },\n {\n label: 'next-i18next',\n packages: ['next-i18next', '@intlayer/next-i18next'],\n replacements: {\n 'next-i18next': '@intlayer/next-i18next',\n },\n },\n {\n label: 'next-translate',\n packages: ['next-translate', '@intlayer/next-translate'],\n replacements: {\n 'next-translate': '@intlayer/next-translate',\n },\n },\n {\n label: 'react-intl',\n packages: ['react-intl', '@intlayer/react-intl'],\n replacements: {\n 'react-intl': '@intlayer/react-intl',\n },\n },\n {\n label: 'Lingui',\n packages: ['@lingui/core', '@lingui/react', '@intlayer/lingui'],\n replacements: {\n '@lingui/core': '@intlayer/lingui',\n '@lingui/react': '@intlayer/lingui',\n },\n },\n {\n label: 'svelte-i18n',\n packages: ['svelte-i18n', '@intlayer/svelte-i18n'],\n replacements: {\n 'svelte-i18n': '@intlayer/svelte-i18n',\n },\n },\n {\n label: '@ngneat/transloco',\n packages: ['@ngneat/transloco', '@intlayer/transloco'],\n replacements: {\n '@ngneat/transloco': '@intlayer/transloco',\n },\n },\n {\n label: '@ngx-translate/core',\n packages: ['@ngx-translate/core', '@intlayer/ngx-translate'],\n replacements: {\n '@ngx-translate/core': '@intlayer/ngx-translate',\n },\n },\n {\n label: 'node-polyglot',\n packages: ['node-polyglot', '@intlayer/polyglot'],\n replacements: {\n 'node-polyglot': '@intlayer/polyglot',\n },\n },\n {\n label: 'i18n-js',\n packages: ['i18n-js', '@intlayer/i18n-js'],\n replacements: {\n 'i18n-js': '@intlayer/i18n-js',\n },\n },\n];\n\n/**\n * Maps third-party i18n libraries to the Intlayer compat adapter that replaces\n * them at runtime, derived from {@link COMPAT_I18N_LIBRARIES}.\n */\nexport const COMPAT_PACKAGE_REPLACEMENTS: Record<string, string> =\n Object.fromEntries(\n COMPAT_I18N_LIBRARIES.flatMap((library) =>\n Object.entries(library.replacements)\n )\n );\n\n/**\n * When a project uses a compat i18n package (such as `next-intl`) that gets\n * replaced by an `@intlayer/*` adapter (e.g. `@intlayer/next-intl`), moves the\n * original package from `dependencies` to `devDependencies` in `package.json`.\n *\n * Returns the list of package names that were moved.\n */\nexport const moveCompatPackagesToDevDependencies = (\n packageJson: Record<string, any>\n): string[] => {\n const movedPackages: string[] = [];\n if (!packageJson.dependencies) return movedPackages;\n\n for (const compatPackage of Object.keys(COMPAT_PACKAGE_REPLACEMENTS)) {\n if (packageJson.dependencies[compatPackage]) {\n const version = packageJson.dependencies[compatPackage];\n delete packageJson.dependencies[compatPackage];\n\n packageJson.devDependencies = packageJson.devDependencies ?? {};\n if (!packageJson.devDependencies[compatPackage]) {\n packageJson.devDependencies[compatPackage] = version;\n }\n movedPackages.push(compatPackage);\n }\n }\n\n return movedPackages;\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/** Lowest ESLint major `eslint-plugin-intlayer` supports (flat config). */\nexport const MINIMUM_ESLINT_MAJOR = 9;\n\n/**\n * True when the project pins ESLint below {@link MINIMUM_ESLINT_MAJOR}.\n * Installing the plugin there fails on its `eslint` peer range (and takes the\n * whole install command down with it). Ranges without a version number\n * (`latest`, `*`, `catalog:`) are assumed current.\n *\n * @param dependencies - Merged dependencies of the target project.\n */\nexport const hasLegacyEslint = (\n dependencies: Record<string, string>\n): boolean => {\n const eslintVersion = dependencies.eslint;\n if (!eslintVersion || !/\\d/.test(eslintVersion)) return false;\n return !isVersionAtLeast(eslintVersion, MINIMUM_ESLINT_MAJOR);\n};\n\n/**\n * True when the project already lints with a linter the Intlayer rules can\n * plug into: oxlint, or ESLint 9+.\n *\n * Shared with the init flow so that installing the plugin and wiring up its\n * configuration are gated on exactly the same condition — a project that does\n * not lint, or lints with ESLint 8, is left alone.\n *\n * @param dependencies - Merged dependencies of the target project.\n */\nexport const hasLintTooling = (dependencies: Record<string, string>): boolean =>\n !hasLegacyEslint(dependencies) &&\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 const packagesToMoveToDev: string[] = [];\n const pendingCompatAdapters: string[] = [];\n\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 /**\n * Whether an adapter can be used: published, or already installed by the\n * user (e.g. a local build).\n */\n const isAdapterAvailable = (adapterPackage: string): boolean =>\n !PENDING_COMPAT_ADAPTERS.has(adapterPackage) || isInstalled(adapterPackage);\n\n const markCompatReplacement = (\n originalPackage: string,\n adapterPackage: string\n ): void => {\n if (!isAdapterAvailable(adapterPackage)) {\n if (!pendingCompatAdapters.includes(adapterPackage)) {\n pendingCompatAdapters.push(adapterPackage);\n }\n return;\n }\n\n addIfMissing(adapterPackage);\n addDevIfMissing(originalPackage);\n\n const isExplicitDev = Boolean(options.devDependencies?.[originalPackage]);\n const isExplicitProd = Boolean(options.dependencies?.[originalPackage]);\n const isImplicitProd =\n !options.dependencies &&\n !options.devDependencies &&\n Boolean(allDependencies[originalPackage]);\n\n if ((isExplicitProd || isImplicitProd) && !isExplicitDev) {\n if (!packagesToMoveToDev.includes(originalPackage)) {\n packagesToMoveToDev.push(originalPackage);\n }\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 // Meta-frameworks whose config init wires the Intlayer module /\n // integration into. A `@nuxtjs/i18n` app keeps its own module instead.\n if (isInstalled('nuxt') && !isInstalled('@nuxtjs/i18n')) {\n addIfMissing('nuxt-intlayer');\n }\n\n if (isInstalled('astro')) {\n addIfMissing('astro-intlayer');\n }\n\n // Server frameworks. NestJS runs on Express unless the Fastify platform is\n // installed, so it gets the matching adapter.\n const isNestJsProject = isInstalled('@nestjs/core');\n const isNestJsOnFastify = isInstalled('@nestjs/platform-fastify');\n\n if (isInstalled('express') || (isNestJsProject && !isNestJsOnFastify)) {\n addIfMissing('express-intlayer');\n }\n\n if (isInstalled('fastify') || isNestJsOnFastify) {\n addIfMissing('fastify-intlayer');\n }\n\n if (isInstalled('hono')) {\n addIfMissing('hono-intlayer');\n }\n\n if (isInstalled('elysia')) {\n addIfMissing('elysia-intlayer');\n }\n\n if (isInstalled('@adonisjs/core')) {\n addIfMissing('adonis-intlayer');\n }\n\n // Lint rules — only when the project already lints.\n if (!options.skipLintPlugin && 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 markCompatReplacement('next-intl', '@intlayer/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 markCompatReplacement('next-i18next', '@intlayer/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 markCompatReplacement('next-translate', '@intlayer/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 markCompatReplacement('i18next', '@intlayer/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 markCompatReplacement('react-i18next', '@intlayer/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 markCompatReplacement('vue-i18n', '@intlayer/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 markCompatReplacement('use-intl', '@intlayer/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 markCompatReplacement('react-intl', '@intlayer/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 markCompatReplacement('@ngneat/transloco', '@intlayer/transloco');\n if (isAdapterAvailable('@intlayer/transloco')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'translocoVitePlugin',\n pluginPackageSource: '@intlayer/transloco/plugin',\n };\n }\n }\n\n // svelte-i18n — vite alias injection required, flat JSON (i18next-compatible)\n if (isInstalled('svelte-i18n') || isInstalled('@intlayer/svelte-i18n')) {\n markCompatReplacement('svelte-i18n', '@intlayer/svelte-i18n');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}.json',\n };\n if (isAdapterAvailable('@intlayer/svelte-i18n')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'svelteI18nVitePlugin',\n pluginPackageSource: '@intlayer/svelte-i18n/plugin',\n };\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 markCompatReplacement('node-polyglot', '@intlayer/polyglot');\n if (isAdapterAvailable('@intlayer/polyglot')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'polyglotVitePlugin',\n pluginPackageSource: '@intlayer/polyglot/plugin',\n };\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 markCompatReplacement('@nuxtjs/i18n', '@intlayer/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 markCompatReplacement('@ngx-translate/core', '@intlayer/ngx-translate');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './assets/i18n/${locale}.json',\n };\n if (isAdapterAvailable('@intlayer/ngx-translate')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'ngxTranslateVitePlugin',\n pluginPackageSource: '@intlayer/ngx-translate/plugin',\n };\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 markCompatReplacement('@lingui/core', '@intlayer/lingui');\n\n if (isInstalled('@lingui/react')) {\n markCompatReplacement('@lingui/react', '@intlayer/lingui');\n }\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 markCompatReplacement('i18n-js', '@intlayer/i18n-js');\n if (isAdapterAvailable('@intlayer/i18n-js')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'i18nJsVitePlugin',\n pluginPackageSource: '@intlayer/i18n-js/plugin',\n };\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 packagesToMoveToDev,\n compatSyncConfig,\n compatVitePluginConfig,\n pendingCompatAdapters,\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`), as well as the\n * `intlayer-*` packages (e.g. `intlayer-editor`).\n */\nexport const isIntlayerPackageName = (packageName: string): boolean =>\n packageName === 'intlayer' ||\n packageName.startsWith('@intlayer/') ||\n packageName.startsWith('intlayer-') ||\n packageName.endsWith('-intlayer');\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\n const match = version.match(/(\\d+)\\.(\\d+)\\.(\\d+)/);\n\n return match ? `${match[1]}.${match[2]}.${match[3]}` : null;\n};\n\n/**\n * Runs the package manager install command synchronously, so edited\n * `package.json` ranges are resolved and the lock file updated. Throws if the\n * install process exits with a non-zero code.\n */\nexport const runPackageInstall = (\n rootDir: string,\n packageManager: PackageManager\n): void => {\n execSync(`${packageManager} install`, { cwd: rootDir, stdio: 'inherit' });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"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 { dirname, join, resolve } from 'node:path';\nimport { isVersionAtLeast } from './version';\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 message format matching the compat library's conventions (or\n * `'intlayer'` for plain Intlayer catalogs). Ignored when `plugin` is `'po'`\n * (PO catalogs are always serialized as gettext).\n */\n format: 'icu' | 'i18next' | 'vue-i18n' | 'intlayer';\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 /** Compat packages in dependencies that got replaced and should be moved to devDependencies. */\n packagesToMoveToDev: 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 * Compat adapters the project would need but that are not published yet\n * (see {@link PENDING_COMPAT_ADAPTERS}). The original library is kept as is;\n * only its catalogs are synced.\n */\n pendingCompatAdapters: string[];\n};\n\n/**\n * Compat adapters that live in `compat-comming/` and are not published to npm\n * yet. Installing one fails the whole install command, so init keeps the\n * original library, skips the adapter and its bundler / framework wiring, and\n * only syncs the catalogs. Remove an entry once its package is released.\n */\nexport const PENDING_COMPAT_ADAPTERS: ReadonlySet<string> = new Set([\n '@intlayer/nuxtjs-i18n',\n '@intlayer/svelte-i18n',\n '@intlayer/ngx-translate',\n]);\n\n/** Lock files revealing the package manager, in detection priority order. */\nconst LOCK_FILE_PACKAGE_MANAGERS: ReadonlyArray<\n readonly [lockFile: string, packageManager: PackageManager]\n> = [\n ['bun.lock', 'bun'],\n ['bun.lockb', 'bun'],\n ['pnpm-lock.yaml', 'pnpm'],\n ['yarn.lock', 'yarn'],\n ['package-lock.json', 'npm'],\n];\n\n/** Package managers accepted in the `packageManager` field of `package.json`. */\nconst PACKAGE_MANAGERS: readonly PackageManager[] = [\n 'bun',\n 'pnpm',\n 'yarn',\n 'npm',\n];\n\n/**\n * Reads the package manager pinned by the `packageManager` field of the\n * `package.json` in `directory` (e.g. `\"pnpm@9.0.0\"`), if any.\n */\nconst readPackageManagerField = (directory: string): PackageManager | null => {\n try {\n const { packageManager } = JSON.parse(\n readFileSync(join(directory, 'package.json'), 'utf8')\n ) as { packageManager?: unknown };\n\n if (typeof packageManager !== 'string') return null;\n\n const name = packageManager.split('@')[0] as PackageManager;\n return PACKAGE_MANAGERS.includes(name) ? name : null;\n } catch {\n return null;\n }\n};\n\n/**\n * Detects the package manager in use by checking for lock files in the\n * project root, then the `packageManager` field of its `package.json`. Falls\n * back to npm when neither is found.\n */\nexport const detectPackageManager = (rootDir: string): PackageManager =>\n LOCK_FILE_PACKAGE_MANAGERS.find(([lockFile]) =>\n existsSync(join(rootDir, lockFile))\n )?.[1] ??\n readPackageManagerField(rootDir) ??\n 'npm';\n\n/**\n * Walks up from `startDir` to the nearest directory holding a lock file — the\n * workspace root an install must run from. Stops at the repository root\n * (`.git`) and returns `null` when no lock file is found.\n */\nexport const findLockFileDir = (startDir: string): string | null => {\n let currentDir = resolve(startDir);\n\n while (true) {\n const hasLockFile = LOCK_FILE_PACKAGE_MANAGERS.some(([lockFile]) =>\n existsSync(join(currentDir, lockFile))\n );\n if (hasLockFile) return currentDir;\n if (existsSync(join(currentDir, '.git'))) return null;\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) return null;\n currentDir = parentDir;\n }\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 * The project's runtime dependencies (from `package.json` `dependencies`).\n * When provided, compat packages found here that are replaced by `@intlayer/*`\n * are reported in `packagesToMoveToDev`.\n */\n dependencies?: Record<string, string>;\n /**\n * The project's development dependencies (from `package.json` `devDependencies`).\n */\n devDependencies?: Record<string, string>;\n /**\n * Skip `eslint-plugin-intlayer`, even when the project lints. Set when the\n * lint step is not part of the setup.\n */\n skipLintPlugin?: boolean;\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 * Upstream third-party packages replaced by Intlayer compat adapters at runtime,\n * mapped to their adapter package name.\n */\n replacements: Record<string, 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 replacements: {\n i18next: '@intlayer/i18next',\n 'react-i18next': '@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 replacements: {\n 'next-intl': '@intlayer/next-intl',\n 'use-intl': '@intlayer/use-intl',\n },\n },\n {\n label: 'vue-i18n',\n packages: ['vue-i18n', '@intlayer/vue-i18n'],\n replacements: {\n 'vue-i18n': '@intlayer/vue-i18n',\n },\n },\n {\n label: '@nuxtjs/i18n',\n packages: ['@nuxtjs/i18n', '@intlayer/nuxtjs-i18n'],\n replacements: {\n '@nuxtjs/i18n': '@intlayer/nuxtjs-i18n',\n },\n },\n {\n label: 'next-i18next',\n packages: ['next-i18next', '@intlayer/next-i18next'],\n replacements: {\n 'next-i18next': '@intlayer/next-i18next',\n },\n },\n {\n label: 'next-translate',\n packages: ['next-translate', '@intlayer/next-translate'],\n replacements: {\n 'next-translate': '@intlayer/next-translate',\n },\n },\n {\n label: 'react-intl',\n packages: ['react-intl', '@intlayer/react-intl'],\n replacements: {\n 'react-intl': '@intlayer/react-intl',\n },\n },\n {\n label: 'Lingui',\n packages: ['@lingui/core', '@lingui/react', '@intlayer/lingui'],\n replacements: {\n '@lingui/core': '@intlayer/lingui',\n '@lingui/react': '@intlayer/lingui',\n },\n },\n {\n label: 'svelte-i18n',\n packages: ['svelte-i18n', '@intlayer/svelte-i18n'],\n replacements: {\n 'svelte-i18n': '@intlayer/svelte-i18n',\n },\n },\n {\n label: '@ngneat/transloco',\n packages: ['@ngneat/transloco', '@intlayer/transloco'],\n replacements: {\n '@ngneat/transloco': '@intlayer/transloco',\n },\n },\n {\n label: '@ngx-translate/core',\n packages: ['@ngx-translate/core', '@intlayer/ngx-translate'],\n replacements: {\n '@ngx-translate/core': '@intlayer/ngx-translate',\n },\n },\n {\n label: 'node-polyglot',\n packages: ['node-polyglot', '@intlayer/polyglot'],\n replacements: {\n 'node-polyglot': '@intlayer/polyglot',\n },\n },\n {\n label: 'i18n-js',\n packages: ['i18n-js', '@intlayer/i18n-js'],\n replacements: {\n 'i18n-js': '@intlayer/i18n-js',\n },\n },\n];\n\n/**\n * Maps third-party i18n libraries to the Intlayer compat adapter that replaces\n * them at runtime, derived from {@link COMPAT_I18N_LIBRARIES}.\n */\nexport const COMPAT_PACKAGE_REPLACEMENTS: Record<string, string> =\n Object.fromEntries(\n COMPAT_I18N_LIBRARIES.flatMap((library) =>\n Object.entries(library.replacements)\n )\n );\n\n/**\n * When a project uses a compat i18n package (such as `next-intl`) that gets\n * replaced by an `@intlayer/*` adapter (e.g. `@intlayer/next-intl`), moves the\n * original package from `dependencies` to `devDependencies` in `package.json`.\n *\n * Returns the list of package names that were moved.\n */\nexport const moveCompatPackagesToDevDependencies = (\n packageJson: Record<string, any>\n): string[] => {\n const movedPackages: string[] = [];\n if (!packageJson.dependencies) return movedPackages;\n\n for (const compatPackage of Object.keys(COMPAT_PACKAGE_REPLACEMENTS)) {\n if (packageJson.dependencies[compatPackage]) {\n const version = packageJson.dependencies[compatPackage];\n delete packageJson.dependencies[compatPackage];\n\n packageJson.devDependencies = packageJson.devDependencies ?? {};\n if (!packageJson.devDependencies[compatPackage]) {\n packageJson.devDependencies[compatPackage] = version;\n }\n movedPackages.push(compatPackage);\n }\n }\n\n return movedPackages;\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/** Lowest ESLint major `eslint-plugin-intlayer` supports (flat config). */\nexport const MINIMUM_ESLINT_MAJOR = 9;\n\n/**\n * True when the project pins ESLint below {@link MINIMUM_ESLINT_MAJOR}.\n * Installing the plugin there fails on its `eslint` peer range (and takes the\n * whole install command down with it). Ranges without a version number\n * (`latest`, `*`, `catalog:`) are assumed current.\n *\n * @param dependencies - Merged dependencies of the target project.\n */\nexport const hasLegacyEslint = (\n dependencies: Record<string, string>\n): boolean => {\n const eslintVersion = dependencies.eslint;\n if (!eslintVersion || !/\\d/.test(eslintVersion)) return false;\n return !isVersionAtLeast(eslintVersion, MINIMUM_ESLINT_MAJOR);\n};\n\n/**\n * True when the project already lints with a linter the Intlayer rules can\n * plug into: oxlint, or ESLint 9+.\n *\n * Shared with the init flow so that installing the plugin and wiring up its\n * configuration are gated on exactly the same condition — a project that does\n * not lint, or lints with ESLint 8, is left alone.\n *\n * @param dependencies - Merged dependencies of the target project.\n */\nexport const hasLintTooling = (dependencies: Record<string, string>): boolean =>\n !hasLegacyEslint(dependencies) &&\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 const packagesToMoveToDev: string[] = [];\n const pendingCompatAdapters: string[] = [];\n\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 /**\n * Whether an adapter can be used: published, or already installed by the\n * user (e.g. a local build).\n */\n const isAdapterAvailable = (adapterPackage: string): boolean =>\n !PENDING_COMPAT_ADAPTERS.has(adapterPackage) || isInstalled(adapterPackage);\n\n const markCompatReplacement = (\n originalPackage: string,\n adapterPackage: string\n ): void => {\n if (!isAdapterAvailable(adapterPackage)) {\n if (!pendingCompatAdapters.includes(adapterPackage)) {\n pendingCompatAdapters.push(adapterPackage);\n }\n return;\n }\n\n addIfMissing(adapterPackage);\n addDevIfMissing(originalPackage);\n\n const isExplicitDev = Boolean(options.devDependencies?.[originalPackage]);\n const isExplicitProd = Boolean(options.dependencies?.[originalPackage]);\n const isImplicitProd =\n !options.dependencies &&\n !options.devDependencies &&\n Boolean(allDependencies[originalPackage]);\n\n if ((isExplicitProd || isImplicitProd) && !isExplicitDev) {\n if (!packagesToMoveToDev.includes(originalPackage)) {\n packagesToMoveToDev.push(originalPackage);\n }\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 // Meta-frameworks whose config init wires the Intlayer module /\n // integration into. A `@nuxtjs/i18n` app keeps its own module instead.\n if (isInstalled('nuxt') && !isInstalled('@nuxtjs/i18n')) {\n addIfMissing('nuxt-intlayer');\n }\n\n if (isInstalled('astro')) {\n addIfMissing('astro-intlayer');\n }\n\n // Server frameworks. NestJS runs on Express unless the Fastify platform is\n // installed, so it gets the matching adapter.\n const isNestJsProject = isInstalled('@nestjs/core');\n const isNestJsOnFastify = isInstalled('@nestjs/platform-fastify');\n\n if (isInstalled('express') || (isNestJsProject && !isNestJsOnFastify)) {\n addIfMissing('express-intlayer');\n }\n\n if (isInstalled('fastify') || isNestJsOnFastify) {\n addIfMissing('fastify-intlayer');\n }\n\n if (isInstalled('hono')) {\n addIfMissing('hono-intlayer');\n }\n\n if (isInstalled('elysia')) {\n addIfMissing('elysia-intlayer');\n }\n\n if (isInstalled('@adonisjs/core')) {\n addIfMissing('adonis-intlayer');\n }\n\n // Lint rules — only when the project already lints.\n if (!options.skipLintPlugin && 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 markCompatReplacement('next-intl', '@intlayer/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 markCompatReplacement('next-i18next', '@intlayer/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 markCompatReplacement('next-translate', '@intlayer/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 markCompatReplacement('i18next', '@intlayer/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 markCompatReplacement('react-i18next', '@intlayer/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 markCompatReplacement('vue-i18n', '@intlayer/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 markCompatReplacement('use-intl', '@intlayer/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 markCompatReplacement('react-intl', '@intlayer/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 markCompatReplacement('@ngneat/transloco', '@intlayer/transloco');\n if (isAdapterAvailable('@intlayer/transloco')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'translocoVitePlugin',\n pluginPackageSource: '@intlayer/transloco/plugin',\n };\n }\n }\n\n // svelte-i18n — vite alias injection required, flat JSON (i18next-compatible)\n if (isInstalled('svelte-i18n') || isInstalled('@intlayer/svelte-i18n')) {\n markCompatReplacement('svelte-i18n', '@intlayer/svelte-i18n');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}.json',\n };\n if (isAdapterAvailable('@intlayer/svelte-i18n')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'svelteI18nVitePlugin',\n pluginPackageSource: '@intlayer/svelte-i18n/plugin',\n };\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 markCompatReplacement('node-polyglot', '@intlayer/polyglot');\n if (isAdapterAvailable('@intlayer/polyglot')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'polyglotVitePlugin',\n pluginPackageSource: '@intlayer/polyglot/plugin',\n };\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 markCompatReplacement('@nuxtjs/i18n', '@intlayer/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 markCompatReplacement('@ngx-translate/core', '@intlayer/ngx-translate');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './assets/i18n/${locale}.json',\n };\n if (isAdapterAvailable('@intlayer/ngx-translate')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'ngxTranslateVitePlugin',\n pluginPackageSource: '@intlayer/ngx-translate/plugin',\n };\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 markCompatReplacement('@lingui/core', '@intlayer/lingui');\n\n if (isInstalled('@lingui/react')) {\n markCompatReplacement('@lingui/react', '@intlayer/lingui');\n }\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 markCompatReplacement('i18n-js', '@intlayer/i18n-js');\n if (isAdapterAvailable('@intlayer/i18n-js')) {\n compatVitePluginConfig ??= {\n pluginFunctionName: 'i18nJsVitePlugin',\n pluginPackageSource: '@intlayer/i18n-js/plugin',\n };\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 packagesToMoveToDev,\n compatSyncConfig,\n compatVitePluginConfig,\n pendingCompatAdapters,\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`), as well as the\n * `intlayer-*` packages (e.g. `intlayer-editor`).\n */\nexport const isIntlayerPackageName = (packageName: string): boolean =>\n packageName === 'intlayer' ||\n packageName.startsWith('@intlayer/') ||\n packageName.startsWith('intlayer-') ||\n packageName.endsWith('-intlayer');\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\n const match = version.match(/(\\d+)\\.(\\d+)\\.(\\d+)/);\n\n return match ? `${match[1]}.${match[2]}.${match[3]}` : null;\n};\n\n/**\n * Runs the package manager install command synchronously, so edited\n * `package.json` ranges are resolved and the lock file updated. Throws if the\n * install process exits with a non-zero code.\n */\nexport const runPackageInstall = (\n rootDir: string,\n packageManager: PackageManager\n): void => {\n execSync(`${packageManager} install`, { cwd: rootDir, stdio: 'inherit' });\n};\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDirectoryExcluded } from "./utils/isDirectoryExcluded.mjs";
|
|
1
2
|
import { normalizePath } from "@intlayer/config/utils";
|
|
2
3
|
import fg from "fast-glob";
|
|
3
4
|
import { stat } from "node:fs/promises";
|
|
@@ -13,11 +14,7 @@ const listDictionaries = async (configuration) => {
|
|
|
13
14
|
const filePromises = fileExtensions.flatMap((ext) => contentDir.map((dir) => `${normalizePath(dir)}/**/*${ext}`.replace("//", "/"))).map(async (pattern) => {
|
|
14
15
|
const magicIndex = pattern.search(/[*?{}(]/);
|
|
15
16
|
const basePattern = magicIndex > -1 ? pattern.slice(0, magicIndex) : pattern;
|
|
16
|
-
const applicableIgnore = excludedPath.filter((excludePattern) =>
|
|
17
|
-
const cleanName = excludePattern.replace(/\*\*/g, "").replace(/\//g, "");
|
|
18
|
-
if (cleanName && basePattern.includes(`/${cleanName}/`)) return false;
|
|
19
|
-
return true;
|
|
20
|
-
});
|
|
17
|
+
const applicableIgnore = excludedPath.filter((excludePattern) => !isDirectoryExcluded(basePattern, [excludePattern]));
|
|
21
18
|
return fg(pattern, {
|
|
22
19
|
ignore: applicableIgnore,
|
|
23
20
|
absolute: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listDictionariesPath.mjs","names":[],"sources":["../../src/listDictionariesPath.ts"],"sourcesContent":["import { stat } from 'node:fs/promises';\nimport { normalizePath } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport fg from 'fast-glob';\n\n/**\n * List all dictionaries absolute paths in the project\n * @param configuration - The configuration object\n * @returns An array of dictionary paths\n */\nexport const listDictionaries = async (\n configuration: IntlayerConfig\n): Promise<string[]> => {\n const { fileExtensions, contentDir, excludedPath } = configuration.content;\n\n const watchedFilesPatternWithPath = fileExtensions.flatMap((ext) =>\n contentDir.map((dir) =>\n `${normalizePath(dir)}/**/*${ext}`.replace('//', '/')\n )\n );\n\n const filePromises = watchedFilesPatternWithPath.map(async (pattern) => {\n // Identify the static part of the path (before any wildcards like *)\n // e.g. \"/Users/.../design-system/dist/esm/**/*.content.ts\" -> \"/Users/.../design-system/dist/esm/\"\n const magicIndex = pattern.search(/[*?{}(]/);\n const basePattern =\n magicIndex > -1 ? pattern.slice(0, magicIndex) : pattern;\n\n //
|
|
1
|
+
{"version":3,"file":"listDictionariesPath.mjs","names":[],"sources":["../../src/listDictionariesPath.ts"],"sourcesContent":["import { stat } from 'node:fs/promises';\nimport { normalizePath } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport fg from 'fast-glob';\nimport { isDirectoryExcluded } from './utils/isDirectoryExcluded';\n\n/**\n * List all dictionaries absolute paths in the project\n * @param configuration - The configuration object\n * @returns An array of dictionary paths\n */\nexport const listDictionaries = async (\n configuration: IntlayerConfig\n): Promise<string[]> => {\n const { fileExtensions, contentDir, excludedPath } = configuration.content;\n\n const watchedFilesPatternWithPath = fileExtensions.flatMap((ext) =>\n contentDir.map((dir) =>\n `${normalizePath(dir)}/**/*${ext}`.replace('//', '/')\n )\n );\n\n const filePromises = watchedFilesPatternWithPath.map(async (pattern) => {\n // Identify the static part of the path (before any wildcards like *)\n // e.g. \"/Users/.../design-system/dist/esm/**/*.content.ts\" -> \"/Users/.../design-system/dist/esm/\"\n const magicIndex = pattern.search(/[*?{}(]/);\n const basePattern =\n magicIndex > -1 ? pattern.slice(0, magicIndex) : pattern;\n\n // An exclusion matching the explicit base path itself (e.g. a contentDir\n // inside `dist`) is dropped: the contentDir is more precise than it\n const applicableIgnore = excludedPath.filter(\n (excludePattern) => !isDirectoryExcluded(basePattern, [excludePattern])\n );\n\n // Run fast-glob with the customized ignore list\n return fg(pattern, {\n ignore: applicableIgnore,\n absolute: true,\n dot: true,\n });\n });\n\n const filesArrays = await Promise.all(filePromises);\n\n // Flatten and deduplicate\n const uniqueFiles = Array.from(new Set(filesArrays.flat()));\n\n return uniqueFiles;\n};\n\nexport const listDictionariesWithStats = async (\n configuration: IntlayerConfig\n) => {\n const files = await listDictionaries(configuration);\n\n return Promise.all(\n files.map(async (file) => ({ path: file, stats: await stat(file) }))\n );\n};\n"],"mappings":";;;;;;;;;;;AAWA,MAAa,mBAAmB,OAC9B,kBACsB;CACtB,MAAM,EAAE,gBAAgB,YAAY,iBAAiB,cAAc;CAQnE,MAAM,eAN8B,eAAe,SAAS,QAC1D,WAAW,KAAK,QACd,GAAG,cAAc,GAAG,EAAE,OAAO,MAAM,QAAQ,MAAM,GAAG,CACtD,CAG6C,CAAC,CAAC,IAAI,OAAO,YAAY;EAGtE,MAAM,aAAa,QAAQ,OAAO,SAAS;EAC3C,MAAM,cACJ,aAAa,KAAK,QAAQ,MAAM,GAAG,UAAU,IAAI;EAInD,MAAM,mBAAmB,aAAa,QACnC,mBAAmB,CAAC,oBAAoB,aAAa,CAAC,cAAc,CAAC,CACxE;EAGA,OAAO,GAAG,SAAS;GACjB,QAAQ;GACR,UAAU;GACV,KAAK;EACP,CAAC;CACH,CAAC;CAED,MAAM,cAAc,MAAM,QAAQ,IAAI,YAAY;CAKlD,OAFoB,MAAM,KAAK,IAAI,IAAI,YAAY,KAAK,CAAC,CAExC;AACnB;AAEA,MAAa,4BAA4B,OACvC,kBACG;CACH,MAAM,QAAQ,MAAM,iBAAiB,aAAa;CAElD,OAAO,QAAQ,IACb,MAAM,IAAI,OAAO,UAAU;EAAE,MAAM;EAAM,OAAO,MAAM,KAAK,IAAI;CAAE,EAAE,CACrE;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ensureIntlayerBundle, formatLocalDictionaries, loadContentDeclaration, loadContentDeclarations } from "./loadContentDeclaration.mjs";
|
|
2
|
+
import { loadLocalDictionaries } from "./loadLocalDictionaries.mjs";
|
|
2
3
|
import { formatDistantDictionaries, loadRemoteDictionaries } from "./loadRemoteDictionaries.mjs";
|
|
3
4
|
import { loadDictionaries } from "./loadDictionaries.mjs";
|
|
4
|
-
import { loadLocalDictionaries } from "./loadLocalDictionaries.mjs";
|
|
5
5
|
|
|
6
6
|
export { ensureIntlayerBundle, formatDistantDictionaries, formatLocalDictionaries, loadContentDeclaration, loadContentDeclarations, loadDictionaries, loadLocalDictionaries, loadRemoteDictionaries };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { filterInvalidDictionaries } from "../filterInvalidDictionaries.mjs";
|
|
2
|
-
import { formatDictionaries } from "../formatDictionary.mjs";
|
|
3
2
|
import { loadContentDeclarations } from "./loadContentDeclaration.mjs";
|
|
3
|
+
import { formatDictionaries } from "../formatDictionary.mjs";
|
|
4
4
|
import { loadRemoteDictionaries } from "./loadRemoteDictionaries.mjs";
|
|
5
5
|
import { DictionariesLogger } from "./log.mjs";
|
|
6
6
|
import * as ANSIColors from "@intlayer/config/colors";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadRemoteDictionaries.mjs","names":[],"sources":["../../../src/loadDictionaries/loadRemoteDictionaries.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"loadRemoteDictionaries.mjs","names":[],"sources":["../../../src/loadDictionaries/loadRemoteDictionaries.ts"],"sourcesContent":["import type { DictionaryAPI } from '@intlayer/api';\nimport { getConfiguration } from '@intlayer/config/node';\nimport { getRemoteDictionaries } from '@intlayer/dictionaries-entry/remote';\nimport type {\n Dictionary,\n DictionaryId,\n DictionaryKey,\n} from '@intlayer/types/dictionary';\nimport { sortAlphabetically } from '../utils/sortAlphabetically';\nimport type { DictionariesStatus } from './loadDictionaries';\n\nexport const formatDistantDictionaries = (\n dictionaries: (DictionaryAPI | Dictionary)[]\n): Dictionary[] =>\n dictionaries.map((dict) => ({\n ...dict,\n localId: `${dict.key}::remote::${dict.id}`,\n location: 'remote' as const,\n }));\n\nexport const loadRemoteDictionaries = async (\n configuration = getConfiguration(),\n onStatusUpdate?: (status: DictionariesStatus[]) => void,\n options?: {\n onStartRemoteCheck?: () => void;\n onStopRemoteCheck?: () => void;\n onError?: (error: Error) => void;\n }\n): Promise<Dictionary[]> => {\n const { editor } = configuration;\n const remoteDictionariesRecord = getRemoteDictionaries(configuration);\n\n const hasRemoteDictionaries = Boolean(editor.clientId && editor.clientSecret);\n\n if (!hasRemoteDictionaries) return [];\n\n try {\n options?.onStartRemoteCheck?.();\n\n // Lazy: the CMS client is only needed when remote credentials are set\n const { createRemoteDictionaryClient, fetchDistantDictionaries } =\n await import('./remoteDictionaryClient');\n\n const dictionary = createRemoteDictionaryClient(configuration);\n\n // Get the list of dictionary keys\n const getDictionariesKeysResult =\n await dictionary.getDictionariesUpdateTimestamp();\n\n const distantDictionaryUpdateTimeStamp: Record<\n DictionaryId,\n { key: DictionaryKey; updatedAt: number }\n > | null = getDictionariesKeysResult.data;\n\n if (!distantDictionaryUpdateTimeStamp) {\n throw new Error('No distant dictionaries found');\n }\n\n const dictionariesIdToFetch = Object.entries(\n distantDictionaryUpdateTimeStamp\n ).filter(([dictionaryId, data]) => {\n // If remote doesn't provide updatedAt, fetch to be safe\n if (!data.updatedAt) return true;\n\n // If no local cache exists, fetch\n const local: Dictionary | undefined = remoteDictionariesRecord[\n data.key\n ]?.find((dictionary) => dictionary.id === dictionaryId);\n if (!local) return true;\n\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n\n // If local timestamp missing or older than remote, fetch\n if (typeof localUpdatedAt !== 'number') return true;\n\n return data.updatedAt > localUpdatedAt;\n });\n\n const flatRemoteDictionariesRecord: DictionaryAPI[] = Object.values(\n remoteDictionariesRecord\n ).flat();\n\n const cachedDictionaries: Dictionary[] =\n flatRemoteDictionariesRecord.filter((dictionary) => {\n const remoteUpdatedAt = dictionary.id\n ? distantDictionaryUpdateTimeStamp[dictionary.id]?.updatedAt\n : undefined;\n\n const localUpdatedAtRaw = dictionary.updatedAt;\n\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n\n // Consider as cached/imported when local exists and is up-to-date or newer\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n });\n\n // Report cached as already imported\n if (cachedDictionaries.length > 0) {\n onStatusUpdate?.(\n cachedDictionaries.map((dictionary) => ({\n dictionaryKey: dictionary.key,\n type: 'remote',\n status: 'imported',\n }))\n );\n }\n\n // Several qualified dictionaries can share a key, so keys are deduplicated:\n // one request per key already returns all of its siblings.\n const orderedDistantDictionaryKeys = [\n ...new Set(dictionariesIdToFetch.map(([, data]) => data.key)),\n ].sort(sortAlphabetically);\n\n // Report pending for keys to be fetched so totals are visible immediately\n if (orderedDistantDictionaryKeys.length > 0) {\n onStatusUpdate?.(\n orderedDistantDictionaryKeys.map((key) => ({\n dictionaryKey: key,\n type: 'remote',\n status: 'pending',\n }))\n );\n }\n\n const distantDictionariesData = await fetchDistantDictionaries(\n {\n dictionaryKeys: orderedDistantDictionaryKeys,\n },\n onStatusUpdate\n );\n\n const distantDictionaries: Dictionary[] = formatDistantDictionaries(\n distantDictionariesData\n );\n\n // Fetching by key also returns the up-to-date siblings of a stale\n // dictionary, so drop the cached copies the remote just sent back.\n const fetchedDictionaryIds = new Set(\n distantDictionaries.map((dictionary) => dictionary.id)\n );\n\n const notRefetchedCachedDictionaries = cachedDictionaries.filter(\n (dictionary) => !fetchedDictionaryIds.has(dictionary.id)\n );\n\n return [...notRefetchedCachedDictionaries, ...distantDictionaries];\n } catch (error) {\n options?.onError?.(error as Error);\n return [];\n } finally {\n options?.onStopRemoteCheck?.();\n }\n};\n"],"mappings":";;;;;AAWA,MAAa,6BACX,iBAEA,aAAa,KAAK,UAAU;CAC1B,GAAG;CACH,SAAS,GAAG,KAAK,IAAI,YAAY,KAAK;CACtC,UAAU;AACZ,EAAE;AAEJ,MAAa,yBAAyB,OACpC,gBAAgB,iBAAiB,GACjC,gBACA,YAK0B;CAC1B,MAAM,EAAE,WAAW;CACnB,MAAM,2BAA2B,sBAAsB,aAAa;CAIpE,IAAI,CAF0B,QAAQ,OAAO,YAAY,OAAO,YAEvC,GAAG,OAAO,CAAC;CAEpC,IAAI;EACF,SAAS,qBAAqB;EAG9B,MAAM,EAAE,8BAA8B,6BACpC,MAAM,OAAO;EAQf,MAAM,oCAGK,MATQ,6BAA6B,aAI/B,CAAC,CAAC,+BAA+B,EAKd,CAAC;EAErC,IAAI,CAAC,kCACH,MAAM,IAAI,MAAM,+BAA+B;EAGjD,MAAM,wBAAwB,OAAO,QACnC,gCACF,CAAC,CAAC,QAAQ,CAAC,cAAc,UAAU;GAEjC,IAAI,CAAC,KAAK,WAAW,OAAO;GAG5B,MAAM,QAAgC,yBACpC,KAAK,IACN,EAAE,MAAM,eAAe,WAAW,OAAO,YAAY;GACtD,IAAI,CAAC,OAAO,OAAO;GAEnB,MAAM,oBAAqB,OAAe;GAK1C,MAAM,iBACJ,OAAO,sBAAsB,WACzB,oBACA,oBACE,IAAI,KAAK,iBAAiB,CAAC,CAAC,QAAQ,IACpC;GAGR,IAAI,OAAO,mBAAmB,UAAU,OAAO;GAE/C,OAAO,KAAK,YAAY;EAC1B,CAAC;EAMD,MAAM,qBAJgD,OAAO,OAC3D,wBACF,CAAC,CAAC,KAG2B,CAAC,CAAC,QAAQ,eAAe;GAClD,MAAM,kBAAkB,WAAW,KAC/B,iCAAiC,WAAW,GAAG,EAAE,YACjD;GAEJ,MAAM,oBAAoB,WAAW;GAErC,MAAM,iBACJ,OAAO,sBAAsB,WACzB,oBACA,oBACE,IAAI,KAAK,iBAAiB,CAAC,CAAC,QAAQ,IACpC;GAGR,OACE,OAAO,mBAAmB,YAC1B,OAAO,oBAAoB,YAC3B,kBAAkB;EAEtB,CAAC;EAGH,IAAI,mBAAmB,SAAS,GAC9B,iBACE,mBAAmB,KAAK,gBAAgB;GACtC,eAAe,WAAW;GAC1B,MAAM;GACN,QAAQ;EACV,EAAE,CACJ;EAKF,MAAM,+BAA+B,CACnC,GAAG,IAAI,IAAI,sBAAsB,KAAK,GAAG,UAAU,KAAK,GAAG,CAAC,CAC9D,CAAC,CAAC,KAAK,kBAAkB;EAGzB,IAAI,6BAA6B,SAAS,GACxC,iBACE,6BAA6B,KAAK,SAAS;GACzC,eAAe;GACf,MAAM;GACN,QAAQ;EACV,EAAE,CACJ;EAGF,MAAM,0BAA0B,MAAM,yBACpC,EACE,gBAAgB,6BAClB,GACA,cACF;EAEA,MAAM,sBAAoC,0BACxC,uBACF;EAIA,MAAM,uBAAuB,IAAI,IAC/B,oBAAoB,KAAK,eAAe,WAAW,EAAE,CACvD;EAMA,OAAO,CAAC,GAJ+B,mBAAmB,QACvD,eAAe,CAAC,qBAAqB,IAAI,WAAW,EAAE,CAGjB,GAAG,GAAG,mBAAmB;CACnE,SAAS,OAAO;EACd,SAAS,UAAU,KAAc;EACjC,OAAO,CAAC;CACV,UAAU;EACR,SAAS,oBAAoB;CAC/B;AACF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDirectoryExcluded } from "./isDirectoryExcluded.mjs";
|
|
2
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
2
3
|
import { TRAVERSE_PATTERN } from "@intlayer/config/defaultValues";
|
|
3
4
|
import { normalizePath } from "@intlayer/config/utils";
|
|
4
5
|
import fg from "fast-glob";
|
|
@@ -9,27 +10,24 @@ import fg from "fast-glob";
|
|
|
9
10
|
*/
|
|
10
11
|
const normalizeToArray = (value) => Array.isArray(value) ? value : [value];
|
|
11
12
|
/**
|
|
12
|
-
* Remove directories
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Remove directories nested in others so files are never scanned twice.
|
|
14
|
+
* A nested directory is kept as its own root when the exclude patterns would
|
|
15
|
+
* drop it from its parent's scan (e.g. `<root>/dist` under `**\/dist/**`).
|
|
16
|
+
*
|
|
17
|
+
* Example: ['/root', '/root/src', '/root/dist'] → ['/root', '/root/dist']
|
|
15
18
|
*/
|
|
16
|
-
const getDistinctRootDirs = (dirs) => {
|
|
19
|
+
const getDistinctRootDirs = (dirs, excludePatterns) => {
|
|
17
20
|
const uniqueDirs = Array.from(new Set(dirs.map((dir) => resolve(dir))));
|
|
18
21
|
uniqueDirs.sort((a, b) => a.length - b.length);
|
|
19
22
|
return uniqueDirs.reduce((acc, dir) => {
|
|
20
23
|
if (!acc.some((parent) => {
|
|
21
|
-
const
|
|
22
|
-
return !
|
|
24
|
+
const relativePath = relative(parent, dir);
|
|
25
|
+
return relativePath !== "" && !relativePath.startsWith("..") && !isAbsolute(relativePath) && !isDirectoryExcluded(relativePath, excludePatterns);
|
|
23
26
|
})) acc.push(dir);
|
|
24
27
|
return acc;
|
|
25
28
|
}, []);
|
|
26
29
|
};
|
|
27
30
|
/**
|
|
28
|
-
* Returns true when the resolved path passes through a `node_modules` segment.
|
|
29
|
-
* Works on both POSIX and Windows paths.
|
|
30
|
-
*/
|
|
31
|
-
const isInsideNodeModules = (dir) => resolve(dir).split(sep).includes("node_modules");
|
|
32
|
-
/**
|
|
33
31
|
* Default exclude patterns derived from TRAVERSE_PATTERN.
|
|
34
32
|
* Extracted once so the function body can reference them without re-computing.
|
|
35
33
|
*/
|
|
@@ -41,18 +39,18 @@ const DEFAULT_EXCLUDE_PATTERNS = TRAVERSE_PATTERN.filter((p) => typeof p === "st
|
|
|
41
39
|
* patterns, negation patterns embedded in `traversePattern`, and optional
|
|
42
40
|
* dot-file inclusion.
|
|
43
41
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
42
|
+
* `codeDir` entries are more precise than the exclude patterns: a `codeDir`
|
|
43
|
+
* inside an excluded directory (e.g. a design-system `dist` or a package in
|
|
44
|
+
* `node_modules`) is scanned as its own root, so the exclusion only applies
|
|
45
|
+
* below it.
|
|
48
46
|
*
|
|
49
47
|
* @example
|
|
50
48
|
* // Single root with excludes
|
|
51
49
|
* const files = buildComponentFilesList(config);
|
|
52
50
|
*
|
|
53
51
|
* @example
|
|
54
|
-
* // Design-system
|
|
55
|
-
* // intlayer.config.ts: { content: { codeDir: ['node_modules/my-ds/
|
|
52
|
+
* // Design-system build output is still scanned
|
|
53
|
+
* // intlayer.config.ts: { content: { codeDir: ['node_modules/my-ds/dist'] } }
|
|
56
54
|
* const files = buildComponentFilesList(config);
|
|
57
55
|
*/
|
|
58
56
|
const buildComponentFilesList = (config, excludePattern) => {
|
|
@@ -67,23 +65,13 @@ const buildComponentFilesList = (config, excludePattern) => {
|
|
|
67
65
|
...excludePattern ?? [],
|
|
68
66
|
...contentDeclarationPattern
|
|
69
67
|
])).filter((pattern) => typeof pattern === "string").map(normalizePath);
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
const normalCodeDirs = resolvedCodeDirs.filter((dir) => !isInsideNodeModules(dir));
|
|
73
|
-
const normalFiles = getDistinctRootDirs([config.system.baseDir, ...normalCodeDirs]).flatMap((root) => fg.sync(patterns, {
|
|
68
|
+
const roots = getDistinctRootDirs([config.system.baseDir, ...config.content.codeDir ?? []], baseExcludePatterns);
|
|
69
|
+
return Array.from(new Set(roots.flatMap((root) => fg.sync(patterns, {
|
|
74
70
|
cwd: root,
|
|
75
71
|
ignore: baseExcludePatterns,
|
|
76
72
|
absolute: true,
|
|
77
73
|
dot: true
|
|
78
|
-
}));
|
|
79
|
-
const nodeModulesExcludePatterns = baseExcludePatterns.filter((pattern) => !pattern.includes("node_modules"));
|
|
80
|
-
const nodeModulesFiles = inNodeModulesCodeDirs.flatMap((dir) => fg.sync(patterns, {
|
|
81
|
-
cwd: dir,
|
|
82
|
-
ignore: nodeModulesExcludePatterns,
|
|
83
|
-
absolute: true,
|
|
84
|
-
dot: false
|
|
85
|
-
}));
|
|
86
|
-
return Array.from(/* @__PURE__ */ new Set([...normalFiles, ...nodeModulesFiles]));
|
|
74
|
+
}))));
|
|
87
75
|
};
|
|
88
76
|
|
|
89
77
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildComponentFilesList.mjs","names":[],"sources":["../../../src/utils/buildComponentFilesList.ts"],"sourcesContent":["import { isAbsolute, relative, resolve
|
|
1
|
+
{"version":3,"file":"buildComponentFilesList.mjs","names":[],"sources":["../../../src/utils/buildComponentFilesList.ts"],"sourcesContent":["import { isAbsolute, relative, resolve } from 'node:path';\nimport { TRAVERSE_PATTERN } from '@intlayer/config/defaultValues';\nimport { normalizePath } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport fg from 'fast-glob';\nimport { isDirectoryExcluded } from './isDirectoryExcluded';\n\n/**\n * Normalizes a pattern value to an array\n */\nconst normalizeToArray = <T>(value: T | T[]): T[] =>\n Array.isArray(value) ? value : [value];\n\n/**\n * Remove directories nested in others so files are never scanned twice.\n * A nested directory is kept as its own root when the exclude patterns would\n * drop it from its parent's scan (e.g. `<root>/dist` under `**\\/dist/**`).\n *\n * Example: ['/root', '/root/src', '/root/dist'] → ['/root', '/root/dist']\n */\nconst getDistinctRootDirs = (\n dirs: string[],\n excludePatterns: string[]\n): string[] => {\n const uniqueDirs = Array.from(new Set(dirs.map((dir) => resolve(dir))));\n uniqueDirs.sort((a, b) => a.length - b.length);\n\n return uniqueDirs.reduce((acc: string[], dir) => {\n const isCoveredByParent = acc.some((parent) => {\n const relativePath = relative(parent, dir);\n const isNested =\n relativePath !== '' &&\n !relativePath.startsWith('..') &&\n !isAbsolute(relativePath);\n\n return isNested && !isDirectoryExcluded(relativePath, excludePatterns);\n });\n if (!isCoveredByParent) acc.push(dir);\n\n return acc;\n }, []);\n};\n\n/**\n * Default exclude patterns derived from TRAVERSE_PATTERN.\n * Extracted once so the function body can reference them without re-computing.\n */\nconst DEFAULT_EXCLUDE_PATTERNS: string[] = TRAVERSE_PATTERN.filter(\n (p): p is string => typeof p === 'string' && p.startsWith('!')\n).map((p) => p.slice(1));\n\n/**\n * Builds a deduplicated list of absolute file paths matching the given patterns.\n *\n * Handles multiple root directories (deduplicates overlapping roots), exclude\n * patterns, negation patterns embedded in `traversePattern`, and optional\n * dot-file inclusion.\n *\n * `codeDir` entries are more precise than the exclude patterns: a `codeDir`\n * inside an excluded directory (e.g. a design-system `dist` or a package in\n * `node_modules`) is scanned as its own root, so the exclusion only applies\n * below it.\n *\n * @example\n * // Single root with excludes\n * const files = buildComponentFilesList(config);\n *\n * @example\n * // Design-system build output is still scanned\n * // intlayer.config.ts: { content: { codeDir: ['node_modules/my-ds/dist'] } }\n * const files = buildComponentFilesList(config);\n */\nexport const buildComponentFilesList = (\n config: IntlayerConfig,\n excludePattern?: string[]\n): string[] => {\n const traversePattern = config.build.traversePattern;\n const compilerTransformPattern = config.compiler.transformPattern;\n const contentDeclarationPattern = config.content.fileExtensions.map(\n (ext) => `/**/*${ext}`\n );\n\n const patterns = [\n ...traversePattern,\n ...normalizeToArray(compilerTransformPattern),\n ]\n .filter((pattern) => typeof pattern === 'string')\n .filter((pattern) => !pattern.startsWith('!'))\n .map(normalizePath);\n\n // User-supplied negations from traversePattern\n const userExcludes = traversePattern\n .filter(\n (pattern): pattern is string =>\n typeof pattern === 'string' && pattern.startsWith('!')\n )\n .map((pattern) => pattern.slice(1));\n\n // Full exclude list: defaults (from TRAVERSE_PATTERN) + user overrides + caller extras + content files.\n // DEFAULT_EXCLUDE_PATTERNS acts as a safety floor — if the user provides a\n // partial traversePattern that omits some defaults, they are still applied.\n const baseExcludePatterns = Array.from(\n new Set([\n ...DEFAULT_EXCLUDE_PATTERNS,\n ...userExcludes,\n ...(excludePattern ?? []),\n ...contentDeclarationPattern,\n ])\n )\n .filter((pattern): pattern is string => typeof pattern === 'string')\n .map(normalizePath);\n\n const roots = getDistinctRootDirs(\n [config.system.baseDir, ...(config.content.codeDir ?? [])],\n baseExcludePatterns\n );\n\n return Array.from(\n new Set(\n roots.flatMap((root) =>\n fg.sync(patterns, {\n cwd: root,\n ignore: baseExcludePatterns,\n absolute: true,\n dot: true, // needed for .intlayer and similar\n })\n )\n )\n );\n};\n"],"mappings":";;;;;;;;;;AAUA,MAAM,oBAAuB,UAC3B,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;;;;;;;;AASvC,MAAM,uBACJ,MACA,oBACa;CACb,MAAM,aAAa,MAAM,KAAK,IAAI,IAAI,KAAK,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,CAAC;CACtE,WAAW,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;CAE7C,OAAO,WAAW,QAAQ,KAAe,QAAQ;EAU/C,IAAI,CATsB,IAAI,MAAM,WAAW;GAC7C,MAAM,eAAe,SAAS,QAAQ,GAAG;GAMzC,OAJE,iBAAiB,MACjB,CAAC,aAAa,WAAW,IAAI,KAC7B,CAAC,WAAW,YAAY,KAEP,CAAC,oBAAoB,cAAc,eAAe;EACvE,CACqB,GAAG,IAAI,KAAK,GAAG;EAEpC,OAAO;CACT,GAAG,CAAC,CAAC;AACP;;;;;AAMA,MAAM,2BAAqC,iBAAiB,QACzD,MAAmB,OAAO,MAAM,YAAY,EAAE,WAAW,GAAG,CAC/D,CAAC,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;AAuBvB,MAAa,2BACX,QACA,mBACa;CACb,MAAM,kBAAkB,OAAO,MAAM;CACrC,MAAM,2BAA2B,OAAO,SAAS;CACjD,MAAM,4BAA4B,OAAO,QAAQ,eAAe,KAC7D,QAAQ,QAAQ,KACnB;CAEA,MAAM,WAAW,CACf,GAAG,iBACH,GAAG,iBAAiB,wBAAwB,CAC9C,CAAC,CACE,QAAQ,YAAY,OAAO,YAAY,QAAQ,CAAC,CAChD,QAAQ,YAAY,CAAC,QAAQ,WAAW,GAAG,CAAC,CAAC,CAC7C,IAAI,aAAa;CAGpB,MAAM,eAAe,gBAClB,QACE,YACC,OAAO,YAAY,YAAY,QAAQ,WAAW,GAAG,CACzD,CAAC,CACA,KAAK,YAAY,QAAQ,MAAM,CAAC,CAAC;CAKpC,MAAM,sBAAsB,MAAM,qBAChC,IAAI,IAAI;EACN,GAAG;EACH,GAAG;EACH,GAAI,kBAAkB,CAAC;EACvB,GAAG;CACL,CAAC,CACH,CAAC,CACE,QAAQ,YAA+B,OAAO,YAAY,QAAQ,CAAC,CACnE,IAAI,aAAa;CAEpB,MAAM,QAAQ,oBACZ,CAAC,OAAO,OAAO,SAAS,GAAI,OAAO,QAAQ,WAAW,CAAC,CAAE,GACzD,mBACF;CAEA,OAAO,MAAM,KACX,IAAI,IACF,MAAM,SAAS,SACb,GAAG,KAAK,UAAU;EAChB,KAAK;EACL,QAAQ;EACR,UAAU;EACV,KAAK;CACP,CAAC,CACH,CACF,CACF;AACF"}
|
|
@@ -21,7 +21,7 @@ const setAtPath = (root, path, value) => {
|
|
|
21
21
|
for (let i = 0; i < path.length - 1; i++) {
|
|
22
22
|
const key = path[i];
|
|
23
23
|
if (key === void 0) continue;
|
|
24
|
-
const isNextIndex = typeof
|
|
24
|
+
const isNextIndex = typeof path[i + 1] === "number";
|
|
25
25
|
if (typeof key === "number") {
|
|
26
26
|
if (!Array.isArray(current)) throw new Error(`Expected array at path segment ${i}`);
|
|
27
27
|
if (current[key] === void 0) current[key] = isNextIndex ? [] : {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunkJSON.mjs","names":[],"sources":["../../../src/utils/chunkJSON.ts"],"sourcesContent":["/**\n * Split & reassemble JSON by character budget.\n * - Measures serialized size using JSON.stringify(..).length (characters).\n * - Ensures each chunk is itself valid JSON.\n * - Very large strings are split into safe pieces using getChunk and re-concatenated on assemble.\n * - Protects against circular structures (JSON can't serialize those anyway).\n */\n\nimport { getChunk } from './getChunk';\n\ntype JSONPrimitive = string | number | boolean | null;\ntype JSONValue = JSONPrimitive | JSONObject | JSONArray;\n\nexport type JSONObject = {\n [k: string]: JSONValue;\n};\n\ntype JSONArray = JSONValue[];\n\ntype Path = Array<string | number>;\n\ntype SetPatch = { op: 'set'; path: Path; value: JSONValue };\ntype StrAppendPatch = {\n op: 'str-append';\n path: Path;\n value: string; // part of a longer string\n index: number;\n total: number;\n};\ntype Patch = SetPatch | StrAppendPatch;\n\ntype RootType = 'object' | 'array';\n\nexport type JsonChunk = {\n schemaVersion: 1;\n index: number;\n total: number;\n rootType: RootType;\n checksum: string; // hash of the full original JSON string\n entries: Patch[];\n};\n\nconst isObject = (val: unknown): val is Record<string, unknown> => {\n return typeof val === 'object' && val !== null && !Array.isArray(val);\n};\n\nconst computeDjb2 = (str: string): string => {\n // Simple 32-bit hash; deterministic & fast\n let hash = 5381;\n\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) + hash) ^ str.charCodeAt(i);\n }\n\n // convert to unsigned hex\n return (hash >>> 0).toString(16).padStart(8, '0');\n};\n\nconst setAtPath = (root: any, path: Path, value: JSONValue) => {\n let current = root;\n\n for (let i = 0; i < path.length - 1; i++) {\n const key = path[i];\n if (key === undefined) continue;\n\n const isNextIndex = typeof nextKey === 'number';\n\n if (typeof key === 'number') {\n if (!Array.isArray(current)) {\n throw new Error(`Expected array at path segment ${i}`);\n }\n\n if (current[key] === undefined) {\n current[key] = isNextIndex ? [] : {};\n }\n\n current = current[key];\n } else {\n if (!isObject(current)) {\n throw new Error(`Expected object at path segment ${i}`);\n }\n\n if (!(key in current)) {\n (current as Record<string, any>)[key] = isNextIndex ? [] : {};\n }\n\n current = (current as Record<string, any>)[key];\n }\n }\n\n const last = path[path.length - 1];\n\n if (last === undefined) return;\n\n if (typeof last === 'number') {\n if (!Array.isArray(current)) {\n throw new Error(`Expected array at final segment`);\n }\n\n current[last] = value as any;\n } else {\n if (!isObject(current)) {\n throw new Error(`Expected object at final segment`);\n }\n\n (current as Record<string, any>)[last] = value as any;\n }\n};\n\nconst pathKey = (path: Path): string => {\n // stable key for grouping string parts\n return JSON.stringify(path);\n};\n\n/**\n * Split a string into parts using getChunk with a charLength budget per part.\n */\nconst splitStringByBudget = (\n str: string,\n maxCharsPerPart: number\n): string[] => {\n if (maxCharsPerPart <= 0) {\n throw new Error('maxChars must be > 0');\n }\n\n const output: string[] = [];\n let offset = 0;\n\n while (offset < str.length) {\n const part = getChunk(str, {\n charStart: offset,\n charLength: maxCharsPerPart,\n });\n\n if (!part) break;\n\n output.push(part);\n offset += part.length;\n }\n\n return output;\n};\n\n/**\n * Flatten JSON into patches (leaf writes). Strings too large to fit in a single\n * chunk are yielded as multiple str-append patches.\n */\nconst flattenToPatches = (\n value: JSONValue,\n maxCharsPerChunk: number,\n path: Path = [],\n seen = new WeakSet<object>()\n): Patch[] => {\n // Conservative per-entry cap so a single entry fits into an empty chunk with envelope overhead.\n // (Envelope ~ a few hundred chars; we keep a comfortable margin.)\n const maxStringPiece = Math.max(\n 1,\n Math.floor((maxCharsPerChunk - 400) * 0.8)\n );\n\n const patches: Patch[] = [];\n\n const walk = (currentValue: JSONValue, currentPath: Path) => {\n if (typeof currentValue === 'string') {\n // If the serialized patch wouldn't fit, split the string into multiple parts\n // and encode as a split-node sentinel with numbered keys.\n const testPatch: SetPatch = {\n op: 'set',\n path: currentPath,\n value: currentValue,\n };\n const testLen = JSON.stringify(testPatch).length + 150; // margin\n\n if (testLen <= maxCharsPerChunk) {\n patches.push(testPatch);\n return;\n }\n\n // Use getChunk-based splitting to produce stable parts\n const parts = splitStringByBudget(currentValue, maxStringPiece);\n\n // Emit split-node metadata and parts as individual leaf writes\n patches.push({\n op: 'set',\n path: [...currentPath, '__splittedType'],\n value: 'string',\n });\n patches.push({\n op: 'set',\n path: [...currentPath, '__total'],\n value: parts.length,\n });\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n\n if (part !== undefined) {\n patches.push({\n op: 'set',\n path: [...currentPath, String(i + 1)],\n value: part,\n });\n }\n }\n\n return;\n }\n\n if (currentValue === null || typeof currentValue !== 'object') {\n patches.push({ op: 'set', path: currentPath, value: currentValue });\n return;\n }\n\n if (seen.has(currentValue as object)) {\n throw new Error('Cannot serialize circular structures to JSON.');\n }\n\n seen.add(currentValue as object);\n\n if (Array.isArray(currentValue)) {\n for (let i = 0; i < currentValue.length; i++) {\n const item = currentValue[i];\n if (item !== undefined) {\n walk(item as JSONValue, [...currentPath, i]);\n }\n }\n } else {\n for (const key of Object.keys(currentValue)) {\n const childVal = (currentValue as JSONObject)[key];\n\n if (childVal !== undefined) {\n walk(childVal, [...currentPath, key]);\n }\n }\n }\n\n seen.delete(currentValue as object);\n };\n\n walk(value, path);\n return patches;\n};\n\n/**\n * Split JSON into chunks constrained by character count of serialized chunk.\n */\nexport const chunkJSON = (\n value: JSONObject | JSONArray,\n maxChars: number\n): JsonChunk[] => {\n if (!isObject(value) && !Array.isArray(value)) {\n throw new Error('Root must be an object or array.');\n }\n\n if (maxChars < 500) {\n // You can lower this if you truly need; recommended to keep some envelope headroom.\n throw new Error('maxChars is too small. Use at least 500 characters.');\n }\n\n const rootType: RootType = Array.isArray(value) ? 'array' : 'object';\n let sourceString: string;\n\n try {\n sourceString = JSON.stringify(value);\n } catch {\n // Provide a deterministic error message for circular refs\n throw new Error('Cannot serialize circular structures to JSON.');\n }\n\n const checksum = computeDjb2(sourceString);\n const allPatches = flattenToPatches(value as JSONValue, maxChars);\n\n const chunks: JsonChunk[] = [];\n let currentChunk: JsonChunk = {\n schemaVersion: 1,\n index: 0, // provisional\n total: 0, // provisional\n rootType,\n checksum,\n entries: [],\n };\n\n const emptyEnvelopeSize = JSON.stringify({\n ...currentChunk,\n entries: [],\n }).length;\n\n const tryFlush = () => {\n if (currentChunk.entries.length > 0) {\n chunks.push(currentChunk);\n currentChunk = {\n schemaVersion: 1,\n index: 0,\n total: 0,\n rootType,\n checksum,\n entries: [],\n };\n }\n };\n\n for (const patch of allPatches) {\n // Would adding this patch exceed maxChars?\n const withPatchSize =\n emptyEnvelopeSize +\n JSON.stringify(currentChunk.entries).length + // current entries array\n (currentChunk.entries.length ? 1 : 0) + // possible comma\n JSON.stringify(patch).length;\n\n if (withPatchSize <= maxChars) {\n currentChunk.entries.push(patch);\n } else {\n // Start a new chunk if current has items\n if (currentChunk.entries.length > 0) {\n tryFlush();\n }\n\n // Ensure single patch fits into an empty chunk\n const singleSize = emptyEnvelopeSize + JSON.stringify([patch]).length;\n\n if (singleSize > maxChars) {\n // This should only happen for massive strings, which we pre-split;\n // if it happens for a non-string, we cannot split further.\n throw new Error(\n 'A single entry exceeds maxChars and cannot be split. Reduce entry size or increase maxChars.'\n );\n }\n\n currentChunk.entries.push(patch);\n }\n }\n\n tryFlush();\n\n // Ensure at least one chunk exists (even for empty root)\n if (chunks.length === 0) {\n chunks.push({\n schemaVersion: 1,\n index: 0,\n total: 0, // provisional\n rootType,\n checksum,\n entries: [],\n });\n }\n\n // Finalize indices & totals\n const totalChunks = chunks.length;\n\n chunks.forEach((chunk, index) => {\n chunk.index = index;\n chunk.total = totalChunks;\n });\n\n return chunks;\n};\n\n/**\n * Reassemble JSON from chunks.\n * - Validates checksums and indices.\n * - Applies 'set' patches and merges string pieces from 'str-append'.\n */\n/**\n * Reconstruct content from a single chunk without validation.\n * Useful for processing individual chunks in a pipeline where you don't have all chunks yet.\n * Note: This will only reconstruct the partial content contained in this chunk.\n */\nexport const reconstructFromSingleChunk = (\n chunk: JsonChunk\n): JSONObject | JSONArray => {\n const root: any = chunk.rootType === 'array' ? [] : {};\n\n // Apply all 'set' patches from this chunk\n for (const entry of chunk.entries) {\n if (entry.op === 'set') {\n setAtPath(root, entry.path, entry.value);\n }\n }\n\n // Reconcile split-node sentinels for strings/arrays\n // When reconstructing from a single chunk, we may have incomplete split nodes\n const reconcileSplitNodes = (node: any): any => {\n if (node === null || typeof node !== 'object') return node;\n\n if (Array.isArray(node)) {\n for (let i = 0; i < node.length; i++) {\n node[i] = reconcileSplitNodes(node[i]);\n }\n return node;\n }\n\n // string split-node\n if ((node as any)['__splittedType'] === 'string') {\n const total = (node as any)['__total'];\n\n if (typeof total !== 'number' || total <= 0) {\n // Invalid split node, return as-is\n return node;\n }\n\n const parts: string[] = [];\n let hasAllParts = true;\n\n for (let i = 1; i <= total; i++) {\n const piece = (node as any)[String(i)];\n\n if (typeof piece !== 'string') {\n hasAllParts = false;\n break;\n }\n\n parts.push(piece);\n }\n\n // Only reconstruct if we have all parts, otherwise return the node as-is\n if (hasAllParts) {\n return parts.join('');\n }\n\n return node;\n }\n\n // array split-node (optional support)\n if ((node as any)['__splittedType'] === 'array') {\n const total = (node as any)['__total'];\n\n if (typeof total !== 'number' || total < 0) {\n // Invalid split node, return as-is\n return node;\n }\n\n const output: any[] = [];\n let hasAllParts = true;\n\n for (let i = 1; i <= total; i++) {\n const slice = (node as any)[String(i)];\n\n if (!Array.isArray(slice)) {\n hasAllParts = false;\n break;\n }\n\n for (let j = 0; j < slice.length; j++) {\n output.push(reconcileSplitNodes(slice[j]));\n }\n }\n\n // Only reconstruct if we have all parts\n if (hasAllParts) {\n return output;\n }\n\n return node;\n }\n\n // walk normal object\n for (const key of Object.keys(node)) {\n node[key] = reconcileSplitNodes(node[key]);\n }\n\n return node;\n };\n\n return reconcileSplitNodes(root);\n};\n\nexport const assembleJSON = (chunks: JsonChunk[]): JSONObject | JSONArray => {\n if (!chunks || chunks.length === 0) {\n throw new Error('No chunks provided.');\n }\n\n // Basic validation & sort\n const sorted = [...chunks].sort((a, b) => a.index - b.index);\n const firstChunk = sorted[0];\n\n if (!firstChunk) {\n throw new Error('No chunks provided.');\n }\n\n const { checksum, rootType } = firstChunk;\n const schemaVersion = 1;\n\n for (let i = 0; i < sorted.length; i++) {\n const chunk = sorted[i];\n\n if (!chunk) continue;\n\n if (chunk.schemaVersion !== schemaVersion) {\n console.error('Unsupported schemaVersion.', {\n cause: chunk,\n schemaVersion,\n });\n throw new Error('Unsupported schemaVersion.');\n }\n\n if (chunk.rootType !== rootType) {\n console.error('Chunks rootType mismatch.', {\n cause: chunk,\n rootType,\n });\n throw new Error('Chunks rootType mismatch.');\n }\n\n if (chunk.checksum !== checksum) {\n console.error('Chunks checksum mismatch (different source objects?).', {\n cause: chunk,\n checksum,\n });\n throw new Error('Chunks checksum mismatch (different source objects?).');\n }\n\n if (chunk.index !== i) {\n console.error('Chunk indices are not contiguous or sorted.', {\n cause: chunk,\n index: chunk.index,\n i,\n });\n throw new Error('Chunk indices are not contiguous or sorted.');\n }\n\n // Defer total check until after reconstruction to prefer more specific errors\n }\n\n const root: any = rootType === 'array' ? [] : {};\n\n // Collect string parts by path\n const stringParts = new Map<\n string,\n { path: Path; total: number; received: StrAppendPatch[] }\n >();\n\n const applySet = (patch: SetPatch) =>\n setAtPath(root, patch.path, patch.value);\n\n for (const chunk of sorted) {\n for (const entry of chunk.entries) {\n if (entry.op === 'set') {\n applySet(entry);\n } else {\n const key = pathKey(entry.path);\n const record = stringParts.get(key) ?? {\n path: entry.path,\n total: entry.total,\n received: [],\n };\n\n if (record.total !== entry.total) {\n throw new Error('Inconsistent string part totals for a path.');\n }\n\n record.received.push(entry);\n stringParts.set(key, record);\n }\n }\n }\n\n // Stitch strings\n for (const { path, total, received } of stringParts.values()) {\n if (received.length !== total) {\n throw new Error('Missing string parts for a path; incomplete chunk set.');\n }\n\n received.sort((a, b) => a.index - b.index);\n const fullString = received.map((part) => part.value).join('');\n setAtPath(root, path, fullString);\n }\n\n // Reconcile split-node sentinels for strings/arrays after all patches applied\n const reconcileSplitNodes = (node: any): any => {\n if (node === null || typeof node !== 'object') return node;\n\n if (Array.isArray(node)) {\n for (let i = 0; i < node.length; i++) {\n node[i] = reconcileSplitNodes(node[i]);\n }\n return node;\n }\n\n // string split-node\n if ((node as any)['__splittedType'] === 'string') {\n const total = (node as any)['__total'];\n\n if (typeof total !== 'number' || total <= 0) {\n throw new Error('Invalid split-node total for a path.');\n }\n\n const parts: string[] = [];\n\n for (let i = 1; i <= total; i++) {\n const piece = (node as any)[String(i)];\n\n if (typeof piece !== 'string') {\n throw new Error(\n 'Missing string parts for a path; incomplete chunk set.'\n );\n }\n\n parts.push(piece);\n }\n\n return parts.join('');\n }\n\n // array split-node (optional support)\n if ((node as any)['__splittedType'] === 'array') {\n const total = (node as any)['__total'];\n\n if (typeof total !== 'number' || total < 0) {\n throw new Error('Invalid split-node total for a path.');\n }\n\n const output: any[] = [];\n\n for (let i = 1; i <= total; i++) {\n const slice = (node as any)[String(i)];\n\n if (!Array.isArray(slice)) {\n throw new Error(\n 'Missing string parts for a path; incomplete chunk set.'\n );\n }\n\n for (let j = 0; j < slice.length; j++) {\n output.push(reconcileSplitNodes(slice[j]));\n }\n }\n\n return output;\n }\n\n // walk normal object\n for (const key of Object.keys(node)) {\n node[key] = reconcileSplitNodes(node[key]);\n }\n\n return node;\n };\n\n const reconciled = reconcileSplitNodes(root);\n\n // Now validate totals match provided count\n for (let i = 0; i < sorted.length; i++) {\n const chunk = sorted[i];\n\n if (!chunk) continue;\n\n if (chunk.total !== sorted.length) {\n throw new Error(\n `Chunk total does not match provided count. Expected ${sorted.length}, but chunk ${i} has total=${chunk.total}`\n );\n }\n }\n\n return reconciled;\n};\n\n/* -------------------------------------------\n * Example usage\n * -------------------------------------------\nconst big: JSONObject = {\n title: \"Document\",\n content: \"…a very very long text…\",\n items: Array.from({ length: 2000 }, (_, i) => ({ id: i, label: `Item ${i}` }))\n};\n\n// Split to ~16k-char chunks\nconst chunks = chunkJSON(big, 16_000);\n\n// Send each `chunks[i]` as JSON to your backend.\n// Later, reassemble:\nconst restored = assembleJSON(chunks);\nconsole.log(JSON.stringify(restored) === JSON.stringify(big)); // true\n*/\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chunkJSON.mjs","names":[],"sources":["../../../src/utils/chunkJSON.ts"],"sourcesContent":["/**\n * Split & reassemble JSON by character budget.\n * - Measures serialized size using JSON.stringify(..).length (characters).\n * - Ensures each chunk is itself valid JSON.\n * - Very large strings are split into safe pieces using getChunk and re-concatenated on assemble.\n * - Protects against circular structures (JSON can't serialize those anyway).\n */\n\nimport { getChunk } from './getChunk';\n\ntype JSONPrimitive = string | number | boolean | null;\ntype JSONValue = JSONPrimitive | JSONObject | JSONArray;\n\nexport type JSONObject = {\n [k: string]: JSONValue;\n};\n\ntype JSONArray = JSONValue[];\n\ntype Path = Array<string | number>;\n\ntype SetPatch = { op: 'set'; path: Path; value: JSONValue };\ntype StrAppendPatch = {\n op: 'str-append';\n path: Path;\n value: string; // part of a longer string\n index: number;\n total: number;\n};\ntype Patch = SetPatch | StrAppendPatch;\n\ntype RootType = 'object' | 'array';\n\nexport type JsonChunk = {\n schemaVersion: 1;\n index: number;\n total: number;\n rootType: RootType;\n checksum: string; // hash of the full original JSON string\n entries: Patch[];\n};\n\nconst isObject = (val: unknown): val is Record<string, unknown> => {\n return typeof val === 'object' && val !== null && !Array.isArray(val);\n};\n\nconst computeDjb2 = (str: string): string => {\n // Simple 32-bit hash; deterministic & fast\n let hash = 5381;\n\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) + hash) ^ str.charCodeAt(i);\n }\n\n // convert to unsigned hex\n return (hash >>> 0).toString(16).padStart(8, '0');\n};\n\nconst setAtPath = (root: any, path: Path, value: JSONValue) => {\n let current = root;\n\n for (let i = 0; i < path.length - 1; i++) {\n const key = path[i];\n if (key === undefined) continue;\n\n const nextKey = path[i + 1];\n const isNextIndex = typeof nextKey === 'number';\n\n if (typeof key === 'number') {\n if (!Array.isArray(current)) {\n throw new Error(`Expected array at path segment ${i}`);\n }\n\n if (current[key] === undefined) {\n current[key] = isNextIndex ? [] : {};\n }\n\n current = current[key];\n } else {\n if (!isObject(current)) {\n throw new Error(`Expected object at path segment ${i}`);\n }\n\n if (!(key in current)) {\n (current as Record<string, any>)[key] = isNextIndex ? [] : {};\n }\n\n current = (current as Record<string, any>)[key];\n }\n }\n\n const last = path[path.length - 1];\n\n if (last === undefined) return;\n\n if (typeof last === 'number') {\n if (!Array.isArray(current)) {\n throw new Error(`Expected array at final segment`);\n }\n\n current[last] = value as any;\n } else {\n if (!isObject(current)) {\n throw new Error(`Expected object at final segment`);\n }\n\n (current as Record<string, any>)[last] = value as any;\n }\n};\n\nconst pathKey = (path: Path): string => {\n // stable key for grouping string parts\n return JSON.stringify(path);\n};\n\n/**\n * Split a string into parts using getChunk with a charLength budget per part.\n */\nconst splitStringByBudget = (\n str: string,\n maxCharsPerPart: number\n): string[] => {\n if (maxCharsPerPart <= 0) {\n throw new Error('maxChars must be > 0');\n }\n\n const output: string[] = [];\n let offset = 0;\n\n while (offset < str.length) {\n const part = getChunk(str, {\n charStart: offset,\n charLength: maxCharsPerPart,\n });\n\n if (!part) break;\n\n output.push(part);\n offset += part.length;\n }\n\n return output;\n};\n\n/**\n * Flatten JSON into patches (leaf writes). Strings too large to fit in a single\n * chunk are yielded as multiple str-append patches.\n */\nconst flattenToPatches = (\n value: JSONValue,\n maxCharsPerChunk: number,\n path: Path = [],\n seen = new WeakSet<object>()\n): Patch[] => {\n // Conservative per-entry cap so a single entry fits into an empty chunk with envelope overhead.\n // (Envelope ~ a few hundred chars; we keep a comfortable margin.)\n const maxStringPiece = Math.max(\n 1,\n Math.floor((maxCharsPerChunk - 400) * 0.8)\n );\n\n const patches: Patch[] = [];\n\n const walk = (currentValue: JSONValue, currentPath: Path) => {\n if (typeof currentValue === 'string') {\n // If the serialized patch wouldn't fit, split the string into multiple parts\n // and encode as a split-node sentinel with numbered keys.\n const testPatch: SetPatch = {\n op: 'set',\n path: currentPath,\n value: currentValue,\n };\n const testLen = JSON.stringify(testPatch).length + 150; // margin\n\n if (testLen <= maxCharsPerChunk) {\n patches.push(testPatch);\n return;\n }\n\n // Use getChunk-based splitting to produce stable parts\n const parts = splitStringByBudget(currentValue, maxStringPiece);\n\n // Emit split-node metadata and parts as individual leaf writes\n patches.push({\n op: 'set',\n path: [...currentPath, '__splittedType'],\n value: 'string',\n });\n patches.push({\n op: 'set',\n path: [...currentPath, '__total'],\n value: parts.length,\n });\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n\n if (part !== undefined) {\n patches.push({\n op: 'set',\n path: [...currentPath, String(i + 1)],\n value: part,\n });\n }\n }\n\n return;\n }\n\n if (currentValue === null || typeof currentValue !== 'object') {\n patches.push({ op: 'set', path: currentPath, value: currentValue });\n return;\n }\n\n if (seen.has(currentValue as object)) {\n throw new Error('Cannot serialize circular structures to JSON.');\n }\n\n seen.add(currentValue as object);\n\n if (Array.isArray(currentValue)) {\n for (let i = 0; i < currentValue.length; i++) {\n const item = currentValue[i];\n if (item !== undefined) {\n walk(item as JSONValue, [...currentPath, i]);\n }\n }\n } else {\n for (const key of Object.keys(currentValue)) {\n const childVal = (currentValue as JSONObject)[key];\n\n if (childVal !== undefined) {\n walk(childVal, [...currentPath, key]);\n }\n }\n }\n\n seen.delete(currentValue as object);\n };\n\n walk(value, path);\n return patches;\n};\n\n/**\n * Split JSON into chunks constrained by character count of serialized chunk.\n */\nexport const chunkJSON = (\n value: JSONObject | JSONArray,\n maxChars: number\n): JsonChunk[] => {\n if (!isObject(value) && !Array.isArray(value)) {\n throw new Error('Root must be an object or array.');\n }\n\n if (maxChars < 500) {\n // You can lower this if you truly need; recommended to keep some envelope headroom.\n throw new Error('maxChars is too small. Use at least 500 characters.');\n }\n\n const rootType: RootType = Array.isArray(value) ? 'array' : 'object';\n let sourceString: string;\n\n try {\n sourceString = JSON.stringify(value);\n } catch {\n // Provide a deterministic error message for circular refs\n throw new Error('Cannot serialize circular structures to JSON.');\n }\n\n const checksum = computeDjb2(sourceString);\n const allPatches = flattenToPatches(value as JSONValue, maxChars);\n\n const chunks: JsonChunk[] = [];\n let currentChunk: JsonChunk = {\n schemaVersion: 1,\n index: 0, // provisional\n total: 0, // provisional\n rootType,\n checksum,\n entries: [],\n };\n\n const emptyEnvelopeSize = JSON.stringify({\n ...currentChunk,\n entries: [],\n }).length;\n\n const tryFlush = () => {\n if (currentChunk.entries.length > 0) {\n chunks.push(currentChunk);\n currentChunk = {\n schemaVersion: 1,\n index: 0,\n total: 0,\n rootType,\n checksum,\n entries: [],\n };\n }\n };\n\n for (const patch of allPatches) {\n // Would adding this patch exceed maxChars?\n const withPatchSize =\n emptyEnvelopeSize +\n JSON.stringify(currentChunk.entries).length + // current entries array\n (currentChunk.entries.length ? 1 : 0) + // possible comma\n JSON.stringify(patch).length;\n\n if (withPatchSize <= maxChars) {\n currentChunk.entries.push(patch);\n } else {\n // Start a new chunk if current has items\n if (currentChunk.entries.length > 0) {\n tryFlush();\n }\n\n // Ensure single patch fits into an empty chunk\n const singleSize = emptyEnvelopeSize + JSON.stringify([patch]).length;\n\n if (singleSize > maxChars) {\n // This should only happen for massive strings, which we pre-split;\n // if it happens for a non-string, we cannot split further.\n throw new Error(\n 'A single entry exceeds maxChars and cannot be split. Reduce entry size or increase maxChars.'\n );\n }\n\n currentChunk.entries.push(patch);\n }\n }\n\n tryFlush();\n\n // Ensure at least one chunk exists (even for empty root)\n if (chunks.length === 0) {\n chunks.push({\n schemaVersion: 1,\n index: 0,\n total: 0, // provisional\n rootType,\n checksum,\n entries: [],\n });\n }\n\n // Finalize indices & totals\n const totalChunks = chunks.length;\n\n chunks.forEach((chunk, index) => {\n chunk.index = index;\n chunk.total = totalChunks;\n });\n\n return chunks;\n};\n\n/**\n * Reassemble JSON from chunks.\n * - Validates checksums and indices.\n * - Applies 'set' patches and merges string pieces from 'str-append'.\n */\n/**\n * Reconstruct content from a single chunk without validation.\n * Useful for processing individual chunks in a pipeline where you don't have all chunks yet.\n * Note: This will only reconstruct the partial content contained in this chunk.\n */\nexport const reconstructFromSingleChunk = (\n chunk: JsonChunk\n): JSONObject | JSONArray => {\n const root: any = chunk.rootType === 'array' ? [] : {};\n\n // Apply all 'set' patches from this chunk\n for (const entry of chunk.entries) {\n if (entry.op === 'set') {\n setAtPath(root, entry.path, entry.value);\n }\n }\n\n // Reconcile split-node sentinels for strings/arrays\n // When reconstructing from a single chunk, we may have incomplete split nodes\n const reconcileSplitNodes = (node: any): any => {\n if (node === null || typeof node !== 'object') return node;\n\n if (Array.isArray(node)) {\n for (let i = 0; i < node.length; i++) {\n node[i] = reconcileSplitNodes(node[i]);\n }\n return node;\n }\n\n // string split-node\n if ((node as any)['__splittedType'] === 'string') {\n const total = (node as any)['__total'];\n\n if (typeof total !== 'number' || total <= 0) {\n // Invalid split node, return as-is\n return node;\n }\n\n const parts: string[] = [];\n let hasAllParts = true;\n\n for (let i = 1; i <= total; i++) {\n const piece = (node as any)[String(i)];\n\n if (typeof piece !== 'string') {\n hasAllParts = false;\n break;\n }\n\n parts.push(piece);\n }\n\n // Only reconstruct if we have all parts, otherwise return the node as-is\n if (hasAllParts) {\n return parts.join('');\n }\n\n return node;\n }\n\n // array split-node (optional support)\n if ((node as any)['__splittedType'] === 'array') {\n const total = (node as any)['__total'];\n\n if (typeof total !== 'number' || total < 0) {\n // Invalid split node, return as-is\n return node;\n }\n\n const output: any[] = [];\n let hasAllParts = true;\n\n for (let i = 1; i <= total; i++) {\n const slice = (node as any)[String(i)];\n\n if (!Array.isArray(slice)) {\n hasAllParts = false;\n break;\n }\n\n for (let j = 0; j < slice.length; j++) {\n output.push(reconcileSplitNodes(slice[j]));\n }\n }\n\n // Only reconstruct if we have all parts\n if (hasAllParts) {\n return output;\n }\n\n return node;\n }\n\n // walk normal object\n for (const key of Object.keys(node)) {\n node[key] = reconcileSplitNodes(node[key]);\n }\n\n return node;\n };\n\n return reconcileSplitNodes(root);\n};\n\nexport const assembleJSON = (chunks: JsonChunk[]): JSONObject | JSONArray => {\n if (!chunks || chunks.length === 0) {\n throw new Error('No chunks provided.');\n }\n\n // Basic validation & sort\n const sorted = [...chunks].sort((a, b) => a.index - b.index);\n const firstChunk = sorted[0];\n\n if (!firstChunk) {\n throw new Error('No chunks provided.');\n }\n\n const { checksum, rootType } = firstChunk;\n const schemaVersion = 1;\n\n for (let i = 0; i < sorted.length; i++) {\n const chunk = sorted[i];\n\n if (!chunk) continue;\n\n if (chunk.schemaVersion !== schemaVersion) {\n console.error('Unsupported schemaVersion.', {\n cause: chunk,\n schemaVersion,\n });\n throw new Error('Unsupported schemaVersion.');\n }\n\n if (chunk.rootType !== rootType) {\n console.error('Chunks rootType mismatch.', {\n cause: chunk,\n rootType,\n });\n throw new Error('Chunks rootType mismatch.');\n }\n\n if (chunk.checksum !== checksum) {\n console.error('Chunks checksum mismatch (different source objects?).', {\n cause: chunk,\n checksum,\n });\n throw new Error('Chunks checksum mismatch (different source objects?).');\n }\n\n if (chunk.index !== i) {\n console.error('Chunk indices are not contiguous or sorted.', {\n cause: chunk,\n index: chunk.index,\n i,\n });\n throw new Error('Chunk indices are not contiguous or sorted.');\n }\n\n // Defer total check until after reconstruction to prefer more specific errors\n }\n\n const root: any = rootType === 'array' ? [] : {};\n\n // Collect string parts by path\n const stringParts = new Map<\n string,\n { path: Path; total: number; received: StrAppendPatch[] }\n >();\n\n const applySet = (patch: SetPatch) =>\n setAtPath(root, patch.path, patch.value);\n\n for (const chunk of sorted) {\n for (const entry of chunk.entries) {\n if (entry.op === 'set') {\n applySet(entry);\n } else {\n const key = pathKey(entry.path);\n const record = stringParts.get(key) ?? {\n path: entry.path,\n total: entry.total,\n received: [],\n };\n\n if (record.total !== entry.total) {\n throw new Error('Inconsistent string part totals for a path.');\n }\n\n record.received.push(entry);\n stringParts.set(key, record);\n }\n }\n }\n\n // Stitch strings\n for (const { path, total, received } of stringParts.values()) {\n if (received.length !== total) {\n throw new Error('Missing string parts for a path; incomplete chunk set.');\n }\n\n received.sort((a, b) => a.index - b.index);\n const fullString = received.map((part) => part.value).join('');\n setAtPath(root, path, fullString);\n }\n\n // Reconcile split-node sentinels for strings/arrays after all patches applied\n const reconcileSplitNodes = (node: any): any => {\n if (node === null || typeof node !== 'object') return node;\n\n if (Array.isArray(node)) {\n for (let i = 0; i < node.length; i++) {\n node[i] = reconcileSplitNodes(node[i]);\n }\n return node;\n }\n\n // string split-node\n if ((node as any)['__splittedType'] === 'string') {\n const total = (node as any)['__total'];\n\n if (typeof total !== 'number' || total <= 0) {\n throw new Error('Invalid split-node total for a path.');\n }\n\n const parts: string[] = [];\n\n for (let i = 1; i <= total; i++) {\n const piece = (node as any)[String(i)];\n\n if (typeof piece !== 'string') {\n throw new Error(\n 'Missing string parts for a path; incomplete chunk set.'\n );\n }\n\n parts.push(piece);\n }\n\n return parts.join('');\n }\n\n // array split-node (optional support)\n if ((node as any)['__splittedType'] === 'array') {\n const total = (node as any)['__total'];\n\n if (typeof total !== 'number' || total < 0) {\n throw new Error('Invalid split-node total for a path.');\n }\n\n const output: any[] = [];\n\n for (let i = 1; i <= total; i++) {\n const slice = (node as any)[String(i)];\n\n if (!Array.isArray(slice)) {\n throw new Error(\n 'Missing string parts for a path; incomplete chunk set.'\n );\n }\n\n for (let j = 0; j < slice.length; j++) {\n output.push(reconcileSplitNodes(slice[j]));\n }\n }\n\n return output;\n }\n\n // walk normal object\n for (const key of Object.keys(node)) {\n node[key] = reconcileSplitNodes(node[key]);\n }\n\n return node;\n };\n\n const reconciled = reconcileSplitNodes(root);\n\n // Now validate totals match provided count\n for (let i = 0; i < sorted.length; i++) {\n const chunk = sorted[i];\n\n if (!chunk) continue;\n\n if (chunk.total !== sorted.length) {\n throw new Error(\n `Chunk total does not match provided count. Expected ${sorted.length}, but chunk ${i} has total=${chunk.total}`\n );\n }\n }\n\n return reconciled;\n};\n\n/* -------------------------------------------\n * Example usage\n * -------------------------------------------\nconst big: JSONObject = {\n title: \"Document\",\n content: \"…a very very long text…\",\n items: Array.from({ length: 2000 }, (_, i) => ({ id: i, label: `Item ${i}` }))\n};\n\n// Split to ~16k-char chunks\nconst chunks = chunkJSON(big, 16_000);\n\n// Send each `chunks[i]` as JSON to your backend.\n// Later, reassemble:\nconst restored = assembleJSON(chunks);\nconsole.log(JSON.stringify(restored) === JSON.stringify(big)); // true\n*/\n"],"mappings":"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"}
|