@intlayer/chokidar 9.0.0-canary.7 → 9.0.0-canary.8

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.
Files changed (54) hide show
  1. package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs.map +1 -1
  2. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +3 -3
  3. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
  4. package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs.map +1 -1
  5. package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs +6 -21
  6. package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs.map +1 -1
  7. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs +33 -20
  8. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs.map +1 -1
  9. package/dist/cjs/init/index.cjs +9 -5
  10. package/dist/cjs/init/index.cjs.map +1 -1
  11. package/dist/cjs/init/utils/astImports.cjs +56 -0
  12. package/dist/cjs/init/utils/astImports.cjs.map +1 -0
  13. package/dist/cjs/init/utils/configManipulation.cjs +28 -14
  14. package/dist/cjs/init/utils/configManipulation.cjs.map +1 -1
  15. package/dist/cjs/init/utils/fileSystem.cjs +50 -0
  16. package/dist/cjs/init/utils/fileSystem.cjs.map +1 -1
  17. package/dist/cjs/init/utils/index.cjs +5 -0
  18. package/dist/cjs/init/utils/packageManager.cjs +12 -7
  19. package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
  20. package/dist/esm/buildIntlayerDictionary/buildIntlayerDictionary.mjs.map +1 -1
  21. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +3 -3
  22. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
  23. package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs.map +1 -1
  24. package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs +1 -16
  25. package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs.map +1 -1
  26. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs +29 -16
  27. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs.map +1 -1
  28. package/dist/esm/init/index.mjs +10 -6
  29. package/dist/esm/init/index.mjs.map +1 -1
  30. package/dist/esm/init/utils/astImports.mjs +51 -0
  31. package/dist/esm/init/utils/astImports.mjs.map +1 -0
  32. package/dist/esm/init/utils/configManipulation.mjs +28 -14
  33. package/dist/esm/init/utils/configManipulation.mjs.map +1 -1
  34. package/dist/esm/init/utils/fileSystem.mjs +50 -1
  35. package/dist/esm/init/utils/fileSystem.mjs.map +1 -1
  36. package/dist/esm/init/utils/index.mjs +3 -2
  37. package/dist/esm/init/utils/packageManager.mjs +12 -7
  38. package/dist/esm/init/utils/packageManager.mjs.map +1 -1
  39. package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts +3 -3
  40. package/dist/types/buildIntlayerDictionary/writeMergedDictionary.d.ts +1 -1
  41. package/dist/types/formatDictionary.d.ts +2 -4
  42. package/dist/types/formatDictionary.d.ts.map +1 -1
  43. package/dist/types/init/frameworkSetup/nextAppRouter/transforms.d.ts.map +1 -1
  44. package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts.map +1 -1
  45. package/dist/types/init/utils/astImports.d.ts +29 -0
  46. package/dist/types/init/utils/astImports.d.ts.map +1 -0
  47. package/dist/types/init/utils/configManipulation.d.ts +3 -3
  48. package/dist/types/init/utils/configManipulation.d.ts.map +1 -1
  49. package/dist/types/init/utils/fileSystem.d.ts +29 -1
  50. package/dist/types/init/utils/fileSystem.d.ts.map +1 -1
  51. package/dist/types/init/utils/index.d.ts +4 -3
  52. package/dist/types/init/utils/packageManager.d.ts +23 -3
  53. package/dist/types/init/utils/packageManager.d.ts.map +1 -1
  54. package/package.json +8 -8
@@ -1 +1 @@
1
- {"version":3,"file":"configManipulation.cjs","names":[],"sources":["../../../../src/init/utils/configManipulation.ts"],"sourcesContent":["import type { RoutingConfig } from '@intlayer/types/config';\nimport * as recast from 'recast';\nimport type {\n CompatSyncConfig,\n CompatVitePluginConfig,\n} from './packageManager';\n\nconst { builders: b, namedTypes: n } = recast.types;\n\n/** The locale routing strategies supported by the Intlayer configuration. */\nexport type RoutingMode = RoutingConfig['mode'];\n\n/** Narrows an arbitrary recast node to an object expression. */\nconst isObjectExpression = (node: any): boolean =>\n Boolean(node) &&\n (node.type === 'ObjectExpression' || n.ObjectExpression.check(node));\n\n/**\n * Finds a property by key name on an object expression, creating it with the\n * provided default value node when it is absent. Returns the property node so\n * callers can read or mutate its value while preserving any attached comments.\n */\nconst ensureObjectProperty = (\n objExpr: any,\n key: string,\n defaultValueNode: any\n): any => {\n let property = (objExpr.properties as any[]).find((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === key;\n });\n\n if (!property) {\n property = b.property('init', b.identifier(key), defaultValueNode);\n (objExpr.properties as any[]).push(property);\n }\n\n return property;\n};\n\n/**\n * Sets (or replaces) a property's value on an object expression. When the\n * property already exists only its value node is swapped, which keeps the\n * leading documentation comment in place.\n */\nconst setObjectPropertyValue = (\n objExpr: any,\n key: string,\n valueNode: any\n): void => {\n const property = (objExpr.properties as any[]).find((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === key;\n });\n\n if (property) {\n property.value = valueNode;\n return;\n }\n\n (objExpr.properties as any[]).push(\n b.property('init', b.identifier(key), valueNode)\n );\n};\n\n/**\n * Adds a `process.env.<envVar>` reference property to an object expression when\n * the property is not already present. Existing values are left untouched.\n */\nconst addEnvReferenceProperty = (\n objExpr: any,\n key: string,\n envVar: string\n): void => {\n const hasProperty = (objExpr.properties as any[]).some((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === key;\n });\n\n if (hasProperty) return;\n\n (objExpr.properties as any[]).push(\n b.property(\n 'init',\n b.identifier(key),\n b.memberExpression(\n b.memberExpression(b.identifier('process'), b.identifier('env')),\n b.identifier(envVar)\n )\n )\n );\n};\n\n/**\n * Sets `routing.mode` in an Intlayer configuration file to the requested\n * strategy. Idempotent: re-running with the same mode produces identical\n * output. Supports `.ts`, `.mjs`, `.js` and `.cjs` configs; JSON configs are\n * handled with a scoped string replacement since they cannot be parsed by the\n * TypeScript recast parser.\n */\nexport const setIntlayerConfigRoutingMode = (\n content: string,\n extension: string,\n mode: RoutingMode\n): string => {\n if (extension === 'json') {\n return content.replace(\n /(\"mode\"\\s*:\\s*)\"(?:prefix-no-default|prefix-all|no-prefix|search-params)\"/,\n `$1\"${mode}\"`\n );\n }\n\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n genericRecastVisit(ast, (objExpr) => {\n if (!isObjectExpression(objExpr)) return;\n\n const routingProperty = ensureObjectProperty(\n objExpr,\n 'routing',\n b.objectExpression([])\n );\n\n if (!isObjectExpression(routingProperty.value)) return;\n\n setObjectPropertyValue(\n routingProperty.value,\n 'mode',\n b.stringLiteral(mode)\n );\n });\n\n return recast.print(ast).code;\n};\n\n/**\n * Enables the Intlayer visual editor in a configuration file: sets\n * `editor.enabled` to `true` and wires `clientId` / `clientSecret` to the\n * `INTLAYER_CLIENT_ID` / `INTLAYER_CLIENT_SECRET` environment variables.\n * Idempotent and non-destructive — existing `clientId` / `clientSecret` values\n * are preserved. Only `.ts`, `.mjs`, `.js` and `.cjs` configs are supported\n * (JSON cannot reference `process.env`).\n */\nexport const enableIntlayerEditorConfig = (content: string): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n genericRecastVisit(ast, (objExpr) => {\n if (!isObjectExpression(objExpr)) return;\n\n const editorProperty = ensureObjectProperty(\n objExpr,\n 'editor',\n b.objectExpression([])\n );\n\n if (!isObjectExpression(editorProperty.value)) return;\n\n const editorObject = editorProperty.value;\n\n setObjectPropertyValue(editorObject, 'enabled', b.booleanLiteral(true));\n addEnvReferenceProperty(editorObject, 'clientId', 'INTLAYER_CLIENT_ID');\n addEnvReferenceProperty(\n editorObject,\n 'clientSecret',\n 'INTLAYER_CLIENT_SECRET'\n );\n });\n\n return recast.print(ast).code;\n};\n\nconst injectImport = (\n ast: any,\n isCJS: boolean,\n importName: string,\n source: string\n) => {\n const body = ast.program.body;\n const hasImport = body.some((stmt: any) => {\n if (isCJS) {\n return (\n n.VariableDeclaration.check(stmt) &&\n stmt.declarations.some(\n (decl: any) =>\n n.VariableDeclarator.check(decl) &&\n n.CallExpression.check(decl.init) &&\n n.Identifier.check(decl.init.callee) &&\n decl.init.callee.name === 'require' &&\n n.StringLiteral.check(decl.init.arguments[0]) &&\n decl.init.arguments[0].value === source\n )\n );\n }\n return (\n n.ImportDeclaration.check(stmt) &&\n (stmt.source.value === source ||\n stmt.specifiers?.some(\n (spec: any) =>\n (n.ImportSpecifier.check(spec) &&\n spec.imported.name === importName) ||\n (n.ImportDefaultSpecifier.check(spec) &&\n spec.local?.name === importName)\n ))\n );\n });\n\n if (hasImport) return;\n\n const declaration = isCJS\n ? b.variableDeclaration('const', [\n b.variableDeclarator(\n b.identifier(`{ ${importName} }`),\n b.callExpression(b.identifier('require'), [b.stringLiteral(source)])\n ),\n ])\n : b.importDeclaration(\n [b.importSpecifier(b.identifier(importName))],\n b.stringLiteral(source)\n );\n\n ast.program.body.unshift(declaration);\n};\n\nconst updatePluginArray = (\n objExpr: any,\n propertyName: string,\n pluginName: string\n) => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' && !n.ObjectExpression.check(objExpr))\n )\n return;\n\n let prop = objExpr.properties.find((p: any) => {\n if (!p?.key) return false;\n const keyName = p.key.name || p.key.value;\n return keyName === propertyName;\n }) as any;\n\n if (!prop) {\n prop = b.property(\n 'init',\n b.identifier(propertyName),\n b.arrayExpression([])\n );\n objExpr.properties.push(prop);\n }\n\n const arrayValue = prop.value;\n\n if (\n arrayValue &&\n (arrayValue.type === 'ArrayExpression' ||\n n.ArrayExpression.check(arrayValue))\n ) {\n const hasPlugin = arrayValue.elements.some((el: any) => {\n const callee = el?.callee;\n if (!callee) return false;\n const name = callee.name || callee.id?.name;\n return name === pluginName || name === 'il';\n });\n\n if (!hasPlugin) {\n arrayValue.elements.push(b.callExpression(b.identifier(pluginName), []));\n }\n }\n};\n\nconst genericRecastVisit = (\n ast: any,\n updateConfigObject: (obj: any) => void,\n callNames: string[] = ['defineConfig']\n) => {\n /**\n * Resolves an identifier reference (e.g. `export default config` /\n * `module.exports = config`) back to the object expression it was declared\n * with, then runs the updater on it.\n */\n const resolveIdentifierConfig = (name: string) => {\n ast.program.body.forEach((stmt: any) => {\n if (n.VariableDeclaration.check(stmt)) {\n stmt.declarations.forEach((vdecl: any) => {\n if (\n n.VariableDeclarator.check(vdecl) &&\n n.Identifier.check(vdecl.id) &&\n vdecl.id.name === name &&\n n.ObjectExpression.check(vdecl.init)\n ) {\n updateConfigObject(vdecl.init);\n }\n });\n }\n });\n };\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const decl = path.node.declaration;\n\n if (n.ObjectExpression.check(decl)) {\n updateConfigObject(decl);\n } else if (\n n.CallExpression.check(decl) &&\n n.Identifier.check(decl.callee) &&\n callNames.includes(decl.callee.name)\n ) {\n if (n.ObjectExpression.check(decl.arguments[0])) {\n updateConfigObject(decl.arguments[0]);\n }\n } else if (n.Identifier.check(decl)) {\n resolveIdentifierConfig(decl.name);\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports'\n ) {\n if (n.ObjectExpression.check(right)) {\n updateConfigObject(right);\n } else if (\n n.CallExpression.check(right) &&\n n.Identifier.check(right.callee) &&\n callNames.includes(right.callee.name)\n ) {\n if (n.ObjectExpression.check(right.arguments[0])) {\n updateConfigObject(right.arguments[0]);\n }\n } else if (n.Identifier.check(right)) {\n resolveIdentifierConfig(right.name);\n }\n }\n return false;\n },\n });\n};\n\nexport const updateViteConfig = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n injectImport(ast, isCJSFile, 'intlayer', 'vite-intlayer');\n\n genericRecastVisit(ast, (obj) =>\n updatePluginArray(obj, 'plugins', 'intlayer')\n );\n\n return recast.print(ast).code;\n};\n\nexport const updateAstroConfig = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n injectImport(ast, isCJSFile, 'intlayer', 'astro-intlayer');\n\n genericRecastVisit(ast, (obj) =>\n updatePluginArray(obj, 'integrations', 'intlayer')\n );\n\n return recast.print(ast).code;\n};\n\nexport const updateNextConfig = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile = extension === 'cjs' || content.includes('module.exports');\n\n injectImport(ast, isCJSFile, 'withIntlayer', 'next-intlayer/server');\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const declaration = path.node.declaration;\n if (\n n.Expression.check(declaration) &&\n !(\n n.CallExpression.check(declaration) &&\n n.Identifier.check(declaration.callee) &&\n declaration.callee.name === 'withIntlayer'\n )\n ) {\n path\n .get('declaration')\n .replace(\n b.callExpression(b.identifier('withIntlayer'), [declaration as any])\n );\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports' &&\n !(\n n.CallExpression.check(right) &&\n n.Identifier.check(right.callee) &&\n right.callee.name === 'withIntlayer'\n )\n ) {\n path\n .get('right')\n .replace(b.callExpression(b.identifier('withIntlayer'), [right]));\n }\n return false;\n },\n });\n\n return recast.print(ast).code;\n};\n\n/**\n * Returns true when the expression looks like an Immediately Invoked Function\n * Expression (e.g. `(async () => { ... })()`). Such custom async exports cannot\n * be safely wrapped with the synchronous Metro helper, so they are skipped.\n */\nconst isImmediatelyInvokedFunction = (node: any): boolean =>\n n.CallExpression.check(node) &&\n (n.ArrowFunctionExpression.check(node.callee) ||\n n.FunctionExpression.check(node.callee));\n\n/**\n * Wraps a React Native Metro config's exported value with\n * `configMetroIntlayerSync` from `react-native-intlayer/metro`, injecting the\n * import. The synchronous helper is used because it wraps a plain config object\n * and needs no IIFE, making it safe to inject into existing configs without\n * restructuring them.\n *\n * Non-destructive: returns the content unchanged when the export is already\n * wrapped, when the Intlayer Metro plugin is already present, or when the export\n * is a custom async IIFE that cannot be wrapped synchronously. Callers should\n * compare the result with the input to detect the skipped case.\n */\nexport const updateMetroConfig = (\n content: string,\n extension: string\n): string => {\n if (content.includes('react-native-intlayer')) {\n return content;\n }\n\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n const wrapperName = 'configMetroIntlayerSync';\n\n const isWrappable = (node: any): boolean =>\n n.Expression.check(node) &&\n !isImmediatelyInvokedFunction(node) &&\n !(\n n.CallExpression.check(node) &&\n n.Identifier.check(node.callee) &&\n node.callee.name === wrapperName\n );\n\n let wrapped = false;\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const declaration = path.node.declaration;\n if (isWrappable(declaration)) {\n path\n .get('declaration')\n .replace(\n b.callExpression(b.identifier(wrapperName), [declaration as any])\n );\n wrapped = true;\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports' &&\n isWrappable(right)\n ) {\n path\n .get('right')\n .replace(b.callExpression(b.identifier(wrapperName), [right]));\n wrapped = true;\n }\n return false;\n },\n });\n\n // Only inject the import when an export was actually wrapped, so an\n // un-wrappable config is left completely untouched.\n if (!wrapped) {\n return content;\n }\n\n injectImport(ast, isCJSFile, wrapperName, 'react-native-intlayer/metro');\n\n return recast.print(ast).code;\n};\n\n/**\n * Builds the contents of a fresh `metro.config.js` wired with the Intlayer\n * Metro plugin. Uses the async `configMetroIntlayer` helper (which can build\n * dictionaries on server start) and picks the default-config source based on\n * whether the project is an Expo app.\n */\nexport const getMetroConfigTemplate = (isExpo: boolean): string => {\n const defaultConfigSource = isExpo\n ? 'expo/metro-config'\n : '@react-native/metro-config';\n\n return `const { getDefaultConfig } = require(\"${defaultConfigSource}\");\nconst { configMetroIntlayer } = require(\"react-native-intlayer/metro\");\n\nmodule.exports = (async () => {\n const defaultConfig = getDefaultConfig(__dirname);\n\n return await configMetroIntlayer(defaultConfig);\n})();\n`;\n};\n\nexport const updateNuxtConfig = (content: string): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const updateConfigObject = (objExpr: any) => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' &&\n !n.ObjectExpression.check(objExpr))\n )\n return;\n\n let modulesProp = objExpr.properties.find((p: any) => {\n if (!p?.key) return false;\n const keyName = p.key.name || p.key.value;\n return keyName === 'modules';\n }) as any;\n\n if (!modulesProp) {\n modulesProp = b.property(\n 'init',\n b.identifier('modules'),\n b.arrayExpression([])\n );\n objExpr.properties.push(modulesProp);\n }\n\n const modulesValue = modulesProp.value;\n\n if (\n modulesValue &&\n (modulesValue.type === 'ArrayExpression' ||\n n.ArrayExpression.check(modulesValue))\n ) {\n const hasModule = modulesValue.elements.some((el: any) => {\n if (\n n.StringLiteral.check(el) ||\n el.type === 'StringLiteral' ||\n el.type === 'Literal'\n ) {\n return (el.value || el.extra?.rawValue) === 'nuxt-intlayer';\n }\n return false;\n });\n\n if (!hasModule) {\n modulesValue.elements.push(b.stringLiteral('nuxt-intlayer'));\n }\n }\n };\n\n genericRecastVisit(ast, updateConfigObject, ['defineNuxtConfig']);\n\n return recast.print(ast).code;\n};\n\n/**\n * Updates a Vite config for vue-i18n compat: injects `vueI18nVitePlugin` from\n * `@intlayer/vue-i18n/plugin` into the plugins array.\n */\nexport const updateViteConfigForVueI18n = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n injectImport(\n ast,\n isCJSFile,\n 'vueI18nVitePlugin',\n '@intlayer/vue-i18n/plugin'\n );\n\n genericRecastVisit(ast, (obj) =>\n updatePluginArray(obj, 'plugins', 'vueI18nVitePlugin')\n );\n\n return recast.print(ast).code;\n};\n\n/**\n * Generic vite config updater for any compat plugin that uses alias injection.\n * Injects the named import from `pluginPackageSource` and appends the plugin\n * call to the `plugins` array.\n */\nexport const updateViteConfigForCompatPlugin = (\n content: string,\n extension: string,\n pluginConfig: CompatVitePluginConfig\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n injectImport(\n ast,\n isCJSFile,\n pluginConfig.pluginFunctionName,\n pluginConfig.pluginPackageSource\n );\n\n genericRecastVisit(ast, (obj) =>\n updatePluginArray(obj, 'plugins', pluginConfig.pluginFunctionName)\n );\n\n return recast.print(ast).code;\n};\n\n/**\n * Rewrites the module source of an existing named import in a vite config,\n * keeping the imported binding and its call site untouched. Used when a compat\n * plugin is a drop-in replacement for an i18n library's own vite plugin — e.g.\n * lingui: `import { lingui } from \"@lingui/vite-plugin\"` becomes\n * `import { lingui } from \"@intlayer/lingui/plugin\"`, leaving `lingui()` in the\n * `plugins` array as-is. Returns the content unchanged when no matching import\n * is found.\n */\nexport const replaceViteConfigPluginImportSource = (\n content: string,\n importName: string,\n fromPackageSource: string,\n toPackageSource: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n let changed = false;\n\n recast.visit(ast, {\n visitImportDeclaration(path) {\n const { source, specifiers } = path.node;\n const importsBinding = (specifiers ?? []).some(\n (specifier) =>\n n.ImportSpecifier.check(specifier) &&\n specifier.imported.name === importName\n );\n\n if (\n n.StringLiteral.check(source) &&\n source.value === fromPackageSource &&\n importsBinding\n ) {\n source.value = toPackageSource;\n changed = true;\n }\n\n return false;\n },\n });\n\n if (!changed) return content;\n return recast.print(ast).code;\n};\n\n/**\n * Generic Next.js config wrapper for compat plugins. Injects the import and\n * wraps the default export / `module.exports` with a HOC call.\n */\nconst wrapNextConfigWithHoc = (\n content: string,\n extension: string,\n hocFunctionName: string,\n pluginPackageSource: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile = extension === 'cjs' || content.includes('module.exports');\n\n injectImport(ast, isCJSFile, hocFunctionName, pluginPackageSource);\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const declaration = path.node.declaration;\n if (\n n.Expression.check(declaration) &&\n !(\n n.CallExpression.check(declaration) &&\n n.Identifier.check(declaration.callee) &&\n declaration.callee.name === hocFunctionName\n )\n ) {\n path\n .get('declaration')\n .replace(\n b.callExpression(b.identifier(hocFunctionName), [\n declaration as any,\n ])\n );\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports' &&\n !(\n n.CallExpression.check(right) &&\n n.Identifier.check(right.callee) &&\n right.callee.name === hocFunctionName\n )\n ) {\n path\n .get('right')\n .replace(b.callExpression(b.identifier(hocFunctionName), [right]));\n }\n return false;\n },\n });\n\n return recast.print(ast).code;\n};\n\n/**\n * Updates a Next.js config for next-translate compat: wraps the default export\n * with `withNextTranslate` from `@intlayer/next-translate/plugin`.\n */\nexport const updateNextConfigForNextTranslate = (\n content: string,\n extension: string\n): string =>\n wrapNextConfigWithHoc(\n content,\n extension,\n 'withNextTranslate',\n '@intlayer/next-translate/plugin'\n );\n\n/**\n * Updates a Nuxt config for nuxtjs-i18n compat: adds `@intlayer/nuxtjs-i18n`\n * to the `modules` array.\n */\nexport const updateNuxtConfigForNuxtjsI18n = (content: string): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const updateConfigObject = (objExpr: any) => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' &&\n !n.ObjectExpression.check(objExpr))\n )\n return;\n\n let modulesProp = (objExpr.properties as any[]).find((p: any) => {\n if (!p?.key) return false;\n const keyName = p.key.name || p.key.value;\n return keyName === 'modules';\n }) as any;\n\n if (!modulesProp) {\n modulesProp = b.property(\n 'init',\n b.identifier('modules'),\n b.arrayExpression([])\n );\n (objExpr.properties as any[]).push(modulesProp);\n }\n\n const modulesValue = modulesProp.value;\n\n if (\n modulesValue &&\n (modulesValue.type === 'ArrayExpression' ||\n n.ArrayExpression.check(modulesValue))\n ) {\n const hasModule = (modulesValue.elements as any[]).some((el: any) => {\n if (\n n.StringLiteral.check(el) ||\n el.type === 'StringLiteral' ||\n el.type === 'Literal'\n ) {\n return (el.value ?? el.extra?.rawValue) === '@intlayer/nuxtjs-i18n';\n }\n return false;\n });\n\n if (!hasModule) {\n (modulesValue.elements as any[]).push(\n b.stringLiteral('@intlayer/nuxtjs-i18n')\n );\n }\n }\n };\n\n genericRecastVisit(ast, updateConfigObject, ['defineNuxtConfig']);\n\n return recast.print(ast).code;\n};\n\n/**\n * Updates a Next.js config for next-i18next compat: wraps the default export\n * with `withI18next` from `@intlayer/next-i18next/plugin`.\n */\nexport const updateNextConfigForNextI18next = (\n content: string,\n extension: string\n): string =>\n wrapNextConfigWithHoc(\n content,\n extension,\n 'withI18next',\n '@intlayer/next-i18next/plugin'\n );\n\n/**\n * Updates a Next.js config for next-intl compat: replaces any existing\n * `next-intl/plugin` import source with `@intlayer/next-intl/plugin`, or\n * injects `createNextIntlPlugin` with a factory-call wrapper when no such\n * import is present.\n */\nexport const updateNextConfigForNextIntl = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile = extension === 'cjs' || content.includes('module.exports');\n let replacedExistingSource = false;\n\n // Replace 'next-intl/plugin' import source with the compat package.\n recast.visit(ast, {\n visitImportDeclaration(path) {\n if (path.node.source.value === 'next-intl/plugin') {\n path.node.source = b.stringLiteral('@intlayer/next-intl/plugin');\n replacedExistingSource = true;\n }\n return false;\n },\n });\n\n if (replacedExistingSource) {\n return recast.print(ast).code;\n }\n\n // No existing next-intl/plugin import: check whether createNextIntlPlugin is\n // already present from any source before injecting the full factory pattern.\n const hasCreatePlugin = (ast.program.body as any[]).some((stmt: any) => {\n if (!n.ImportDeclaration.check(stmt)) return false;\n return (stmt.specifiers ?? []).some(\n (spec: any) =>\n (n.ImportSpecifier.check(spec) &&\n spec.imported.name === 'createNextIntlPlugin') ||\n (n.ImportDefaultSpecifier.check(spec) &&\n spec.local?.name === 'createNextIntlPlugin')\n );\n });\n\n if (hasCreatePlugin) {\n return recast.print(ast).code;\n }\n\n // Inject the import.\n injectImport(\n ast,\n isCJSFile,\n 'createNextIntlPlugin',\n '@intlayer/next-intl/plugin'\n );\n\n // Insert a factory-call variable declaration after the last import.\n const lastImportIndex = (ast.program.body as any[]).reduce(\n (lastIndex: number, stmt: any, index: number) => {\n if (n.ImportDeclaration.check(stmt)) return index;\n return lastIndex;\n },\n -1\n );\n\n const factoryCallDeclaration = b.variableDeclaration('const', [\n b.variableDeclarator(\n b.identifier('_withNextIntlayer'),\n b.callExpression(b.identifier('createNextIntlPlugin'), [])\n ),\n ]);\n\n (ast.program.body as any[]).splice(\n lastImportIndex + 1,\n 0,\n factoryCallDeclaration\n );\n\n // Wrap the default export with _withNextIntlayer(...).\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const declaration = path.node.declaration;\n if (\n n.Expression.check(declaration) &&\n !(\n n.CallExpression.check(declaration) &&\n n.Identifier.check(declaration.callee) &&\n declaration.callee.name === '_withNextIntlayer'\n )\n ) {\n path\n .get('declaration')\n .replace(\n b.callExpression(b.identifier('_withNextIntlayer'), [\n declaration as any,\n ])\n );\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports' &&\n !(\n n.CallExpression.check(right) &&\n n.Identifier.check(right.callee) &&\n right.callee.name === '_withNextIntlayer'\n )\n ) {\n path\n .get('right')\n .replace(\n b.callExpression(b.identifier('_withNextIntlayer'), [right])\n );\n }\n return false;\n },\n });\n\n return recast.print(ast).code;\n};\n\n/**\n * Parses a syncJSON({ ... }) call expression from a source snippet so it can\n * be injected into a config AST without manually constructing template-literal\n * nodes via builders.\n *\n * The destructuring parameters adapt to whether the source template uses the\n * `key` placeholder (nested pattern) or only `locale` (flat pattern).\n */\nconst buildSyncJSONCallNode = (syncConfig: CompatSyncConfig): any => {\n const usesKey = syncConfig.sourceTemplate.includes('${key}');\n const paramDestructuring =\n syncConfig.format === 'icu'\n ? usesKey\n ? '{ key, locale }'\n : '{ locale }'\n : usesKey\n ? '{ locale, key }'\n : '{ locale }';\n\n // `splitKeys` is written explicitly only when forced on (next-intl / use-intl\n // single-file namespace model). When omitted, syncJSON auto-detects it from\n // the presence of a `${key}` segment in the source.\n const splitKeysProperty = syncConfig.splitKeys ? ', splitKeys: true' : '';\n\n // The sourceTemplate contains ${locale} / ${key} as literal characters;\n // they become proper template expressions once the snippet is parsed by recast.\n const snippet = `syncJSON({ format: '${syncConfig.format}', source: (${paramDestructuring}) => \\`${syncConfig.sourceTemplate}\\`${splitKeysProperty} })`;\n const snippetAst = recast.parse(snippet, {\n parser: require('recast/parsers/typescript'),\n });\n return (snippetAst.program.body[0] as any).expression;\n};\n\n/**\n * Injects or ensures `dictionary: { format: '<value>' }` exists in an object\n * expression. Leaves any pre-existing `dictionary` properties untouched —\n * only the `format` sub-property is added when absent.\n */\nconst injectDictionaryFormat = (objExpr: any, format: string): void => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' && !n.ObjectExpression.check(objExpr))\n )\n return;\n\n let dictionaryProp = (objExpr.properties as any[]).find((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === 'dictionary';\n });\n\n if (!dictionaryProp) {\n dictionaryProp = b.property(\n 'init',\n b.identifier('dictionary'),\n b.objectExpression([])\n );\n (objExpr.properties as any[]).push(dictionaryProp);\n }\n\n const dictionaryObj = dictionaryProp.value;\n if (\n !dictionaryObj ||\n (dictionaryObj.type !== 'ObjectExpression' &&\n !n.ObjectExpression.check(dictionaryObj))\n )\n return;\n\n const hasFormat = (dictionaryObj.properties as any[]).some((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === 'format';\n });\n\n if (!hasFormat) {\n (dictionaryObj.properties as any[]).push(\n b.property('init', b.identifier('format'), b.stringLiteral(format))\n );\n }\n};\n\n/**\n * Injects the syncJSON import and a configured syncJSON(...) call into the\n * plugins array of an intlayer config file. Idempotent: skips when\n * @intlayer/sync-json-plugin is already imported.\n */\nexport const updateIntlayerConfigWithSyncPlugin = (\n content: string,\n extension: string,\n syncConfig: CompatSyncConfig\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile = extension === 'cjs' || content.includes('module.exports');\n\n injectImport(ast, isCJSFile, 'syncJSON', '@intlayer/sync-json-plugin');\n\n const callNode = buildSyncJSONCallNode(syncConfig);\n\n genericRecastVisit(ast, (objExpr) => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' &&\n !n.ObjectExpression.check(objExpr))\n )\n return;\n\n // Inject dictionary.format alongside the plugin so intlayer knows how to\n // interpret dictionary content at runtime.\n injectDictionaryFormat(objExpr, syncConfig.format);\n\n let pluginsProp = (objExpr.properties as any[]).find((prop: any) => {\n if (!prop?.key) return false;\n const keyName = prop.key.name ?? prop.key.value;\n return keyName === 'plugins';\n });\n\n if (!pluginsProp) {\n pluginsProp = b.property(\n 'init',\n b.identifier('plugins'),\n b.arrayExpression([])\n );\n (objExpr.properties as any[]).push(pluginsProp);\n }\n\n const arrayValue = pluginsProp.value;\n\n if (\n arrayValue &&\n (arrayValue.type === 'ArrayExpression' ||\n n.ArrayExpression.check(arrayValue))\n ) {\n const hasSyncJSON = (arrayValue.elements as any[]).some(\n (element: any) => {\n const callee = element?.callee;\n if (!callee) return false;\n const name: string = callee.name ?? callee.id?.name;\n return name === 'syncJSON';\n }\n );\n\n if (!hasSyncJSON) {\n (arrayValue.elements as any[]).push(callNode);\n }\n }\n });\n\n return recast.print(ast).code;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"configManipulation.cjs","names":["isModuleScopeBinding"],"sources":["../../../../src/init/utils/configManipulation.ts"],"sourcesContent":["import type { RoutingConfig } from '@intlayer/types/config';\nimport * as recast from 'recast';\nimport { isModuleScopeBinding } from './astImports';\nimport type {\n CompatSyncConfig,\n CompatVitePluginConfig,\n} from './packageManager';\n\nconst { builders: b, namedTypes: n } = recast.types;\n\n/** The locale routing strategies supported by the Intlayer configuration. */\nexport type RoutingMode = RoutingConfig['mode'];\n\n/** Narrows an arbitrary recast node to an object expression. */\nconst isObjectExpression = (node: any): boolean =>\n Boolean(node) &&\n (node.type === 'ObjectExpression' || n.ObjectExpression.check(node));\n\n/**\n * Finds a property by key name on an object expression, creating it with the\n * provided default value node when it is absent. Returns the property node so\n * callers can read or mutate its value while preserving any attached comments.\n */\nconst ensureObjectProperty = (\n objExpr: any,\n key: string,\n defaultValueNode: any\n): any => {\n let property = (objExpr.properties as any[]).find((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === key;\n });\n\n if (!property) {\n property = b.property('init', b.identifier(key), defaultValueNode);\n (objExpr.properties as any[]).push(property);\n }\n\n return property;\n};\n\n/**\n * Sets (or replaces) a property's value on an object expression. When the\n * property already exists only its value node is swapped, which keeps the\n * leading documentation comment in place.\n */\nconst setObjectPropertyValue = (\n objExpr: any,\n key: string,\n valueNode: any\n): void => {\n const property = (objExpr.properties as any[]).find((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === key;\n });\n\n if (property) {\n property.value = valueNode;\n return;\n }\n\n (objExpr.properties as any[]).push(\n b.property('init', b.identifier(key), valueNode)\n );\n};\n\n/**\n * Adds a `process.env.<envVar>` reference property to an object expression when\n * the property is not already present. Existing values are left untouched.\n */\nconst addEnvReferenceProperty = (\n objExpr: any,\n key: string,\n envVar: string\n): void => {\n const hasProperty = (objExpr.properties as any[]).some((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === key;\n });\n\n if (hasProperty) return;\n\n (objExpr.properties as any[]).push(\n b.property(\n 'init',\n b.identifier(key),\n b.memberExpression(\n b.memberExpression(b.identifier('process'), b.identifier('env')),\n b.identifier(envVar)\n )\n )\n );\n};\n\n/**\n * Sets `routing.mode` in an Intlayer configuration file to the requested\n * strategy. Idempotent: re-running with the same mode produces identical\n * output. Supports `.ts`, `.mjs`, `.js` and `.cjs` configs; JSON configs are\n * handled with a scoped string replacement since they cannot be parsed by the\n * TypeScript recast parser.\n */\nexport const setIntlayerConfigRoutingMode = (\n content: string,\n extension: string,\n mode: RoutingMode\n): string => {\n if (extension === 'json') {\n return content.replace(\n /(\"mode\"\\s*:\\s*)\"(?:prefix-no-default|prefix-all|no-prefix|search-params)\"/,\n `$1\"${mode}\"`\n );\n }\n\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n genericRecastVisit(ast, (objExpr) => {\n if (!isObjectExpression(objExpr)) return;\n\n const routingProperty = ensureObjectProperty(\n objExpr,\n 'routing',\n b.objectExpression([])\n );\n\n if (!isObjectExpression(routingProperty.value)) return;\n\n setObjectPropertyValue(\n routingProperty.value,\n 'mode',\n b.stringLiteral(mode)\n );\n });\n\n return recast.print(ast).code;\n};\n\n/**\n * Enables the Intlayer visual editor in a configuration file: sets\n * `editor.enabled` to `true` and wires `clientId` / `clientSecret` to the\n * `INTLAYER_CLIENT_ID` / `INTLAYER_CLIENT_SECRET` environment variables.\n * Idempotent and non-destructive — existing `clientId` / `clientSecret` values\n * are preserved. Only `.ts`, `.mjs`, `.js` and `.cjs` configs are supported\n * (JSON cannot reference `process.env`).\n */\nexport const enableIntlayerEditorConfig = (content: string): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n genericRecastVisit(ast, (objExpr) => {\n if (!isObjectExpression(objExpr)) return;\n\n const editorProperty = ensureObjectProperty(\n objExpr,\n 'editor',\n b.objectExpression([])\n );\n\n if (!isObjectExpression(editorProperty.value)) return;\n\n const editorObject = editorProperty.value;\n\n setObjectPropertyValue(editorObject, 'enabled', b.booleanLiteral(true));\n addEnvReferenceProperty(editorObject, 'clientId', 'INTLAYER_CLIENT_ID');\n addEnvReferenceProperty(\n editorObject,\n 'clientSecret',\n 'INTLAYER_CLIENT_SECRET'\n );\n });\n\n return recast.print(ast).code;\n};\n\nconst injectImport = (\n ast: any,\n isCJS: boolean,\n importName: string,\n source: string\n) => {\n // Never redeclare an identifier already bound at module scope (e.g. imported\n // from another source or declared locally) — that would be a parse error.\n if (!isCJS && isModuleScopeBinding(ast, importName)) return;\n\n const body = ast.program.body;\n const hasImport = body.some((stmt: any) => {\n if (isCJS) {\n return (\n n.VariableDeclaration.check(stmt) &&\n stmt.declarations.some(\n (decl: any) =>\n n.VariableDeclarator.check(decl) &&\n n.CallExpression.check(decl.init) &&\n n.Identifier.check(decl.init.callee) &&\n decl.init.callee.name === 'require' &&\n n.StringLiteral.check(decl.init.arguments[0]) &&\n decl.init.arguments[0].value === source\n )\n );\n }\n return (\n n.ImportDeclaration.check(stmt) &&\n (stmt.source.value === source ||\n stmt.specifiers?.some(\n (spec: any) =>\n (n.ImportSpecifier.check(spec) &&\n spec.imported.name === importName) ||\n (n.ImportDefaultSpecifier.check(spec) &&\n spec.local?.name === importName)\n ))\n );\n });\n\n if (hasImport) return;\n\n const declaration = isCJS\n ? b.variableDeclaration('const', [\n b.variableDeclarator(\n b.identifier(`{ ${importName} }`),\n b.callExpression(b.identifier('require'), [b.stringLiteral(source)])\n ),\n ])\n : b.importDeclaration(\n [b.importSpecifier(b.identifier(importName))],\n b.stringLiteral(source)\n );\n\n ast.program.body.unshift(declaration);\n};\n\nconst updatePluginArray = (\n objExpr: any,\n propertyName: string,\n pluginName: string\n) => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' && !n.ObjectExpression.check(objExpr))\n )\n return;\n\n let prop = objExpr.properties.find((p: any) => {\n if (!p?.key) return false;\n const keyName = p.key.name || p.key.value;\n return keyName === propertyName;\n }) as any;\n\n if (!prop) {\n prop = b.property(\n 'init',\n b.identifier(propertyName),\n b.arrayExpression([])\n );\n objExpr.properties.push(prop);\n }\n\n const arrayValue = prop.value;\n\n if (\n arrayValue &&\n (arrayValue.type === 'ArrayExpression' ||\n n.ArrayExpression.check(arrayValue))\n ) {\n const hasPlugin = arrayValue.elements.some((el: any) => {\n const callee = el?.callee;\n if (!callee) return false;\n const name = callee.name || callee.id?.name;\n return name === pluginName || name === 'il';\n });\n\n if (!hasPlugin) {\n arrayValue.elements.push(b.callExpression(b.identifier(pluginName), []));\n }\n }\n};\n\nconst genericRecastVisit = (\n ast: any,\n updateConfigObject: (obj: any) => void,\n callNames: string[] = ['defineConfig']\n) => {\n /**\n * Resolves an identifier reference (e.g. `export default config` /\n * `module.exports = config`) back to the object expression it was declared\n * with, then runs the updater on it.\n */\n const resolveIdentifierConfig = (name: string) => {\n ast.program.body.forEach((stmt: any) => {\n if (n.VariableDeclaration.check(stmt)) {\n stmt.declarations.forEach((vdecl: any) => {\n if (\n n.VariableDeclarator.check(vdecl) &&\n n.Identifier.check(vdecl.id) &&\n vdecl.id.name === name &&\n n.ObjectExpression.check(vdecl.init)\n ) {\n updateConfigObject(vdecl.init);\n }\n });\n }\n });\n };\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const decl = path.node.declaration;\n\n if (n.ObjectExpression.check(decl)) {\n updateConfigObject(decl);\n } else if (\n n.CallExpression.check(decl) &&\n n.Identifier.check(decl.callee) &&\n callNames.includes(decl.callee.name)\n ) {\n if (n.ObjectExpression.check(decl.arguments[0])) {\n updateConfigObject(decl.arguments[0]);\n }\n } else if (n.Identifier.check(decl)) {\n resolveIdentifierConfig(decl.name);\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports'\n ) {\n if (n.ObjectExpression.check(right)) {\n updateConfigObject(right);\n } else if (\n n.CallExpression.check(right) &&\n n.Identifier.check(right.callee) &&\n callNames.includes(right.callee.name)\n ) {\n if (n.ObjectExpression.check(right.arguments[0])) {\n updateConfigObject(right.arguments[0]);\n }\n } else if (n.Identifier.check(right)) {\n resolveIdentifierConfig(right.name);\n }\n }\n return false;\n },\n });\n};\n\nexport const updateViteConfig = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n injectImport(ast, isCJSFile, 'intlayer', 'vite-intlayer');\n\n genericRecastVisit(ast, (obj) =>\n updatePluginArray(obj, 'plugins', 'intlayer')\n );\n\n return recast.print(ast).code;\n};\n\nexport const updateAstroConfig = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n injectImport(ast, isCJSFile, 'intlayer', 'astro-intlayer');\n\n genericRecastVisit(ast, (obj) =>\n updatePluginArray(obj, 'integrations', 'intlayer')\n );\n\n return recast.print(ast).code;\n};\n\nexport const updateNextConfig = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile = extension === 'cjs' || content.includes('module.exports');\n\n injectImport(ast, isCJSFile, 'withIntlayer', 'next-intlayer/server');\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const declaration = path.node.declaration;\n if (\n n.Expression.check(declaration) &&\n !(\n n.CallExpression.check(declaration) &&\n n.Identifier.check(declaration.callee) &&\n declaration.callee.name === 'withIntlayer'\n )\n ) {\n path\n .get('declaration')\n .replace(\n b.callExpression(b.identifier('withIntlayer'), [declaration as any])\n );\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports' &&\n !(\n n.CallExpression.check(right) &&\n n.Identifier.check(right.callee) &&\n right.callee.name === 'withIntlayer'\n )\n ) {\n path\n .get('right')\n .replace(b.callExpression(b.identifier('withIntlayer'), [right]));\n }\n return false;\n },\n });\n\n return recast.print(ast).code;\n};\n\n/**\n * Returns true when the expression looks like an Immediately Invoked Function\n * Expression (e.g. `(async () => { ... })()`). Such custom async exports cannot\n * be safely wrapped with the synchronous Metro helper, so they are skipped.\n */\nconst isImmediatelyInvokedFunction = (node: any): boolean =>\n n.CallExpression.check(node) &&\n (n.ArrowFunctionExpression.check(node.callee) ||\n n.FunctionExpression.check(node.callee));\n\n/**\n * Wraps a React Native Metro config's exported value with\n * `configMetroIntlayerSync` from `react-native-intlayer/metro`, injecting the\n * import. The synchronous helper is used because it wraps a plain config object\n * and needs no IIFE, making it safe to inject into existing configs without\n * restructuring them.\n *\n * Non-destructive: returns the content unchanged when the export is already\n * wrapped, when the Intlayer Metro plugin is already present, or when the export\n * is a custom async IIFE that cannot be wrapped synchronously. Callers should\n * compare the result with the input to detect the skipped case.\n */\nexport const updateMetroConfig = (\n content: string,\n extension: string\n): string => {\n if (content.includes('react-native-intlayer')) {\n return content;\n }\n\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n const wrapperName = 'configMetroIntlayerSync';\n\n const isWrappable = (node: any): boolean =>\n n.Expression.check(node) &&\n !isImmediatelyInvokedFunction(node) &&\n !(\n n.CallExpression.check(node) &&\n n.Identifier.check(node.callee) &&\n node.callee.name === wrapperName\n );\n\n let wrapped = false;\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const declaration = path.node.declaration;\n if (isWrappable(declaration)) {\n path\n .get('declaration')\n .replace(\n b.callExpression(b.identifier(wrapperName), [declaration as any])\n );\n wrapped = true;\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports' &&\n isWrappable(right)\n ) {\n path\n .get('right')\n .replace(b.callExpression(b.identifier(wrapperName), [right]));\n wrapped = true;\n }\n return false;\n },\n });\n\n // Only inject the import when an export was actually wrapped, so an\n // un-wrappable config is left completely untouched.\n if (!wrapped) {\n return content;\n }\n\n injectImport(ast, isCJSFile, wrapperName, 'react-native-intlayer/metro');\n\n return recast.print(ast).code;\n};\n\n/**\n * Builds the contents of a fresh `metro.config.js` wired with the Intlayer\n * Metro plugin. Uses the async `configMetroIntlayer` helper (which can build\n * dictionaries on server start) and picks the default-config source based on\n * whether the project is an Expo app.\n */\nexport const getMetroConfigTemplate = (isExpo: boolean): string => {\n const defaultConfigSource = isExpo\n ? 'expo/metro-config'\n : '@react-native/metro-config';\n\n return `const { getDefaultConfig } = require(\"${defaultConfigSource}\");\nconst { configMetroIntlayer } = require(\"react-native-intlayer/metro\");\n\nmodule.exports = (async () => {\n const defaultConfig = getDefaultConfig(__dirname);\n\n return await configMetroIntlayer(defaultConfig);\n})();\n`;\n};\n\nexport const updateNuxtConfig = (content: string): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const updateConfigObject = (objExpr: any) => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' &&\n !n.ObjectExpression.check(objExpr))\n )\n return;\n\n let modulesProp = objExpr.properties.find((p: any) => {\n if (!p?.key) return false;\n const keyName = p.key.name || p.key.value;\n return keyName === 'modules';\n }) as any;\n\n if (!modulesProp) {\n modulesProp = b.property(\n 'init',\n b.identifier('modules'),\n b.arrayExpression([])\n );\n objExpr.properties.push(modulesProp);\n }\n\n const modulesValue = modulesProp.value;\n\n if (\n modulesValue &&\n (modulesValue.type === 'ArrayExpression' ||\n n.ArrayExpression.check(modulesValue))\n ) {\n const hasModule = modulesValue.elements.some((el: any) => {\n if (\n n.StringLiteral.check(el) ||\n el.type === 'StringLiteral' ||\n el.type === 'Literal'\n ) {\n return (el.value || el.extra?.rawValue) === 'nuxt-intlayer';\n }\n return false;\n });\n\n if (!hasModule) {\n modulesValue.elements.push(b.stringLiteral('nuxt-intlayer'));\n }\n }\n };\n\n genericRecastVisit(ast, updateConfigObject, ['defineNuxtConfig']);\n\n return recast.print(ast).code;\n};\n\n/**\n * Updates a Vite config for vue-i18n compat: injects `vueI18nVitePlugin` from\n * `@intlayer/vue-i18n/plugin` into the plugins array.\n */\nexport const updateViteConfigForVueI18n = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n injectImport(\n ast,\n isCJSFile,\n 'vueI18nVitePlugin',\n '@intlayer/vue-i18n/plugin'\n );\n\n genericRecastVisit(ast, (obj) =>\n updatePluginArray(obj, 'plugins', 'vueI18nVitePlugin')\n );\n\n return recast.print(ast).code;\n};\n\n/**\n * Generic vite config updater for any compat plugin that uses alias injection.\n * Injects the named import from `pluginPackageSource` and appends the plugin\n * call to the `plugins` array.\n */\nexport const updateViteConfigForCompatPlugin = (\n content: string,\n extension: string,\n pluginConfig: CompatVitePluginConfig\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile =\n extension === 'cjs' ||\n (content.includes('module.exports') && !content.includes('import '));\n\n injectImport(\n ast,\n isCJSFile,\n pluginConfig.pluginFunctionName,\n pluginConfig.pluginPackageSource\n );\n\n genericRecastVisit(ast, (obj) =>\n updatePluginArray(obj, 'plugins', pluginConfig.pluginFunctionName)\n );\n\n return recast.print(ast).code;\n};\n\n/**\n * Rewrites the module source of an existing named import in a vite config,\n * keeping the imported binding and its call site untouched. Used when a compat\n * plugin is a drop-in replacement for an i18n library's own vite plugin — e.g.\n * lingui: `import { lingui } from \"@lingui/vite-plugin\"` becomes\n * `import { lingui } from \"@intlayer/lingui/plugin\"`, leaving `lingui()` in the\n * `plugins` array as-is. Returns the content unchanged when no matching import\n * is found.\n */\nexport const replaceViteConfigPluginImportSource = (\n content: string,\n importName: string,\n fromPackageSource: string,\n toPackageSource: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n let changed = false;\n\n recast.visit(ast, {\n visitImportDeclaration(path) {\n const { source, specifiers } = path.node;\n const importsBinding = (specifiers ?? []).some(\n (specifier) =>\n n.ImportSpecifier.check(specifier) &&\n specifier.imported.name === importName\n );\n\n if (\n n.StringLiteral.check(source) &&\n source.value === fromPackageSource &&\n importsBinding\n ) {\n source.value = toPackageSource;\n changed = true;\n }\n\n return false;\n },\n });\n\n if (!changed) return content;\n return recast.print(ast).code;\n};\n\n/**\n * Generic Next.js config wrapper for compat plugins. Injects the import and\n * wraps the default export / `module.exports` with a HOC call.\n */\nconst wrapNextConfigWithHoc = (\n content: string,\n extension: string,\n hocFunctionName: string,\n pluginPackageSource: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile = extension === 'cjs' || content.includes('module.exports');\n\n injectImport(ast, isCJSFile, hocFunctionName, pluginPackageSource);\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const declaration = path.node.declaration;\n if (\n n.Expression.check(declaration) &&\n !(\n n.CallExpression.check(declaration) &&\n n.Identifier.check(declaration.callee) &&\n declaration.callee.name === hocFunctionName\n )\n ) {\n path\n .get('declaration')\n .replace(\n b.callExpression(b.identifier(hocFunctionName), [\n declaration as any,\n ])\n );\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports' &&\n !(\n n.CallExpression.check(right) &&\n n.Identifier.check(right.callee) &&\n right.callee.name === hocFunctionName\n )\n ) {\n path\n .get('right')\n .replace(b.callExpression(b.identifier(hocFunctionName), [right]));\n }\n return false;\n },\n });\n\n return recast.print(ast).code;\n};\n\n/**\n * Updates a Next.js config for next-translate compat: wraps the default export\n * with `withNextTranslate` from `@intlayer/next-translate/plugin`.\n */\nexport const updateNextConfigForNextTranslate = (\n content: string,\n extension: string\n): string =>\n wrapNextConfigWithHoc(\n content,\n extension,\n 'withNextTranslate',\n '@intlayer/next-translate/plugin'\n );\n\n/**\n * Updates a Nuxt config for nuxtjs-i18n compat: adds `@intlayer/nuxtjs-i18n`\n * to the `modules` array.\n */\nexport const updateNuxtConfigForNuxtjsI18n = (content: string): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const updateConfigObject = (objExpr: any) => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' &&\n !n.ObjectExpression.check(objExpr))\n )\n return;\n\n let modulesProp = (objExpr.properties as any[]).find((p: any) => {\n if (!p?.key) return false;\n const keyName = p.key.name || p.key.value;\n return keyName === 'modules';\n }) as any;\n\n if (!modulesProp) {\n modulesProp = b.property(\n 'init',\n b.identifier('modules'),\n b.arrayExpression([])\n );\n (objExpr.properties as any[]).push(modulesProp);\n }\n\n const modulesValue = modulesProp.value;\n\n if (\n modulesValue &&\n (modulesValue.type === 'ArrayExpression' ||\n n.ArrayExpression.check(modulesValue))\n ) {\n const hasModule = (modulesValue.elements as any[]).some((el: any) => {\n if (\n n.StringLiteral.check(el) ||\n el.type === 'StringLiteral' ||\n el.type === 'Literal'\n ) {\n return (el.value ?? el.extra?.rawValue) === '@intlayer/nuxtjs-i18n';\n }\n return false;\n });\n\n if (!hasModule) {\n (modulesValue.elements as any[]).push(\n b.stringLiteral('@intlayer/nuxtjs-i18n')\n );\n }\n }\n };\n\n genericRecastVisit(ast, updateConfigObject, ['defineNuxtConfig']);\n\n return recast.print(ast).code;\n};\n\n/**\n * Updates a Next.js config for next-i18next compat: wraps the default export\n * with `withI18next` from `@intlayer/next-i18next/plugin`.\n */\nexport const updateNextConfigForNextI18next = (\n content: string,\n extension: string\n): string =>\n wrapNextConfigWithHoc(\n content,\n extension,\n 'withI18next',\n '@intlayer/next-i18next/plugin'\n );\n\n/**\n * Updates a Next.js config for next-intl compat: replaces any existing\n * `next-intl/plugin` import source with `@intlayer/next-intl/plugin`, or\n * injects `createNextIntlPlugin` with a factory-call wrapper when no such\n * import is present.\n */\nexport const updateNextConfigForNextIntl = (\n content: string,\n extension: string\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile = extension === 'cjs' || content.includes('module.exports');\n let replacedExistingSource = false;\n\n // Replace 'next-intl/plugin' import source with the compat package.\n recast.visit(ast, {\n visitImportDeclaration(path) {\n if (path.node.source.value === 'next-intl/plugin') {\n path.node.source = b.stringLiteral('@intlayer/next-intl/plugin');\n replacedExistingSource = true;\n }\n return false;\n },\n });\n\n if (replacedExistingSource) {\n return recast.print(ast).code;\n }\n\n // No existing next-intl/plugin import: check whether createNextIntlPlugin is\n // already present from any source before injecting the full factory pattern.\n const hasCreatePlugin = (ast.program.body as any[]).some((stmt: any) => {\n if (!n.ImportDeclaration.check(stmt)) return false;\n return (stmt.specifiers ?? []).some(\n (spec: any) =>\n (n.ImportSpecifier.check(spec) &&\n spec.imported.name === 'createNextIntlPlugin') ||\n (n.ImportDefaultSpecifier.check(spec) &&\n spec.local?.name === 'createNextIntlPlugin')\n );\n });\n\n if (hasCreatePlugin) {\n return recast.print(ast).code;\n }\n\n // Inject the import.\n injectImport(\n ast,\n isCJSFile,\n 'createNextIntlPlugin',\n '@intlayer/next-intl/plugin'\n );\n\n // Insert a factory-call variable declaration after the last import.\n const lastImportIndex = (ast.program.body as any[]).reduce(\n (lastIndex: number, stmt: any, index: number) => {\n if (n.ImportDeclaration.check(stmt)) return index;\n return lastIndex;\n },\n -1\n );\n\n const factoryCallDeclaration = b.variableDeclaration('const', [\n b.variableDeclarator(\n b.identifier('_withNextIntlayer'),\n b.callExpression(b.identifier('createNextIntlPlugin'), [])\n ),\n ]);\n\n (ast.program.body as any[]).splice(\n lastImportIndex + 1,\n 0,\n factoryCallDeclaration\n );\n\n // Wrap the default export with _withNextIntlayer(...).\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const declaration = path.node.declaration;\n if (\n n.Expression.check(declaration) &&\n !(\n n.CallExpression.check(declaration) &&\n n.Identifier.check(declaration.callee) &&\n declaration.callee.name === '_withNextIntlayer'\n )\n ) {\n path\n .get('declaration')\n .replace(\n b.callExpression(b.identifier('_withNextIntlayer'), [\n declaration as any,\n ])\n );\n }\n return false;\n },\n visitAssignmentExpression(path) {\n const { left, right } = path.node;\n\n if (\n n.MemberExpression.check(left) &&\n recast.print(left).code === 'module.exports' &&\n !(\n n.CallExpression.check(right) &&\n n.Identifier.check(right.callee) &&\n right.callee.name === '_withNextIntlayer'\n )\n ) {\n path\n .get('right')\n .replace(\n b.callExpression(b.identifier('_withNextIntlayer'), [right])\n );\n }\n return false;\n },\n });\n\n return recast.print(ast).code;\n};\n\n/** The sync plugin used to ingest a compat library's catalogs. */\ntype SyncPluginInfo = {\n /** Called function name, e.g. `'syncJSON'`. */\n functionName: string;\n /** Package the function is imported from. */\n packageSource: string;\n};\n\n/** Resolves the sync plugin (function + package) for a compat sync config. */\nconst getSyncPluginInfo = (syncConfig: CompatSyncConfig): SyncPluginInfo =>\n syncConfig.plugin === 'po'\n ? { functionName: 'syncPO', packageSource: '@intlayer/sync-po-plugin' }\n : { functionName: 'syncJSON', packageSource: '@intlayer/sync-json-plugin' };\n\n/**\n * Parses a `syncJSON({ ... })` / `syncPO({ ... })` call expression from a source\n * snippet so it can be injected into a config AST without manually constructing\n * template-literal nodes via builders.\n *\n * The destructuring parameters adapt to whether the source template uses the\n * `key` placeholder (nested pattern) or only `locale` (flat pattern).\n */\nconst buildSyncCallNode = (syncConfig: CompatSyncConfig): any => {\n const { functionName } = getSyncPluginInfo(syncConfig);\n const usesKey = syncConfig.sourceTemplate.includes('${key}');\n const paramDestructuring = !usesKey\n ? '{ locale }'\n : // `icu` (and PO, which is ICU-dialect) reads `{ key, locale }`; the\n // i18next/vue-i18n JSON dialects keep their established `{ locale, key }`.\n syncConfig.format === 'icu' || syncConfig.plugin === 'po'\n ? '{ key, locale }'\n : '{ locale, key }';\n\n // `syncPO` always serializes gettext, so it takes no `format`. `syncJSON`\n // needs the JSON dialect.\n const formatProperty =\n syncConfig.plugin === 'po' ? '' : `format: '${syncConfig.format}', `;\n\n // `splitKeys` is written explicitly only when forced on (next-intl / use-intl\n // single-file namespace model). When omitted, syncJSON auto-detects it from\n // the presence of a `${key}` segment in the source.\n const splitKeysProperty = syncConfig.splitKeys ? ', splitKeys: true' : '';\n\n // The sourceTemplate contains ${locale} / ${key} as literal characters;\n // they become proper template expressions once the snippet is parsed by recast.\n const snippet = `${functionName}({ ${formatProperty}source: (${paramDestructuring}) => \\`${syncConfig.sourceTemplate}\\`${splitKeysProperty} })`;\n const snippetAst = recast.parse(snippet, {\n parser: require('recast/parsers/typescript'),\n });\n return (snippetAst.program.body[0] as any).expression;\n};\n\n/**\n * Injects or ensures `dictionary: { format: '<value>' }` exists in an object\n * expression. Leaves any pre-existing `dictionary` properties untouched —\n * only the `format` sub-property is added when absent.\n */\nconst injectDictionaryFormat = (objExpr: any, format: string): void => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' && !n.ObjectExpression.check(objExpr))\n )\n return;\n\n let dictionaryProp = (objExpr.properties as any[]).find((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === 'dictionary';\n });\n\n if (!dictionaryProp) {\n dictionaryProp = b.property(\n 'init',\n b.identifier('dictionary'),\n b.objectExpression([])\n );\n (objExpr.properties as any[]).push(dictionaryProp);\n }\n\n const dictionaryObj = dictionaryProp.value;\n if (\n !dictionaryObj ||\n (dictionaryObj.type !== 'ObjectExpression' &&\n !n.ObjectExpression.check(dictionaryObj))\n )\n return;\n\n const hasFormat = (dictionaryObj.properties as any[]).some((prop: any) => {\n if (!prop?.key) return false;\n return (prop.key.name ?? prop.key.value) === 'format';\n });\n\n if (!hasFormat) {\n (dictionaryObj.properties as any[]).push(\n b.property('init', b.identifier('format'), b.stringLiteral(format))\n );\n }\n};\n\n/**\n * Injects the sync plugin import (`syncJSON` / `syncPO`) and a configured\n * `syncJSON(...)` / `syncPO(...)` call into the plugins array of an intlayer\n * config file. Idempotent: skips when the plugin call is already present.\n */\nexport const updateIntlayerConfigWithSyncPlugin = (\n content: string,\n extension: string,\n syncConfig: CompatSyncConfig\n): string => {\n const ast = recast.parse(content, {\n parser: require('recast/parsers/typescript'),\n });\n\n const isCJSFile = extension === 'cjs' || content.includes('module.exports');\n\n const { functionName, packageSource } = getSyncPluginInfo(syncConfig);\n\n injectImport(ast, isCJSFile, functionName, packageSource);\n\n const callNode = buildSyncCallNode(syncConfig);\n\n // PO catalogs are serialized as gettext; JSON catalogs carry the dialect.\n const dictionaryFormat =\n syncConfig.plugin === 'po' ? 'po' : syncConfig.format;\n\n genericRecastVisit(ast, (objExpr) => {\n if (\n !objExpr ||\n (objExpr.type !== 'ObjectExpression' &&\n !n.ObjectExpression.check(objExpr))\n )\n return;\n\n // Inject dictionary.format alongside the plugin so intlayer knows how to\n // interpret dictionary content at runtime.\n injectDictionaryFormat(objExpr, dictionaryFormat);\n\n let pluginsProp = (objExpr.properties as any[]).find((prop: any) => {\n if (!prop?.key) return false;\n const keyName = prop.key.name ?? prop.key.value;\n return keyName === 'plugins';\n });\n\n if (!pluginsProp) {\n pluginsProp = b.property(\n 'init',\n b.identifier('plugins'),\n b.arrayExpression([])\n );\n (objExpr.properties as any[]).push(pluginsProp);\n }\n\n const arrayValue = pluginsProp.value;\n\n if (\n arrayValue &&\n (arrayValue.type === 'ArrayExpression' ||\n n.ArrayExpression.check(arrayValue))\n ) {\n const hasSyncPlugin = (arrayValue.elements as any[]).some(\n (element: any) => {\n const callee = element?.callee;\n if (!callee) return false;\n const name: string = callee.name ?? callee.id?.name;\n return name === functionName;\n }\n );\n\n if (!hasSyncPlugin) {\n (arrayValue.elements as any[]).push(callNode);\n }\n }\n });\n\n return recast.print(ast).code;\n};\n"],"mappings":";;;;;;;AAQA,MAAM,EAAE,UAAU,GAAG,YAAY,MAAM,OAAO;;AAM9C,MAAM,sBAAsB,SAC1B,QAAQ,KAAK,KACZ,KAAK,SAAS,sBAAsB,EAAE,iBAAiB,MAAM,KAAK;;;;;;AAOrE,MAAM,wBACJ,SACA,KACA,qBACQ;CACR,IAAI,WAAY,QAAQ,WAAqB,MAAM,SAAc;AAC/D,MAAI,CAAC,MAAM,IAAK,QAAO;AACvB,UAAQ,KAAK,IAAI,QAAQ,KAAK,IAAI,WAAW;GAC7C;AAEF,KAAI,CAAC,UAAU;AACb,aAAW,EAAE,SAAS,QAAQ,EAAE,WAAW,IAAI,EAAE,iBAAiB;AAClE,EAAC,QAAQ,WAAqB,KAAK,SAAS;;AAG9C,QAAO;;;;;;;AAQT,MAAM,0BACJ,SACA,KACA,cACS;CACT,MAAM,WAAY,QAAQ,WAAqB,MAAM,SAAc;AACjE,MAAI,CAAC,MAAM,IAAK,QAAO;AACvB,UAAQ,KAAK,IAAI,QAAQ,KAAK,IAAI,WAAW;GAC7C;AAEF,KAAI,UAAU;AACZ,WAAS,QAAQ;AACjB;;AAGF,CAAC,QAAQ,WAAqB,KAC5B,EAAE,SAAS,QAAQ,EAAE,WAAW,IAAI,EAAE,UAAU,CACjD;;;;;;AAOH,MAAM,2BACJ,SACA,KACA,WACS;AAMT,KALqB,QAAQ,WAAqB,MAAM,SAAc;AACpE,MAAI,CAAC,MAAM,IAAK,QAAO;AACvB,UAAQ,KAAK,IAAI,QAAQ,KAAK,IAAI,WAAW;GAGhC,CAAE;AAEjB,CAAC,QAAQ,WAAqB,KAC5B,EAAE,SACA,QACA,EAAE,WAAW,IAAI,EACjB,EAAE,iBACA,EAAE,iBAAiB,EAAE,WAAW,UAAU,EAAE,EAAE,WAAW,MAAM,CAAC,EAChE,EAAE,WAAW,OAAO,CACrB,CACF,CACF;;;;;;;;;AAUH,MAAa,gCACX,SACA,WACA,SACW;AACX,KAAI,cAAc,OAChB,QAAO,QAAQ,QACb,6EACA,MAAM,KAAK,GACZ;CAGH,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;AAEF,oBAAmB,MAAM,YAAY;AACnC,MAAI,CAAC,mBAAmB,QAAQ,CAAE;EAElC,MAAM,kBAAkB,qBACtB,SACA,WACA,EAAE,iBAAiB,EAAE,CAAC,CACvB;AAED,MAAI,CAAC,mBAAmB,gBAAgB,MAAM,CAAE;AAEhD,yBACE,gBAAgB,OAChB,QACA,EAAE,cAAc,KAAK,CACtB;GACD;AAEF,QAAO,OAAO,MAAM,IAAI,CAAC;;;;;;;;;;AAW3B,MAAa,8BAA8B,YAA4B;CACrE,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;AAEF,oBAAmB,MAAM,YAAY;AACnC,MAAI,CAAC,mBAAmB,QAAQ,CAAE;EAElC,MAAM,iBAAiB,qBACrB,SACA,UACA,EAAE,iBAAiB,EAAE,CAAC,CACvB;AAED,MAAI,CAAC,mBAAmB,eAAe,MAAM,CAAE;EAE/C,MAAM,eAAe,eAAe;AAEpC,yBAAuB,cAAc,WAAW,EAAE,eAAe,KAAK,CAAC;AACvE,0BAAwB,cAAc,YAAY,qBAAqB;AACvE,0BACE,cACA,gBACA,yBACD;GACD;AAEF,QAAO,OAAO,MAAM,IAAI,CAAC;;AAG3B,MAAM,gBACJ,KACA,OACA,YACA,WACG;AAGH,KAAI,CAAC,SAASA,mDAAqB,KAAK,WAAW,CAAE;AA+BrD,KA7Ba,IAAI,QAAQ,KACF,MAAM,SAAc;AACzC,MAAI,MACF,QACE,EAAE,oBAAoB,MAAM,KAAK,IACjC,KAAK,aAAa,MACf,SACC,EAAE,mBAAmB,MAAM,KAAK,IAChC,EAAE,eAAe,MAAM,KAAK,KAAK,IACjC,EAAE,WAAW,MAAM,KAAK,KAAK,OAAO,IACpC,KAAK,KAAK,OAAO,SAAS,aAC1B,EAAE,cAAc,MAAM,KAAK,KAAK,UAAU,GAAG,IAC7C,KAAK,KAAK,UAAU,GAAG,UAAU,OACpC;AAGL,SACE,EAAE,kBAAkB,MAAM,KAAK,KAC9B,KAAK,OAAO,UAAU,UACrB,KAAK,YAAY,MACd,SACE,EAAE,gBAAgB,MAAM,KAAK,IAC5B,KAAK,SAAS,SAAS,cACxB,EAAE,uBAAuB,MAAM,KAAK,IACnC,KAAK,OAAO,SAAS,WAC1B;GAIM,CAAE;CAEf,MAAM,cAAc,QAChB,EAAE,oBAAoB,SAAS,CAC7B,EAAE,mBACA,EAAE,WAAW,KAAK,WAAW,IAAI,EACjC,EAAE,eAAe,EAAE,WAAW,UAAU,EAAE,CAAC,EAAE,cAAc,OAAO,CAAC,CAAC,CACrE,CACF,CAAC,GACF,EAAE,kBACA,CAAC,EAAE,gBAAgB,EAAE,WAAW,WAAW,CAAC,CAAC,EAC7C,EAAE,cAAc,OAAO,CACxB;AAEL,KAAI,QAAQ,KAAK,QAAQ,YAAY;;AAGvC,MAAM,qBACJ,SACA,cACA,eACG;AACH,KACE,CAAC,WACA,QAAQ,SAAS,sBAAsB,CAAC,EAAE,iBAAiB,MAAM,QAAQ,CAE1E;CAEF,IAAI,OAAO,QAAQ,WAAW,MAAM,MAAW;AAC7C,MAAI,CAAC,GAAG,IAAK,QAAO;AAEpB,UADgB,EAAE,IAAI,QAAQ,EAAE,IAAI,WACjB;GACnB;AAEF,KAAI,CAAC,MAAM;AACT,SAAO,EAAE,SACP,QACA,EAAE,WAAW,aAAa,EAC1B,EAAE,gBAAgB,EAAE,CAAC,CACtB;AACD,UAAQ,WAAW,KAAK,KAAK;;CAG/B,MAAM,aAAa,KAAK;AAExB,KACE,eACC,WAAW,SAAS,qBACnB,EAAE,gBAAgB,MAAM,WAAW,GASrC;MAAI,CAPc,WAAW,SAAS,MAAM,OAAY;GACtD,MAAM,SAAS,IAAI;AACnB,OAAI,CAAC,OAAQ,QAAO;GACpB,MAAM,OAAO,OAAO,QAAQ,OAAO,IAAI;AACvC,UAAO,SAAS,cAAc,SAAS;IAG3B,CACZ,YAAW,SAAS,KAAK,EAAE,eAAe,EAAE,WAAW,WAAW,EAAE,EAAE,CAAC,CAAC;;;AAK9E,MAAM,sBACJ,KACA,oBACA,YAAsB,CAAC,eAAe,KACnC;;;;;;CAMH,MAAM,2BAA2B,SAAiB;AAChD,MAAI,QAAQ,KAAK,SAAS,SAAc;AACtC,OAAI,EAAE,oBAAoB,MAAM,KAAK,CACnC,MAAK,aAAa,SAAS,UAAe;AACxC,QACE,EAAE,mBAAmB,MAAM,MAAM,IACjC,EAAE,WAAW,MAAM,MAAM,GAAG,IAC5B,MAAM,GAAG,SAAS,QAClB,EAAE,iBAAiB,MAAM,MAAM,KAAK,CAEpC,oBAAmB,MAAM,KAAK;KAEhC;IAEJ;;AAGJ,QAAO,MAAM,KAAK;EAChB,8BAA8B,MAAM;GAClC,MAAM,OAAO,KAAK,KAAK;AAEvB,OAAI,EAAE,iBAAiB,MAAM,KAAK,CAChC,oBAAmB,KAAK;YAExB,EAAE,eAAe,MAAM,KAAK,IAC5B,EAAE,WAAW,MAAM,KAAK,OAAO,IAC/B,UAAU,SAAS,KAAK,OAAO,KAAK,EAEpC;QAAI,EAAE,iBAAiB,MAAM,KAAK,UAAU,GAAG,CAC7C,oBAAmB,KAAK,UAAU,GAAG;cAE9B,EAAE,WAAW,MAAM,KAAK,CACjC,yBAAwB,KAAK,KAAK;AAEpC,UAAO;;EAET,0BAA0B,MAAM;GAC9B,MAAM,EAAE,MAAM,UAAU,KAAK;AAE7B,OACE,EAAE,iBAAiB,MAAM,KAAK,IAC9B,OAAO,MAAM,KAAK,CAAC,SAAS,kBAE5B;QAAI,EAAE,iBAAiB,MAAM,MAAM,CACjC,oBAAmB,MAAM;aAEzB,EAAE,eAAe,MAAM,MAAM,IAC7B,EAAE,WAAW,MAAM,MAAM,OAAO,IAChC,UAAU,SAAS,MAAM,OAAO,KAAK,EAErC;SAAI,EAAE,iBAAiB,MAAM,MAAM,UAAU,GAAG,CAC9C,oBAAmB,MAAM,UAAU,GAAG;eAE/B,EAAE,WAAW,MAAM,MAAM,CAClC,yBAAwB,MAAM,KAAK;;AAGvC,UAAO;;EAEV,CAAC;;AAGJ,MAAa,oBACX,SACA,cACW;CACX,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;AAMF,cAAa,KAHX,cAAc,SACb,QAAQ,SAAS,iBAAiB,IAAI,CAAC,QAAQ,SAAS,UAAU,EAExC,YAAY,gBAAgB;AAEzD,oBAAmB,MAAM,QACvB,kBAAkB,KAAK,WAAW,WAAW,CAC9C;AAED,QAAO,OAAO,MAAM,IAAI,CAAC;;AAG3B,MAAa,qBACX,SACA,cACW;CACX,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;AAMF,cAAa,KAHX,cAAc,SACb,QAAQ,SAAS,iBAAiB,IAAI,CAAC,QAAQ,SAAS,UAAU,EAExC,YAAY,iBAAiB;AAE1D,oBAAmB,MAAM,QACvB,kBAAkB,KAAK,gBAAgB,WAAW,CACnD;AAED,QAAO,OAAO,MAAM,IAAI,CAAC;;AAG3B,MAAa,oBACX,SACA,cACW;CACX,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;AAIF,cAAa,KAFK,cAAc,SAAS,QAAQ,SAAS,iBAAiB,EAE9C,gBAAgB,uBAAuB;AAEpE,QAAO,MAAM,KAAK;EAChB,8BAA8B,MAAM;GAClC,MAAM,cAAc,KAAK,KAAK;AAC9B,OACE,EAAE,WAAW,MAAM,YAAY,IAC/B,EACE,EAAE,eAAe,MAAM,YAAY,IACnC,EAAE,WAAW,MAAM,YAAY,OAAO,IACtC,YAAY,OAAO,SAAS,gBAG9B,MACG,IAAI,cAAc,CAClB,QACC,EAAE,eAAe,EAAE,WAAW,eAAe,EAAE,CAAC,YAAmB,CAAC,CACrE;AAEL,UAAO;;EAET,0BAA0B,MAAM;GAC9B,MAAM,EAAE,MAAM,UAAU,KAAK;AAE7B,OACE,EAAE,iBAAiB,MAAM,KAAK,IAC9B,OAAO,MAAM,KAAK,CAAC,SAAS,oBAC5B,EACE,EAAE,eAAe,MAAM,MAAM,IAC7B,EAAE,WAAW,MAAM,MAAM,OAAO,IAChC,MAAM,OAAO,SAAS,gBAGxB,MACG,IAAI,QAAQ,CACZ,QAAQ,EAAE,eAAe,EAAE,WAAW,eAAe,EAAE,CAAC,MAAM,CAAC,CAAC;AAErE,UAAO;;EAEV,CAAC;AAEF,QAAO,OAAO,MAAM,IAAI,CAAC;;;;;;;AAQ3B,MAAM,gCAAgC,SACpC,EAAE,eAAe,MAAM,KAAK,KAC3B,EAAE,wBAAwB,MAAM,KAAK,OAAO,IAC3C,EAAE,mBAAmB,MAAM,KAAK,OAAO;;;;;;;;;;;;;AAc3C,MAAa,qBACX,SACA,cACW;AACX,KAAI,QAAQ,SAAS,wBAAwB,CAC3C,QAAO;CAGT,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;CAEF,MAAM,YACJ,cAAc,SACb,QAAQ,SAAS,iBAAiB,IAAI,CAAC,QAAQ,SAAS,UAAU;CAErE,MAAM,cAAc;CAEpB,MAAM,eAAe,SACnB,EAAE,WAAW,MAAM,KAAK,IACxB,CAAC,6BAA6B,KAAK,IACnC,EACE,EAAE,eAAe,MAAM,KAAK,IAC5B,EAAE,WAAW,MAAM,KAAK,OAAO,IAC/B,KAAK,OAAO,SAAS;CAGzB,IAAI,UAAU;AAEd,QAAO,MAAM,KAAK;EAChB,8BAA8B,MAAM;GAClC,MAAM,cAAc,KAAK,KAAK;AAC9B,OAAI,YAAY,YAAY,EAAE;AAC5B,SACG,IAAI,cAAc,CAClB,QACC,EAAE,eAAe,EAAE,WAAW,YAAY,EAAE,CAAC,YAAmB,CAAC,CAClE;AACH,cAAU;;AAEZ,UAAO;;EAET,0BAA0B,MAAM;GAC9B,MAAM,EAAE,MAAM,UAAU,KAAK;AAE7B,OACE,EAAE,iBAAiB,MAAM,KAAK,IAC9B,OAAO,MAAM,KAAK,CAAC,SAAS,oBAC5B,YAAY,MAAM,EAClB;AACA,SACG,IAAI,QAAQ,CACZ,QAAQ,EAAE,eAAe,EAAE,WAAW,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC;AAChE,cAAU;;AAEZ,UAAO;;EAEV,CAAC;AAIF,KAAI,CAAC,QACH,QAAO;AAGT,cAAa,KAAK,WAAW,aAAa,8BAA8B;AAExE,QAAO,OAAO,MAAM,IAAI,CAAC;;;;;;;;AAS3B,MAAa,0BAA0B,WAA4B;AAKjE,QAAO,yCAJqB,SACxB,sBACA,6BAEgE;;;;;;;;;;AAWtE,MAAa,oBAAoB,YAA4B;CAC3D,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;CAEF,MAAM,sBAAsB,YAAiB;AAC3C,MACE,CAAC,WACA,QAAQ,SAAS,sBAChB,CAAC,EAAE,iBAAiB,MAAM,QAAQ,CAEpC;EAEF,IAAI,cAAc,QAAQ,WAAW,MAAM,MAAW;AACpD,OAAI,CAAC,GAAG,IAAK,QAAO;AAEpB,WADgB,EAAE,IAAI,QAAQ,EAAE,IAAI,WACjB;IACnB;AAEF,MAAI,CAAC,aAAa;AAChB,iBAAc,EAAE,SACd,QACA,EAAE,WAAW,UAAU,EACvB,EAAE,gBAAgB,EAAE,CAAC,CACtB;AACD,WAAQ,WAAW,KAAK,YAAY;;EAGtC,MAAM,eAAe,YAAY;AAEjC,MACE,iBACC,aAAa,SAAS,qBACrB,EAAE,gBAAgB,MAAM,aAAa,GAavC;OAAI,CAXc,aAAa,SAAS,MAAM,OAAY;AACxD,QACE,EAAE,cAAc,MAAM,GAAG,IACzB,GAAG,SAAS,mBACZ,GAAG,SAAS,UAEZ,SAAQ,GAAG,SAAS,GAAG,OAAO,cAAc;AAE9C,WAAO;KAGK,CACZ,cAAa,SAAS,KAAK,EAAE,cAAc,gBAAgB,CAAC;;;AAKlE,oBAAmB,KAAK,oBAAoB,CAAC,mBAAmB,CAAC;AAEjE,QAAO,OAAO,MAAM,IAAI,CAAC;;;;;;AAO3B,MAAa,8BACX,SACA,cACW;CACX,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;AAMF,cACE,KAJA,cAAc,SACb,QAAQ,SAAS,iBAAiB,IAAI,CAAC,QAAQ,SAAS,UAAU,EAKnE,qBACA,4BACD;AAED,oBAAmB,MAAM,QACvB,kBAAkB,KAAK,WAAW,oBAAoB,CACvD;AAED,QAAO,OAAO,MAAM,IAAI,CAAC;;;;;;;AAQ3B,MAAa,mCACX,SACA,WACA,iBACW;CACX,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;AAMF,cACE,KAJA,cAAc,SACb,QAAQ,SAAS,iBAAiB,IAAI,CAAC,QAAQ,SAAS,UAAU,EAKnE,aAAa,oBACb,aAAa,oBACd;AAED,oBAAmB,MAAM,QACvB,kBAAkB,KAAK,WAAW,aAAa,mBAAmB,CACnE;AAED,QAAO,OAAO,MAAM,IAAI,CAAC;;;;;;;;;;;AAY3B,MAAa,uCACX,SACA,YACA,mBACA,oBACW;CACX,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;CAEF,IAAI,UAAU;AAEd,QAAO,MAAM,KAAK,EAChB,uBAAuB,MAAM;EAC3B,MAAM,EAAE,QAAQ,eAAe,KAAK;EACpC,MAAM,kBAAkB,cAAc,EAAE,EAAE,MACvC,cACC,EAAE,gBAAgB,MAAM,UAAU,IAClC,UAAU,SAAS,SAAS,WAC/B;AAED,MACE,EAAE,cAAc,MAAM,OAAO,IAC7B,OAAO,UAAU,qBACjB,gBACA;AACA,UAAO,QAAQ;AACf,aAAU;;AAGZ,SAAO;IAEV,CAAC;AAEF,KAAI,CAAC,QAAS,QAAO;AACrB,QAAO,OAAO,MAAM,IAAI,CAAC;;;;;;AAO3B,MAAM,yBACJ,SACA,WACA,iBACA,wBACW;CACX,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;AAIF,cAAa,KAFK,cAAc,SAAS,QAAQ,SAAS,iBAAiB,EAE9C,iBAAiB,oBAAoB;AAElE,QAAO,MAAM,KAAK;EAChB,8BAA8B,MAAM;GAClC,MAAM,cAAc,KAAK,KAAK;AAC9B,OACE,EAAE,WAAW,MAAM,YAAY,IAC/B,EACE,EAAE,eAAe,MAAM,YAAY,IACnC,EAAE,WAAW,MAAM,YAAY,OAAO,IACtC,YAAY,OAAO,SAAS,iBAG9B,MACG,IAAI,cAAc,CAClB,QACC,EAAE,eAAe,EAAE,WAAW,gBAAgB,EAAE,CAC9C,YACD,CAAC,CACH;AAEL,UAAO;;EAET,0BAA0B,MAAM;GAC9B,MAAM,EAAE,MAAM,UAAU,KAAK;AAE7B,OACE,EAAE,iBAAiB,MAAM,KAAK,IAC9B,OAAO,MAAM,KAAK,CAAC,SAAS,oBAC5B,EACE,EAAE,eAAe,MAAM,MAAM,IAC7B,EAAE,WAAW,MAAM,MAAM,OAAO,IAChC,MAAM,OAAO,SAAS,iBAGxB,MACG,IAAI,QAAQ,CACZ,QAAQ,EAAE,eAAe,EAAE,WAAW,gBAAgB,EAAE,CAAC,MAAM,CAAC,CAAC;AAEtE,UAAO;;EAEV,CAAC;AAEF,QAAO,OAAO,MAAM,IAAI,CAAC;;;;;;AAO3B,MAAa,oCACX,SACA,cAEA,sBACE,SACA,WACA,qBACA,kCACD;;;;;AAMH,MAAa,iCAAiC,YAA4B;CACxE,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;CAEF,MAAM,sBAAsB,YAAiB;AAC3C,MACE,CAAC,WACA,QAAQ,SAAS,sBAChB,CAAC,EAAE,iBAAiB,MAAM,QAAQ,CAEpC;EAEF,IAAI,cAAe,QAAQ,WAAqB,MAAM,MAAW;AAC/D,OAAI,CAAC,GAAG,IAAK,QAAO;AAEpB,WADgB,EAAE,IAAI,QAAQ,EAAE,IAAI,WACjB;IACnB;AAEF,MAAI,CAAC,aAAa;AAChB,iBAAc,EAAE,SACd,QACA,EAAE,WAAW,UAAU,EACvB,EAAE,gBAAgB,EAAE,CAAC,CACtB;AACD,GAAC,QAAQ,WAAqB,KAAK,YAAY;;EAGjD,MAAM,eAAe,YAAY;AAEjC,MACE,iBACC,aAAa,SAAS,qBACrB,EAAE,gBAAgB,MAAM,aAAa,GAavC;OAAI,CAXe,aAAa,SAAmB,MAAM,OAAY;AACnE,QACE,EAAE,cAAc,MAAM,GAAG,IACzB,GAAG,SAAS,mBACZ,GAAG,SAAS,UAEZ,SAAQ,GAAG,SAAS,GAAG,OAAO,cAAc;AAE9C,WAAO;KAGK,CACZ,CAAC,aAAa,SAAmB,KAC/B,EAAE,cAAc,wBAAwB,CACzC;;;AAKP,oBAAmB,KAAK,oBAAoB,CAAC,mBAAmB,CAAC;AAEjE,QAAO,OAAO,MAAM,IAAI,CAAC;;;;;;AAO3B,MAAa,kCACX,SACA,cAEA,sBACE,SACA,WACA,eACA,gCACD;;;;;;;AAQH,MAAa,+BACX,SACA,cACW;CACX,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;CAEF,MAAM,YAAY,cAAc,SAAS,QAAQ,SAAS,iBAAiB;CAC3E,IAAI,yBAAyB;AAG7B,QAAO,MAAM,KAAK,EAChB,uBAAuB,MAAM;AAC3B,MAAI,KAAK,KAAK,OAAO,UAAU,oBAAoB;AACjD,QAAK,KAAK,SAAS,EAAE,cAAc,6BAA6B;AAChE,4BAAyB;;AAE3B,SAAO;IAEV,CAAC;AAEF,KAAI,uBACF,QAAO,OAAO,MAAM,IAAI,CAAC;AAgB3B,KAXyB,IAAI,QAAQ,KAAe,MAAM,SAAc;AACtE,MAAI,CAAC,EAAE,kBAAkB,MAAM,KAAK,CAAE,QAAO;AAC7C,UAAQ,KAAK,cAAc,EAAE,EAAE,MAC5B,SACE,EAAE,gBAAgB,MAAM,KAAK,IAC5B,KAAK,SAAS,SAAS,0BACxB,EAAE,uBAAuB,MAAM,KAAK,IACnC,KAAK,OAAO,SAAS,uBAC1B;GAGgB,CACjB,QAAO,OAAO,MAAM,IAAI,CAAC;AAI3B,cACE,KACA,WACA,wBACA,6BACD;CAGD,MAAM,kBAAmB,IAAI,QAAQ,KAAe,QACjD,WAAmB,MAAW,UAAkB;AAC/C,MAAI,EAAE,kBAAkB,MAAM,KAAK,CAAE,QAAO;AAC5C,SAAO;IAET,GACD;CAED,MAAM,yBAAyB,EAAE,oBAAoB,SAAS,CAC5D,EAAE,mBACA,EAAE,WAAW,oBAAoB,EACjC,EAAE,eAAe,EAAE,WAAW,uBAAuB,EAAE,EAAE,CAAC,CAC3D,CACF,CAAC;AAEF,CAAC,IAAI,QAAQ,KAAe,OAC1B,kBAAkB,GAClB,GACA,uBACD;AAGD,QAAO,MAAM,KAAK;EAChB,8BAA8B,MAAM;GAClC,MAAM,cAAc,KAAK,KAAK;AAC9B,OACE,EAAE,WAAW,MAAM,YAAY,IAC/B,EACE,EAAE,eAAe,MAAM,YAAY,IACnC,EAAE,WAAW,MAAM,YAAY,OAAO,IACtC,YAAY,OAAO,SAAS,qBAG9B,MACG,IAAI,cAAc,CAClB,QACC,EAAE,eAAe,EAAE,WAAW,oBAAoB,EAAE,CAClD,YACD,CAAC,CACH;AAEL,UAAO;;EAET,0BAA0B,MAAM;GAC9B,MAAM,EAAE,MAAM,UAAU,KAAK;AAE7B,OACE,EAAE,iBAAiB,MAAM,KAAK,IAC9B,OAAO,MAAM,KAAK,CAAC,SAAS,oBAC5B,EACE,EAAE,eAAe,MAAM,MAAM,IAC7B,EAAE,WAAW,MAAM,MAAM,OAAO,IAChC,MAAM,OAAO,SAAS,qBAGxB,MACG,IAAI,QAAQ,CACZ,QACC,EAAE,eAAe,EAAE,WAAW,oBAAoB,EAAE,CAAC,MAAM,CAAC,CAC7D;AAEL,UAAO;;EAEV,CAAC;AAEF,QAAO,OAAO,MAAM,IAAI,CAAC;;;AAY3B,MAAM,qBAAqB,eACzB,WAAW,WAAW,OAClB;CAAE,cAAc;CAAU,eAAe;CAA4B,GACrE;CAAE,cAAc;CAAY,eAAe;CAA8B;;;;;;;;;AAU/E,MAAM,qBAAqB,eAAsC;CAC/D,MAAM,EAAE,iBAAiB,kBAAkB,WAAW;CAEtD,MAAM,qBAAqB,CADX,WAAW,eAAe,SAAS,SAChB,GAC/B,eAGA,WAAW,WAAW,SAAS,WAAW,WAAW,OACnD,oBACA;CAIN,MAAM,iBACJ,WAAW,WAAW,OAAO,KAAK,YAAY,WAAW,OAAO;CAKlE,MAAM,oBAAoB,WAAW,YAAY,sBAAsB;CAIvE,MAAM,UAAU,GAAG,aAAa,KAAK,eAAe,WAAW,mBAAmB,SAAS,WAAW,eAAe,IAAI,kBAAkB;AAI3I,QAHmB,OAAO,MAAM,SAAS,EACvC,QAAQ,QAAQ,4BAA4B,EAC7C,CACiB,CAAC,QAAQ,KAAK,GAAW;;;;;;;AAQ7C,MAAM,0BAA0B,SAAc,WAAyB;AACrE,KACE,CAAC,WACA,QAAQ,SAAS,sBAAsB,CAAC,EAAE,iBAAiB,MAAM,QAAQ,CAE1E;CAEF,IAAI,iBAAkB,QAAQ,WAAqB,MAAM,SAAc;AACrE,MAAI,CAAC,MAAM,IAAK,QAAO;AACvB,UAAQ,KAAK,IAAI,QAAQ,KAAK,IAAI,WAAW;GAC7C;AAEF,KAAI,CAAC,gBAAgB;AACnB,mBAAiB,EAAE,SACjB,QACA,EAAE,WAAW,aAAa,EAC1B,EAAE,iBAAiB,EAAE,CAAC,CACvB;AACD,EAAC,QAAQ,WAAqB,KAAK,eAAe;;CAGpD,MAAM,gBAAgB,eAAe;AACrC,KACE,CAAC,iBACA,cAAc,SAAS,sBACtB,CAAC,EAAE,iBAAiB,MAAM,cAAc,CAE1C;AAOF,KAAI,CALe,cAAc,WAAqB,MAAM,SAAc;AACxE,MAAI,CAAC,MAAM,IAAK,QAAO;AACvB,UAAQ,KAAK,IAAI,QAAQ,KAAK,IAAI,WAAW;GAGjC,CACZ,CAAC,cAAc,WAAqB,KAClC,EAAE,SAAS,QAAQ,EAAE,WAAW,SAAS,EAAE,EAAE,cAAc,OAAO,CAAC,CACpE;;;;;;;AASL,MAAa,sCACX,SACA,WACA,eACW;CACX,MAAM,MAAM,OAAO,MAAM,SAAS,EAChC,QAAQ,QAAQ,4BAA4B,EAC7C,CAAC;CAEF,MAAM,YAAY,cAAc,SAAS,QAAQ,SAAS,iBAAiB;CAE3E,MAAM,EAAE,cAAc,kBAAkB,kBAAkB,WAAW;AAErE,cAAa,KAAK,WAAW,cAAc,cAAc;CAEzD,MAAM,WAAW,kBAAkB,WAAW;CAG9C,MAAM,mBACJ,WAAW,WAAW,OAAO,OAAO,WAAW;AAEjD,oBAAmB,MAAM,YAAY;AACnC,MACE,CAAC,WACA,QAAQ,SAAS,sBAChB,CAAC,EAAE,iBAAiB,MAAM,QAAQ,CAEpC;AAIF,yBAAuB,SAAS,iBAAiB;EAEjD,IAAI,cAAe,QAAQ,WAAqB,MAAM,SAAc;AAClE,OAAI,CAAC,MAAM,IAAK,QAAO;AAEvB,WADgB,KAAK,IAAI,QAAQ,KAAK,IAAI,WACvB;IACnB;AAEF,MAAI,CAAC,aAAa;AAChB,iBAAc,EAAE,SACd,QACA,EAAE,WAAW,UAAU,EACvB,EAAE,gBAAgB,EAAE,CAAC,CACtB;AACD,GAAC,QAAQ,WAAqB,KAAK,YAAY;;EAGjD,MAAM,aAAa,YAAY;AAE/B,MACE,eACC,WAAW,SAAS,qBACnB,EAAE,gBAAgB,MAAM,WAAW,GAWrC;OAAI,CATmB,WAAW,SAAmB,MAClD,YAAiB;IAChB,MAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ,QAAO;AAEpB,YADqB,OAAO,QAAQ,OAAO,IAAI,UAC/B;KAIF,CAChB,CAAC,WAAW,SAAmB,KAAK,SAAS;;GAGjD;AAEF,QAAO,OAAO,MAAM,IAAI,CAAC"}
@@ -126,6 +126,55 @@ const detectJsonLocalePattern = async (rootDir) => {
126
126
  locales: Array.from(flatLocales)
127
127
  };
128
128
  };
129
+ /**
130
+ * Scans the project for lingui catalog files (`{base}/{locale}/messages.po` or
131
+ * `…/messages.json`, lingui's default layout) and derives the `syncPO` /
132
+ * `syncJSON` `source` template.
133
+ *
134
+ * `.po` is preferred when present (lingui's default format). Returns `null`
135
+ * when no lingui catalog is found, in which case the caller falls back to the
136
+ * generic JSON detection.
137
+ *
138
+ * @param rootDir - Project root directory.
139
+ */
140
+ const detectLinguiCatalogPattern = async (rootDir) => {
141
+ const files = await (0, fast_glob.default)(["**/messages.po", "**/messages.json"], {
142
+ cwd: rootDir,
143
+ ignore: _intlayer_config_defaultValues.EXCLUDED_PATHS,
144
+ absolute: false,
145
+ onlyFiles: true
146
+ });
147
+ const basePathsByFormat = {
148
+ po: [],
149
+ json: []
150
+ };
151
+ const localesByFormat = {
152
+ po: /* @__PURE__ */ new Set(),
153
+ json: /* @__PURE__ */ new Set()
154
+ };
155
+ for (const file of files) {
156
+ const parts = file.split("/");
157
+ if (parts.length < 2) continue;
158
+ const localeDir = parts[parts.length - 2] ?? "";
159
+ if (!isLocaleSegment(localeDir)) continue;
160
+ const extension = file.endsWith(".po") ? "po" : "json";
161
+ basePathsByFormat[extension].push(parts.slice(0, -2).join("/") || ".");
162
+ localesByFormat[extension].add(localeDir);
163
+ }
164
+ const format = basePathsByFormat.po.length > 0 ? "po" : "json";
165
+ const basePaths = basePathsByFormat[format];
166
+ if (basePaths.length === 0) return null;
167
+ const counts = basePaths.reduce((accumulator, path) => {
168
+ accumulator[path] = (accumulator[path] ?? 0) + 1;
169
+ return accumulator;
170
+ }, {});
171
+ const topBasePath = Object.entries(counts).sort(([, a], [, b]) => b - a)[0]?.[0] ?? ".";
172
+ return {
173
+ format,
174
+ template: `${topBasePath === "." ? "." : `./${topBasePath}`}/\${locale}/\${key}.${format}`,
175
+ locales: Array.from(localesByFormat[format])
176
+ };
177
+ };
129
178
  /** Common locations of the `next-intl` request config, relative to the root. */
130
179
  const NEXT_INTL_REQUEST_FILES = [
131
180
  "i18n/request.ts",
@@ -173,6 +222,7 @@ const detectNextIntlMessagesPattern = async (rootDir) => {
173
222
 
174
223
  //#endregion
175
224
  exports.detectJsonLocalePattern = detectJsonLocalePattern;
225
+ exports.detectLinguiCatalogPattern = detectLinguiCatalogPattern;
176
226
  exports.detectNextIntlMessagesPattern = detectNextIntlMessagesPattern;
177
227
  exports.ensureDirectory = ensureDirectory;
178
228
  exports.exists = exists;
@@ -1 +1 @@
1
- {"version":3,"file":"fileSystem.cjs","names":["ALL_LOCALES","EXCLUDED_PATHS","sep"],"sources":["../../../../src/init/utils/fileSystem.ts"],"sourcesContent":["import { access, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative, resolve, sep } from 'node:path';\nimport { EXCLUDED_PATHS } from '@intlayer/config/defaultValues';\nimport { ALL_LOCALES } from '@intlayer/types/allLocales';\nimport fg from 'fast-glob';\n\n/**\n * Helper to check if a file exists\n */\nexport const exists = async (rootDir: string, filePath: string) => {\n try {\n await access(join(rootDir, filePath));\n return true;\n } catch {\n return false;\n }\n};\n\n/**\n * Helper to read a file\n */\nexport const readFileFromRoot = async (rootDir: string, filePath: string) =>\n await readFile(join(rootDir, filePath), 'utf8');\n\n/**\n * Helper to write a file\n */\nexport const writeFileToRoot = async (\n rootDir: string,\n filePath: string,\n content: string\n) => await writeFile(join(rootDir, filePath), content, 'utf8');\n\n/**\n * Helper to ensure a directory exists\n */\nexport const ensureDirectory = async (rootDir: string, dirPath: string) => {\n try {\n await mkdir(join(rootDir, dirPath), { recursive: true });\n } catch {\n // Directory already exists or could not be created\n }\n};\n\n/**\n * Pattern type for locale JSON file organisation.\n * - 'nested': files are at `{base}/{locale}/{key}.json`\n * - 'flat': files are at `{base}/{locale}.json`\n */\nexport type JsonLocalePatternType = 'nested' | 'flat';\n\n/**\n * Detected locale JSON file pattern and the corresponding source template.\n * `template` uses `${locale}` and `${key}` as literal placeholders (not JS\n * expressions) so it can be embedded directly in a template-literal string.\n */\nexport type JsonLocalePattern = {\n type: JsonLocalePatternType;\n /**\n * Source path template for syncJSON `source` option.\n * Example nested: `./locales/${locale}/${key}.json`\n * Example flat: `./locales/${locale}.json`\n */\n template: string;\n /**\n * Detected locales in the directory.\n */\n locales: string[];\n};\n\n/**\n * Set of all known locale string values from the intlayer Locales registry\n * (e.g. `'en'`, `'fr'`, `'zh-TW'`). Keyed by the exact locale string so\n * `.has()` lookups are O(1) and common short directory names (`src`, `lib`,\n * `app`, …) are not mistaken for locales.\n */\nconst ALL_LOCALE_VALUES = new Set<string>(Object.values(ALL_LOCALES));\n\n/**\n * Returns true when `segment` matches a known BCP-47 locale identifier, e.g.\n * `en`, `fr`, `zh-TW`, `pt-BR`, `en-US`.\n */\nconst isLocaleSegment = (segment: string): boolean =>\n ALL_LOCALE_VALUES.has(segment);\n\n/** JSON filenames that are never locale translation files. */\nconst KNOWN_CONFIG_FILENAMES = new Set([\n 'package.json',\n 'tsconfig.json',\n 'jsconfig.json',\n 'biome.json',\n 'turbo.json',\n 'lerna.json',\n 'vercel.json',\n 'netlify.json',\n 'babel.config.json',\n 'jest.config.json',\n 'vitest.config.json',\n '.eslintrc.json',\n '.prettierrc.json',\n]);\n\n/**\n * Scans the project for JSON files and determines whether locale files are\n * organised as `{base}/{locale}/{key}.json` (nested) or `{base}/{locale}.json`\n * (flat). Returns the most likely source template, or `null` when no locale\n * JSON files are found.\n *\n * The returned `template` contains `${locale}` and `${key}` as **literal**\n * placeholder strings so it can be embedded inside a JS template literal.\n */\nexport const detectJsonLocalePattern = async (\n rootDir: string\n): Promise<JsonLocalePattern | null> => {\n const files = await fg('**/*.json', {\n cwd: rootDir,\n ignore: EXCLUDED_PATHS,\n absolute: false,\n onlyFiles: true,\n });\n\n const nestedBasePaths: string[] = [];\n const flatBasePaths: string[] = [];\n const nestedLocales = new Set<string>();\n const flatLocales = new Set<string>();\n\n for (const file of files) {\n const parts = file.split('/');\n const filename = parts[parts.length - 1] ?? '';\n\n if (KNOWN_CONFIG_FILENAMES.has(filename)) continue;\n\n // Nested: …/{locale}/{key}.json — parent directory is a locale code\n if (parts.length >= 3) {\n const localeDir = parts[parts.length - 2] ?? '';\n if (isLocaleSegment(localeDir)) {\n nestedBasePaths.push(parts.slice(0, -2).join('/') || '.');\n nestedLocales.add(localeDir);\n }\n }\n\n // Flat: …/{locale}.json — filename (without extension) is a locale code\n if (parts.length >= 2) {\n const baseName = filename.slice(0, -5); // strip \".json\"\n if (isLocaleSegment(baseName)) {\n flatBasePaths.push(parts.slice(0, -1).join('/') || '.');\n flatLocales.add(baseName);\n }\n }\n }\n\n if (nestedBasePaths.length === 0 && flatBasePaths.length === 0) {\n return null;\n }\n\n /**\n * Returns the path prefix that appears most frequently among the matches,\n * formatted as a relative path suitable for a source template.\n */\n const mostFrequentPrefix = (paths: string[]): string => {\n const counts = paths.reduce<Record<string, number>>((accumulator, path) => {\n accumulator[path] = (accumulator[path] ?? 0) + 1;\n return accumulator;\n }, {});\n const topEntry = Object.entries(counts).sort(([, a], [, b]) => b - a)[0];\n const basePath = topEntry?.[0] ?? '.';\n return basePath === '.' ? '.' : `./${basePath}`;\n };\n\n if (nestedBasePaths.length >= flatBasePaths.length) {\n const prefix = mostFrequentPrefix(nestedBasePaths);\n return {\n type: 'nested',\n // Literal ${locale} and ${key} — not evaluated here, used in template literals\n template: `${prefix}/\\${locale}/\\${key}.json`,\n locales: Array.from(nestedLocales),\n };\n }\n\n const prefix = mostFrequentPrefix(flatBasePaths);\n return {\n type: 'flat',\n template: `${prefix}/\\${locale}.json`,\n locales: Array.from(flatLocales),\n };\n};\n\n/**\n * The messages source template derived from a `next-intl` `i18n/request.ts`\n * file, ready to be used as a `syncJSON` `source` builder.\n */\nexport type NextIntlMessagesPattern = {\n /** `'flat'` (one file per locale) or `'nested'` (per-namespace files). */\n type: JsonLocalePatternType;\n /**\n * Source path template relative to the project root, with `${locale}` (and\n * `${key}` when nested) as literal placeholders, e.g. `./messages/${locale}.json`.\n */\n template: string;\n};\n\n/** Common locations of the `next-intl` request config, relative to the root. */\nconst NEXT_INTL_REQUEST_FILES = [\n 'i18n/request.ts',\n 'i18n/request.tsx',\n 'i18n/request.js',\n 'i18n/request.mjs',\n 'src/i18n/request.ts',\n 'src/i18n/request.tsx',\n 'src/i18n/request.js',\n 'src/i18n/request.mjs',\n 'app/i18n/request.ts',\n 'src/app/i18n/request.ts',\n];\n\n/**\n * Derives the messages source template from a `next-intl` `i18n/request.ts`\n * file, which is the authoritative location of the messages path in a next-intl\n * project (e.g. `messages: (await import(\\`../messages/${locale}.json\\`)).default`).\n *\n * Reading it removes the ambiguity of globbing the file system and yields the\n * exact `source` template for `syncJSON`. When the resulting template has no\n * `${key}` segment (the common single-file-per-locale layout, where top-level\n * keys are namespaces), `syncJSON` `splitKeys` auto-detection turns each\n * top-level key into its own dictionary.\n *\n * Returns `null` when no request file is found, the messages import cannot be\n * parsed, or the path is not project-root-relative (e.g. uses a TS path alias).\n *\n * @param rootDir - Project root directory.\n */\nexport const detectNextIntlMessagesPattern = async (\n rootDir: string\n): Promise<NextIntlMessagesPattern | null> => {\n for (const requestFile of NEXT_INTL_REQUEST_FILES) {\n if (!(await exists(rootDir, requestFile))) continue;\n\n const content = await readFileFromRoot(rootDir, requestFile);\n\n // Capture the template-literal path of the messages dynamic import, e.g.\n // import(`../messages/${locale}.json`) → `../messages/${locale}.json`.\n const importMatch = content.match(\n /import\\(\\s*`([^`]*\\$\\{\\s*locale\\s*\\}[^`]*\\.json)`/\n );\n\n const importPath = importMatch?.[1];\n if (!importPath) continue;\n\n // Only relative imports can be resolved against the file system; path\n // aliases (e.g. `@/messages/...`) are skipped in favour of glob detection.\n if (!importPath.startsWith('.')) continue;\n\n // Resolve the import (relative to the request file) back to a\n // project-root-relative template. The `${locale}` / `${key}` placeholders\n // are kept literal — path utilities treat them as ordinary segments.\n const requestDir = dirname(resolve(rootDir, requestFile));\n const absoluteTemplate = resolve(requestDir, importPath);\n const relativeTemplate = relative(rootDir, absoluteTemplate)\n .split(sep)\n .join('/');\n\n const template = relativeTemplate.startsWith('.')\n ? relativeTemplate\n : `./${relativeTemplate}`;\n\n const type: JsonLocalePatternType =\n template.includes('${key}') || template.includes('${namespace}')\n ? 'nested'\n : 'flat';\n\n return { type, template };\n }\n\n return null;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"fileSystem.cjs","names":["ALL_LOCALES","EXCLUDED_PATHS","sep"],"sources":["../../../../src/init/utils/fileSystem.ts"],"sourcesContent":["import { access, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative, resolve, sep } from 'node:path';\nimport { EXCLUDED_PATHS } from '@intlayer/config/defaultValues';\nimport { ALL_LOCALES } from '@intlayer/types/allLocales';\nimport fg from 'fast-glob';\n\n/**\n * Helper to check if a file exists\n */\nexport const exists = async (rootDir: string, filePath: string) => {\n try {\n await access(join(rootDir, filePath));\n return true;\n } catch {\n return false;\n }\n};\n\n/**\n * Helper to read a file\n */\nexport const readFileFromRoot = async (rootDir: string, filePath: string) =>\n await readFile(join(rootDir, filePath), 'utf8');\n\n/**\n * Helper to write a file\n */\nexport const writeFileToRoot = async (\n rootDir: string,\n filePath: string,\n content: string\n) => await writeFile(join(rootDir, filePath), content, 'utf8');\n\n/**\n * Helper to ensure a directory exists\n */\nexport const ensureDirectory = async (rootDir: string, dirPath: string) => {\n try {\n await mkdir(join(rootDir, dirPath), { recursive: true });\n } catch {\n // Directory already exists or could not be created\n }\n};\n\n/**\n * Pattern type for locale JSON file organisation.\n * - 'nested': files are at `{base}/{locale}/{key}.json`\n * - 'flat': files are at `{base}/{locale}.json`\n */\nexport type JsonLocalePatternType = 'nested' | 'flat';\n\n/**\n * Detected locale JSON file pattern and the corresponding source template.\n * `template` uses `${locale}` and `${key}` as literal placeholders (not JS\n * expressions) so it can be embedded directly in a template-literal string.\n */\nexport type JsonLocalePattern = {\n type: JsonLocalePatternType;\n /**\n * Source path template for syncJSON `source` option.\n * Example nested: `./locales/${locale}/${key}.json`\n * Example flat: `./locales/${locale}.json`\n */\n template: string;\n /**\n * Detected locales in the directory.\n */\n locales: string[];\n};\n\n/**\n * Set of all known locale string values from the intlayer Locales registry\n * (e.g. `'en'`, `'fr'`, `'zh-TW'`). Keyed by the exact locale string so\n * `.has()` lookups are O(1) and common short directory names (`src`, `lib`,\n * `app`, …) are not mistaken for locales.\n */\nconst ALL_LOCALE_VALUES = new Set<string>(Object.values(ALL_LOCALES));\n\n/**\n * Returns true when `segment` matches a known BCP-47 locale identifier, e.g.\n * `en`, `fr`, `zh-TW`, `pt-BR`, `en-US`.\n */\nconst isLocaleSegment = (segment: string): boolean =>\n ALL_LOCALE_VALUES.has(segment);\n\n/** JSON filenames that are never locale translation files. */\nconst KNOWN_CONFIG_FILENAMES = new Set([\n 'package.json',\n 'tsconfig.json',\n 'jsconfig.json',\n 'biome.json',\n 'turbo.json',\n 'lerna.json',\n 'vercel.json',\n 'netlify.json',\n 'babel.config.json',\n 'jest.config.json',\n 'vitest.config.json',\n '.eslintrc.json',\n '.prettierrc.json',\n]);\n\n/**\n * Scans the project for JSON files and determines whether locale files are\n * organised as `{base}/{locale}/{key}.json` (nested) or `{base}/{locale}.json`\n * (flat). Returns the most likely source template, or `null` when no locale\n * JSON files are found.\n *\n * The returned `template` contains `${locale}` and `${key}` as **literal**\n * placeholder strings so it can be embedded inside a JS template literal.\n */\nexport const detectJsonLocalePattern = async (\n rootDir: string\n): Promise<JsonLocalePattern | null> => {\n const files = await fg('**/*.json', {\n cwd: rootDir,\n ignore: EXCLUDED_PATHS,\n absolute: false,\n onlyFiles: true,\n });\n\n const nestedBasePaths: string[] = [];\n const flatBasePaths: string[] = [];\n const nestedLocales = new Set<string>();\n const flatLocales = new Set<string>();\n\n for (const file of files) {\n const parts = file.split('/');\n const filename = parts[parts.length - 1] ?? '';\n\n if (KNOWN_CONFIG_FILENAMES.has(filename)) continue;\n\n // Nested: …/{locale}/{key}.json — parent directory is a locale code\n if (parts.length >= 3) {\n const localeDir = parts[parts.length - 2] ?? '';\n if (isLocaleSegment(localeDir)) {\n nestedBasePaths.push(parts.slice(0, -2).join('/') || '.');\n nestedLocales.add(localeDir);\n }\n }\n\n // Flat: …/{locale}.json — filename (without extension) is a locale code\n if (parts.length >= 2) {\n const baseName = filename.slice(0, -5); // strip \".json\"\n if (isLocaleSegment(baseName)) {\n flatBasePaths.push(parts.slice(0, -1).join('/') || '.');\n flatLocales.add(baseName);\n }\n }\n }\n\n if (nestedBasePaths.length === 0 && flatBasePaths.length === 0) {\n return null;\n }\n\n /**\n * Returns the path prefix that appears most frequently among the matches,\n * formatted as a relative path suitable for a source template.\n */\n const mostFrequentPrefix = (paths: string[]): string => {\n const counts = paths.reduce<Record<string, number>>((accumulator, path) => {\n accumulator[path] = (accumulator[path] ?? 0) + 1;\n return accumulator;\n }, {});\n const topEntry = Object.entries(counts).sort(([, a], [, b]) => b - a)[0];\n const basePath = topEntry?.[0] ?? '.';\n return basePath === '.' ? '.' : `./${basePath}`;\n };\n\n if (nestedBasePaths.length >= flatBasePaths.length) {\n const prefix = mostFrequentPrefix(nestedBasePaths);\n return {\n type: 'nested',\n // Literal ${locale} and ${key} — not evaluated here, used in template literals\n template: `${prefix}/\\${locale}/\\${key}.json`,\n locales: Array.from(nestedLocales),\n };\n }\n\n const prefix = mostFrequentPrefix(flatBasePaths);\n return {\n type: 'flat',\n template: `${prefix}/\\${locale}.json`,\n locales: Array.from(flatLocales),\n };\n};\n\n/**\n * Detected lingui catalog pattern. lingui stores one catalog file per locale\n * (default name `messages`), as `.po` (its default) or `.json`.\n */\nexport type LinguiCatalogPattern = {\n /** Which sync plugin should ingest the catalogs. */\n format: 'po' | 'json';\n /**\n * Source path template, with `${locale}` and `${key}` as literal\n * placeholders. The catalog filename (`messages`) is captured by `${key}` so\n * the produced intlayer dictionary key is `messages` — matching the fixed\n * `messages` namespace the lingui compat runtime reads from.\n * Example: `./src/locales/${locale}/${key}.po`.\n */\n template: string;\n /** Detected locales. */\n locales: string[];\n};\n\n/**\n * Scans the project for lingui catalog files (`{base}/{locale}/messages.po` or\n * `…/messages.json`, lingui's default layout) and derives the `syncPO` /\n * `syncJSON` `source` template.\n *\n * `.po` is preferred when present (lingui's default format). Returns `null`\n * when no lingui catalog is found, in which case the caller falls back to the\n * generic JSON detection.\n *\n * @param rootDir - Project root directory.\n */\nexport const detectLinguiCatalogPattern = async (\n rootDir: string\n): Promise<LinguiCatalogPattern | null> => {\n // lingui's default catalog name is `messages`; match that basename for both\n // formats so the captured `${key}` is `messages`.\n const files = await fg(['**/messages.po', '**/messages.json'], {\n cwd: rootDir,\n ignore: EXCLUDED_PATHS,\n absolute: false,\n onlyFiles: true,\n });\n\n const basePathsByFormat: Record<'po' | 'json', string[]> = {\n po: [],\n json: [],\n };\n const localesByFormat: Record<'po' | 'json', Set<string>> = {\n po: new Set(),\n json: new Set(),\n };\n\n for (const file of files) {\n const parts = file.split('/');\n // Nested layout: …/{locale}/messages.{po,json} — parent dir is the locale.\n if (parts.length < 2) continue;\n\n const localeDir = parts[parts.length - 2] ?? '';\n if (!isLocaleSegment(localeDir)) continue;\n\n const extension = (file.endsWith('.po') ? 'po' : 'json') as 'po' | 'json';\n basePathsByFormat[extension].push(parts.slice(0, -2).join('/') || '.');\n localesByFormat[extension].add(localeDir);\n }\n\n // Prefer `.po` (lingui's default) when catalogs of both formats coexist.\n const format: 'po' | 'json' = basePathsByFormat.po.length > 0 ? 'po' : 'json';\n\n const basePaths = basePathsByFormat[format];\n if (basePaths.length === 0) return null;\n\n const counts = basePaths.reduce<Record<string, number>>(\n (accumulator, path) => {\n accumulator[path] = (accumulator[path] ?? 0) + 1;\n return accumulator;\n },\n {}\n );\n const topBasePath =\n Object.entries(counts).sort(([, a], [, b]) => b - a)[0]?.[0] ?? '.';\n const prefix = topBasePath === '.' ? '.' : `./${topBasePath}`;\n\n return {\n format,\n template: `${prefix}/\\${locale}/\\${key}.${format}`,\n locales: Array.from(localesByFormat[format]),\n };\n};\n\n/**\n * The messages source template derived from a `next-intl` `i18n/request.ts`\n * file, ready to be used as a `syncJSON` `source` builder.\n */\nexport type NextIntlMessagesPattern = {\n /** `'flat'` (one file per locale) or `'nested'` (per-namespace files). */\n type: JsonLocalePatternType;\n /**\n * Source path template relative to the project root, with `${locale}` (and\n * `${key}` when nested) as literal placeholders, e.g. `./messages/${locale}.json`.\n */\n template: string;\n};\n\n/** Common locations of the `next-intl` request config, relative to the root. */\nconst NEXT_INTL_REQUEST_FILES = [\n 'i18n/request.ts',\n 'i18n/request.tsx',\n 'i18n/request.js',\n 'i18n/request.mjs',\n 'src/i18n/request.ts',\n 'src/i18n/request.tsx',\n 'src/i18n/request.js',\n 'src/i18n/request.mjs',\n 'app/i18n/request.ts',\n 'src/app/i18n/request.ts',\n];\n\n/**\n * Derives the messages source template from a `next-intl` `i18n/request.ts`\n * file, which is the authoritative location of the messages path in a next-intl\n * project (e.g. `messages: (await import(\\`../messages/${locale}.json\\`)).default`).\n *\n * Reading it removes the ambiguity of globbing the file system and yields the\n * exact `source` template for `syncJSON`. When the resulting template has no\n * `${key}` segment (the common single-file-per-locale layout, where top-level\n * keys are namespaces), `syncJSON` `splitKeys` auto-detection turns each\n * top-level key into its own dictionary.\n *\n * Returns `null` when no request file is found, the messages import cannot be\n * parsed, or the path is not project-root-relative (e.g. uses a TS path alias).\n *\n * @param rootDir - Project root directory.\n */\nexport const detectNextIntlMessagesPattern = async (\n rootDir: string\n): Promise<NextIntlMessagesPattern | null> => {\n for (const requestFile of NEXT_INTL_REQUEST_FILES) {\n if (!(await exists(rootDir, requestFile))) continue;\n\n const content = await readFileFromRoot(rootDir, requestFile);\n\n // Capture the template-literal path of the messages dynamic import, e.g.\n // import(`../messages/${locale}.json`) → `../messages/${locale}.json`.\n const importMatch = content.match(\n /import\\(\\s*`([^`]*\\$\\{\\s*locale\\s*\\}[^`]*\\.json)`/\n );\n\n const importPath = importMatch?.[1];\n if (!importPath) continue;\n\n // Only relative imports can be resolved against the file system; path\n // aliases (e.g. `@/messages/...`) are skipped in favour of glob detection.\n if (!importPath.startsWith('.')) continue;\n\n // Resolve the import (relative to the request file) back to a\n // project-root-relative template. The `${locale}` / `${key}` placeholders\n // are kept literal — path utilities treat them as ordinary segments.\n const requestDir = dirname(resolve(rootDir, requestFile));\n const absoluteTemplate = resolve(requestDir, importPath);\n const relativeTemplate = relative(rootDir, absoluteTemplate)\n .split(sep)\n .join('/');\n\n const template = relativeTemplate.startsWith('.')\n ? relativeTemplate\n : `./${relativeTemplate}`;\n\n const type: JsonLocalePatternType =\n template.includes('${key}') || template.includes('${namespace}')\n ? 'nested'\n : 'flat';\n\n return { type, template };\n }\n\n return null;\n};\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_init_utils_astImports = require('./astImports.cjs');
2
3
  const require_init_utils_configManipulation = require('./configManipulation.cjs');
3
4
  const require_init_utils_fileSystem = require('./fileSystem.cjs');
4
5
  const require_init_utils_githubActions = require('./githubActions.cjs');
@@ -11,19 +12,23 @@ exports.GITHUB_FILL_WORKFLOW_PATH = require_init_utils_githubActions.GITHUB_FILL
11
12
  exports.GITHUB_TEST_WORKFLOW_PATH = require_init_utils_githubActions.GITHUB_TEST_WORKFLOW_PATH;
12
13
  exports.NEXT_INTLAYER_BABEL_CONFIG_CONTENT = require_init_utils_nextCompilerBabel.NEXT_INTLAYER_BABEL_CONFIG_CONTENT;
13
14
  exports.detectJsonLocalePattern = require_init_utils_fileSystem.detectJsonLocalePattern;
15
+ exports.detectLinguiCatalogPattern = require_init_utils_fileSystem.detectLinguiCatalogPattern;
14
16
  exports.detectMissingIntlayerPackages = require_init_utils_packageManager.detectMissingIntlayerPackages;
15
17
  exports.detectNextIntlMessagesPattern = require_init_utils_fileSystem.detectNextIntlMessagesPattern;
16
18
  exports.detectOutdatedIntlayerPackages = require_init_utils_packageManager.detectOutdatedIntlayerPackages;
17
19
  exports.detectPackageManager = require_init_utils_packageManager.detectPackageManager;
18
20
  exports.enableIntlayerEditorConfig = require_init_utils_configManipulation.enableIntlayerEditorConfig;
19
21
  exports.ensureDirectory = require_init_utils_fileSystem.ensureDirectory;
22
+ exports.ensureNamedImport = require_init_utils_astImports.ensureNamedImport;
20
23
  exports.exists = require_init_utils_fileSystem.exists;
21
24
  exports.findTsConfigFiles = require_init_utils_tsConfig.findTsConfigFiles;
25
+ exports.firstInsertIndex = require_init_utils_astImports.firstInsertIndex;
22
26
  exports.getGithubWorkflows = require_init_utils_githubActions.getGithubWorkflows;
23
27
  exports.getInstalledPackageVersion = require_init_utils_packageManager.getInstalledPackageVersion;
24
28
  exports.getMetroConfigTemplate = require_init_utils_configManipulation.getMetroConfigTemplate;
25
29
  exports.installPackages = require_init_utils_packageManager.installPackages;
26
30
  exports.isIntlayerPackageName = require_init_utils_packageManager.isIntlayerPackageName;
31
+ exports.isModuleScopeBinding = require_init_utils_astImports.isModuleScopeBinding;
27
32
  exports.normalizeVersion = require_init_utils_packageManager.normalizeVersion;
28
33
  exports.parseJSONWithComments = require_init_utils_jsonParser.parseJSONWithComments;
29
34
  exports.readFileFromRoot = require_init_utils_fileSystem.readFileFromRoot;
@@ -28,12 +28,7 @@ const buildInstallCommand = (packageManager, packages, isDev = false) => {
28
28
  case "npm": return `npm install ${isDev ? "-D " : ""}${packageList}`;
29
29
  }
30
30
  };
31
- /**
32
- * Analyzes existing project dependencies to determine which intlayer packages
33
- * are missing and what syncJSON configuration to inject when compat i18n
34
- * libraries are present.
35
- */
36
- const detectMissingIntlayerPackages = (allDependencies) => {
31
+ const detectMissingIntlayerPackages = (allDependencies, options = {}) => {
37
32
  const packagesToInstall = [];
38
33
  const devPackagesToInstall = [];
39
34
  let compatSyncConfig;
@@ -183,6 +178,16 @@ const detectMissingIntlayerPackages = (allDependencies) => {
183
178
  if (isInstalled("@lingui/core") || isInstalled("@lingui/react") || isInstalled("@intlayer/lingui")) {
184
179
  addIfMissing("@intlayer/lingui");
185
180
  addIfMissing("@lingui/core");
181
+ const linguiUsesPo = options.linguiCatalogFormat === "po";
182
+ compatSyncConfig ??= linguiUsesPo ? {
183
+ plugin: "po",
184
+ format: "icu",
185
+ sourceTemplate: "./src/locales/${locale}/${key}.po"
186
+ } : {
187
+ plugin: "json",
188
+ format: "icu",
189
+ sourceTemplate: "./src/locales/${locale}/${key}.json"
190
+ };
186
191
  compatVitePluginConfig ??= {
187
192
  pluginFunctionName: "lingui",
188
193
  pluginPackageSource: "@intlayer/lingui/plugin",
@@ -200,7 +205,7 @@ const detectMissingIntlayerPackages = (allDependencies) => {
200
205
  pluginPackageSource: "@intlayer/i18n-js/plugin"
201
206
  };
202
207
  }
203
- if (compatSyncConfig) addDevIfMissing("@intlayer/sync-json-plugin");
208
+ if (compatSyncConfig) addDevIfMissing(compatSyncConfig.plugin === "po" ? "@intlayer/sync-po-plugin" : "@intlayer/sync-json-plugin");
204
209
  return {
205
210
  packagesToInstall,
206
211
  devPackagesToInstall,
@@ -1 +1 @@
1
- {"version":3,"file":"packageManager.cjs","names":[],"sources":["../../../../src/init/utils/packageManager.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { compareVersions } from '@intlayer/config/utils';\n\n/** Package managers supported for dependency installation. */\nexport type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Configuration for the syncJSON plugin injected into intlayer.config\n * when a compat i18n library is detected.\n */\nexport type CompatSyncConfig = {\n /** JSON format matching the compat library's conventions. */\n format: 'icu' | 'i18next' | 'vue-i18n';\n /**\n * Source path template using ${locale} and ${key} placeholders.\n * Rendered as a template literal in the generated config.\n */\n sourceTemplate: string;\n /**\n * Force `splitKeys: true` in the generated `syncJSON(...)` call so each\n * top-level key of a single per-locale file becomes its own dictionary.\n *\n * Set for libraries whose single `messages/${locale}.json` file groups\n * namespaces by its first-level keys (`next-intl` / `use-intl`, where\n * `useTranslations('Hero')` resolves to the `Hero` dictionary). Left\n * undefined for libraries whose top-level keys are plain message keys\n * (e.g. `i18next`, `react-intl`); for those, syncJSON's auto-detection\n * (split only when the source has no `${key}` segment) stays in control.\n *\n * Only meaningful for flat templates (no `${key}` segment); it is dropped\n * automatically when the resolved template addresses one namespace per file.\n */\n splitKeys?: boolean;\n};\n\n/**\n * Configuration for injecting a compat vite plugin into vite.config.\n * The plugin replaces the generic `intlayer` plugin for libraries that\n * require alias injection (e.g. `vue-i18n` → `@intlayer/vue-i18n`).\n */\nexport type CompatVitePluginConfig = {\n /** Exported function name from the plugin package, e.g. `'vueI18nVitePlugin'`. */\n pluginFunctionName: string;\n /** Import path for the plugin package, e.g. `'@intlayer/vue-i18n/plugin'`. */\n pluginPackageSource: string;\n /**\n * Set when the compat plugin is a drop-in replacement for an i18n library's\n * own Vite plugin (e.g. lingui ships `@lingui/vite-plugin`). When the original\n * import is present, init rewrites only that import's module source to\n * `pluginPackageSource` — keeping the binding and its call site — instead of\n * injecting a second import and appending another plugin to the array.\n */\n replacesVitePlugin?: {\n /** Imported binding to keep, e.g. `'lingui'`. */\n importName: string;\n /** Original package source to rewrite, e.g. `'@lingui/vite-plugin'`. */\n fromPackageSource: string;\n };\n};\n\n/** Result of analyzing project dependencies for intlayer package gaps. */\nexport type IntlayerPackageAnalysis = {\n /** Intlayer packages that are referenced but not yet installed. */\n packagesToInstall: string[];\n /** Intlayer dev packages that are referenced but not yet installed. */\n devPackagesToInstall: string[];\n /**\n * syncJSON plugin configuration to inject when a compat i18n library is\n * detected. Undefined when no compat library is present or format is not\n * yet implemented.\n */\n compatSyncConfig: CompatSyncConfig | undefined;\n /**\n * Vite config plugin to inject when a vite-based compat library is\n * detected. Undefined for Next.js/Nuxt-only compat libs or when no compat\n * library requires alias injection.\n */\n compatVitePluginConfig: CompatVitePluginConfig | undefined;\n};\n\n/**\n * Detects the package manager in use by checking for lock files in the\n * project root. Falls back to npm when no lock file is found.\n */\nexport const detectPackageManager = (rootDir: string): PackageManager => {\n if (\n existsSync(join(rootDir, 'bun.lock')) ||\n existsSync(join(rootDir, 'bun.lockb'))\n ) {\n return 'bun';\n }\n if (existsSync(join(rootDir, 'pnpm-lock.yaml'))) {\n return 'pnpm';\n }\n if (existsSync(join(rootDir, 'yarn.lock'))) {\n return 'yarn';\n }\n return 'npm';\n};\n\n/**\n * Returns the install command for the given package manager and package list.\n */\nconst buildInstallCommand = (\n packageManager: PackageManager,\n packages: string[],\n isDev: boolean = false\n): string => {\n const packageList = packages.join(' ');\n switch (packageManager) {\n case 'bun':\n return `bun add ${isDev ? '-d ' : ''}${packageList}`;\n case 'pnpm':\n return `pnpm add ${isDev ? '-D ' : ''}${packageList}`;\n case 'yarn':\n return `yarn add ${isDev ? '-D ' : ''}${packageList}`;\n case 'npm':\n return `npm install ${isDev ? '-D ' : ''}${packageList}`;\n }\n};\n\n/**\n * Analyzes existing project dependencies to determine which intlayer packages\n * are missing and what syncJSON configuration to inject when compat i18n\n * libraries are present.\n */\nexport const detectMissingIntlayerPackages = (\n allDependencies: Record<string, string>\n): IntlayerPackageAnalysis => {\n const packagesToInstall: string[] = [];\n const devPackagesToInstall: string[] = [];\n let compatSyncConfig: CompatSyncConfig | undefined;\n let compatVitePluginConfig: CompatVitePluginConfig | undefined;\n\n const isInstalled = (packageName: string): boolean =>\n Boolean(allDependencies[packageName]);\n\n const addIfMissing = (packageName: string): void => {\n if (!isInstalled(packageName)) {\n packagesToInstall.push(packageName);\n }\n };\n\n const addDevIfMissing = (packageName: string): void => {\n if (!isInstalled(packageName)) {\n devPackagesToInstall.push(packageName);\n }\n };\n\n // Core package — always required\n addIfMissing('intlayer');\n\n // Framework-specific runtime integrations\n if (isInstalled('next')) {\n addIfMissing('next-intlayer');\n } else if (isInstalled('react')) {\n addIfMissing('react-intlayer');\n }\n\n // React Native / Expo — the Metro plugin lives in react-native-intlayer,\n // installed as a dev dependency (it is only used by the bundler).\n if (isInstalled('react-native') || isInstalled('expo')) {\n addDevIfMissing('react-native-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 // -------------------------------------------------------------------------\n // Compat adapters for existing i18n libraries.\n //\n // Detection order matters: more specific libraries are checked first so that\n // `compatSyncConfig ??=` and `compatVitePluginConfig ??=` capture the most\n // relevant match. Libraries that only affect the Next.js or Nuxt config do\n // not set `compatVitePluginConfig` (handled separately in init/index.ts).\n // Libraries whose JSON format is not yet supported leave `compatSyncConfig`\n // undefined so no syncJSON plugin is injected.\n // -------------------------------------------------------------------------\n\n // next-intl — next.js only, ICU format. Default layout is a single file per\n // locale (`messages/${locale}.json`) whose top-level keys are namespaces;\n // syncJSON `splitKeys` auto-detection (no `${key}` segment) turns each\n // top-level key into its own dictionary. The exact path is refined from\n // `i18n/request.ts` in init/index.ts when present.\n if (isInstalled('next-intl') || isInstalled('@intlayer/next-intl')) {\n addIfMissing('@intlayer/next-intl');\n addIfMissing('next-intl');\n compatSyncConfig ??= {\n format: 'icu',\n sourceTemplate: './messages/${locale}.json',\n // next-intl groups namespaces by the first-level keys of one file.\n splitKeys: true,\n };\n // next config handled via updateNextConfigForNextIntl in init/index.ts\n }\n\n // next-i18next — next.js only, i18next JSON format\n if (isInstalled('next-i18next') || isInstalled('@intlayer/next-i18next')) {\n addIfMissing('@intlayer/next-i18next');\n addIfMissing('next-i18next');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}/${key}.json',\n };\n // next config handled via updateNextConfigForNextI18next in init/index.ts\n }\n\n // next-translate — next.js only, i18next-style flat-namespace JSON\n if (\n isInstalled('next-translate') ||\n isInstalled('@intlayer/next-translate')\n ) {\n addIfMissing('@intlayer/next-translate');\n addIfMissing('next-translate');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './locales/${locale}/${key}.json',\n };\n // next config handled via updateNextConfigForNextTranslate in init/index.ts\n }\n\n // i18next — explicit import from @intlayer/i18next (no alias injection needed)\n if (isInstalled('i18next') || isInstalled('@intlayer/i18next')) {\n addIfMissing('@intlayer/i18next');\n // Ensure the required peer dependency is installed\n addIfMissing('i18next');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}/${key}.json',\n };\n }\n\n // react-i18next — explicit import from @intlayer/react-i18next (no alias)\n if (isInstalled('react-i18next') || isInstalled('@intlayer/react-i18next')) {\n addIfMissing('@intlayer/react-i18next');\n // Ensure the required peer dependency is installed\n addIfMissing('react-i18next');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}/${key}.json',\n };\n }\n\n // vue-i18n — vite alias injection required\n if (isInstalled('vue-i18n') || isInstalled('@intlayer/vue-i18n')) {\n addIfMissing('@intlayer/vue-i18n');\n addIfMissing('vue-i18n');\n compatSyncConfig ??= {\n format: 'vue-i18n',\n sourceTemplate: './locales/${locale}/${key}.json',\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'vueI18nVitePlugin',\n pluginPackageSource: '@intlayer/vue-i18n/plugin',\n };\n }\n\n // use-intl — framework-agnostic React core of next-intl; vite alias\n // injection required, ICU format. Commonly a single `messages/${locale}.json`\n // file whose top-level keys are namespaces, handled by syncJSON `splitKeys`\n // auto-detection (no `${key}` segment in the source template).\n if (isInstalled('use-intl') || isInstalled('@intlayer/use-intl')) {\n addIfMissing('@intlayer/use-intl');\n addIfMissing('use-intl');\n compatSyncConfig ??= {\n format: 'icu',\n sourceTemplate: './messages/${locale}.json',\n // use-intl (the core of next-intl) uses the same single-file namespace model.\n splitKeys: true,\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'useIntlVitePlugin',\n pluginPackageSource: '@intlayer/use-intl/plugin',\n };\n }\n\n // react-intl — vite alias injection required, ICU format\n if (isInstalled('react-intl') || isInstalled('@intlayer/react-intl')) {\n addIfMissing('@intlayer/react-intl');\n addIfMissing('react-intl');\n compatSyncConfig ??= {\n format: 'icu',\n sourceTemplate: './src/i18n/${locale}.json',\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'reactIntlVitePlugin',\n pluginPackageSource: '@intlayer/react-intl/plugin',\n };\n }\n\n // @ngneat/transloco — vite alias injection required\n // @todo syncJSON format not yet implemented for transloco\n if (isInstalled('@ngneat/transloco') || isInstalled('@intlayer/transloco')) {\n addIfMissing('@intlayer/transloco');\n addIfMissing('@ngneat/transloco');\n compatVitePluginConfig ??= {\n pluginFunctionName: 'translocoVitePlugin',\n pluginPackageSource: '@intlayer/transloco/plugin',\n };\n }\n\n // svelte-i18n — vite alias injection required, flat JSON (i18next-compatible)\n if (isInstalled('svelte-i18n') || isInstalled('@intlayer/svelte-i18n')) {\n addIfMissing('@intlayer/svelte-i18n');\n addIfMissing('svelte-i18n');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}.json',\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'svelteI18nVitePlugin',\n pluginPackageSource: '@intlayer/svelte-i18n/plugin',\n };\n }\n\n // node-polyglot — vite alias injection required\n // @todo syncJSON format not yet implemented for polyglot\n if (isInstalled('node-polyglot') || isInstalled('@intlayer/polyglot')) {\n addIfMissing('@intlayer/polyglot');\n addIfMissing('node-polyglot');\n compatVitePluginConfig ??= {\n pluginFunctionName: 'polyglotVitePlugin',\n pluginPackageSource: '@intlayer/polyglot/plugin',\n };\n }\n\n // @nuxtjs/i18n — nuxt module (no vite plugin), vue-i18n JSON format\n if (isInstalled('@nuxtjs/i18n') || isInstalled('@intlayer/nuxtjs-i18n')) {\n addIfMissing('@intlayer/nuxtjs-i18n');\n addIfMissing('@nuxtjs/i18n');\n compatSyncConfig ??= {\n format: 'vue-i18n',\n sourceTemplate: './locales/${locale}/${key}.json',\n };\n // nuxt config handled via updateNuxtConfigForNuxtjsI18n in init/index.ts\n }\n\n // @ngx-translate/core — vite alias injection required, flat JSON (i18next)\n if (\n isInstalled('@ngx-translate/core') ||\n isInstalled('@intlayer/ngx-translate')\n ) {\n addIfMissing('@intlayer/ngx-translate');\n addIfMissing('@ngx-translate/core');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './assets/i18n/${locale}.json',\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'ngxTranslateVitePlugin',\n pluginPackageSource: '@intlayer/ngx-translate/plugin',\n };\n }\n\n // @lingui/core — vite alias injection required\n // @todo syncJSON format not yet implemented for lingui (uses PO files)\n if (\n isInstalled('@lingui/core') ||\n isInstalled('@lingui/react') ||\n isInstalled('@intlayer/lingui')\n ) {\n addIfMissing('@intlayer/lingui');\n addIfMissing('@lingui/core');\n compatVitePluginConfig ??= {\n // `@intlayer/lingui/plugin` exports `lingui` as a drop-in replacement for\n // `@lingui/vite-plugin`, so a fresh project gets `lingui()` injected and a\n // project already using `@lingui/vite-plugin` only has its import source\n // rewritten (see `replacesVitePlugin`).\n pluginFunctionName: 'lingui',\n pluginPackageSource: '@intlayer/lingui/plugin',\n replacesVitePlugin: {\n importName: 'lingui',\n fromPackageSource: '@lingui/vite-plugin',\n },\n };\n }\n\n // i18n-js — vite alias injection required\n // @todo syncJSON format not yet implemented for i18n-js\n if (isInstalled('i18n-js') || isInstalled('@intlayer/i18n-js')) {\n addIfMissing('@intlayer/i18n-js');\n addIfMissing('i18n-js');\n compatVitePluginConfig ??= {\n pluginFunctionName: 'i18nJsVitePlugin',\n pluginPackageSource: '@intlayer/i18n-js/plugin',\n };\n }\n\n if (compatSyncConfig) {\n addDevIfMissing('@intlayer/sync-json-plugin');\n }\n\n return {\n packagesToInstall,\n devPackagesToInstall,\n compatSyncConfig,\n compatVitePluginConfig,\n };\n};\n\n/**\n * Runs the package install command synchronously.\n * Throws if the install process exits with a non-zero code.\n */\nexport const installPackages = (\n rootDir: string,\n packages: string[],\n packageManager: PackageManager,\n isDev: boolean = false\n): void => {\n const command = buildInstallCommand(packageManager, packages, isDev);\n execSync(command, { cwd: rootDir, stdio: 'inherit' });\n};\n\n/**\n * Determines whether a dependency name belongs to the Intlayer ecosystem.\n *\n * Matches the core `intlayer` package, every scoped `@intlayer/*` package\n * (including compat adapters such as `@intlayer/next-intl`) and the framework\n * runtime integrations that follow the `<framework>-intlayer` convention\n * (e.g. `next-intlayer`, `react-intlayer`, `express-intlayer`).\n */\nexport const isIntlayerPackageName = (packageName: string): boolean =>\n packageName === 'intlayer' ||\n packageName.startsWith('@intlayer/') ||\n /-intlayer$/.test(packageName);\n\n/**\n * Reduces a semver range or full version to its `major.minor.patch` core,\n * stripping range prefixes (`^`, `~`), pre-release identifiers and build\n * metadata. Returns `null` when no `major.minor.patch` can be extracted.\n *\n * @example normalizeVersion('^9.0.0-canary.3') // '9.0.0'\n */\nexport const normalizeVersion = (version?: string): string | null => {\n if (!version || typeof version !== 'string') return null;\n const match = version.match(/(\\d+)\\.(\\d+)\\.(\\d+)/);\n return match ? `${match[1]}.${match[2]}.${match[3]}` : null;\n};\n\n/**\n * Reads the installed version of a package from its `package.json` inside the\n * project's `node_modules`. Returns `null` when the package is not installed or\n * its manifest cannot be read.\n */\nexport const getInstalledPackageVersion = (\n rootDir: string,\n packageName: string\n): string | null => {\n try {\n const manifestPath = join(\n rootDir,\n 'node_modules',\n packageName,\n 'package.json'\n );\n if (!existsSync(manifestPath)) return null;\n const { version } = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n return typeof version === 'string' ? version : null;\n } catch {\n return null;\n }\n};\n\n/**\n * Returns the Intlayer packages from `dependencies` whose installed version is\n * behind `targetVersion` (compared on `major.minor.patch`). Packages that are\n * not installed yet are ignored — those are handled by\n * {@link detectMissingIntlayerPackages}.\n */\nexport const detectOutdatedIntlayerPackages = (\n rootDir: string,\n dependencies: Record<string, string>,\n targetVersion: string\n): string[] => {\n const normalizedTarget = normalizeVersion(targetVersion);\n if (!normalizedTarget) return [];\n\n return Object.keys(dependencies)\n .filter(isIntlayerPackageName)\n .filter((packageName) => {\n const installedVersion = getInstalledPackageVersion(rootDir, packageName);\n const normalizedInstalled = normalizeVersion(\n installedVersion ?? undefined\n );\n if (!normalizedInstalled) return false;\n return compareVersions(normalizedInstalled, '<', normalizedTarget);\n });\n};\n\n/**\n * Upgrades the given packages to `targetVersion` synchronously, preserving the\n * dependency type via the `isDev` flag. Throws if the install process exits\n * with a non-zero code.\n */\nexport const upgradePackages = (\n rootDir: string,\n packages: string[],\n packageManager: PackageManager,\n targetVersion: string,\n isDev: boolean = false\n): void => {\n if (packages.length === 0) return;\n const versionedPackages = packages.map(\n (packageName) => `${packageName}@${targetVersion}`\n );\n const command = buildInstallCommand(packageManager, versionedPackages, isDev);\n execSync(command, { cwd: rootDir, stdio: 'inherit' });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"packageManager.cjs","names":[],"sources":["../../../../src/init/utils/packageManager.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { compareVersions } from '@intlayer/config/utils';\n\n/** Package managers supported for dependency installation. */\nexport type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Configuration for the syncJSON plugin injected into intlayer.config\n * when a compat i18n library is detected.\n */\nexport type CompatSyncConfig = {\n /**\n * Which sync plugin ingests the catalogs:\n * - `'json'` → `syncJSON` from `@intlayer/sync-json-plugin` (default).\n * - `'po'` → `syncPO` from `@intlayer/sync-po-plugin` (lingui's default\n * format).\n */\n plugin?: 'json' | 'po';\n /**\n * JSON format matching the compat library's conventions. Ignored when\n * `plugin` is `'po'` (PO catalogs are always serialized as gettext).\n */\n format: 'icu' | 'i18next' | 'vue-i18n';\n /**\n * Source path template using ${locale} and ${key} placeholders.\n * Rendered as a template literal in the generated config.\n */\n sourceTemplate: string;\n /**\n * Force `splitKeys: true` in the generated `syncJSON(...)` call so each\n * top-level key of a single per-locale file becomes its own dictionary.\n *\n * Set for libraries whose single `messages/${locale}.json` file groups\n * namespaces by its first-level keys (`next-intl` / `use-intl`, where\n * `useTranslations('Hero')` resolves to the `Hero` dictionary). Left\n * undefined for libraries whose top-level keys are plain message keys\n * (e.g. `i18next`, `react-intl`); for those, syncJSON's auto-detection\n * (split only when the source has no `${key}` segment) stays in control.\n *\n * Only meaningful for flat templates (no `${key}` segment); it is dropped\n * automatically when the resolved template addresses one namespace per file.\n */\n splitKeys?: boolean;\n};\n\n/**\n * Configuration for injecting a compat vite plugin into vite.config.\n * The plugin replaces the generic `intlayer` plugin for libraries that\n * require alias injection (e.g. `vue-i18n` → `@intlayer/vue-i18n`).\n */\nexport type CompatVitePluginConfig = {\n /** Exported function name from the plugin package, e.g. `'vueI18nVitePlugin'`. */\n pluginFunctionName: string;\n /** Import path for the plugin package, e.g. `'@intlayer/vue-i18n/plugin'`. */\n pluginPackageSource: string;\n /**\n * Set when the compat plugin is a drop-in replacement for an i18n library's\n * own Vite plugin (e.g. lingui ships `@lingui/vite-plugin`). When the original\n * import is present, init rewrites only that import's module source to\n * `pluginPackageSource` — keeping the binding and its call site — instead of\n * injecting a second import and appending another plugin to the array.\n */\n replacesVitePlugin?: {\n /** Imported binding to keep, e.g. `'lingui'`. */\n importName: string;\n /** Original package source to rewrite, e.g. `'@lingui/vite-plugin'`. */\n fromPackageSource: string;\n };\n};\n\n/** Result of analyzing project dependencies for intlayer package gaps. */\nexport type IntlayerPackageAnalysis = {\n /** Intlayer packages that are referenced but not yet installed. */\n packagesToInstall: string[];\n /** Intlayer dev packages that are referenced but not yet installed. */\n devPackagesToInstall: string[];\n /**\n * syncJSON plugin configuration to inject when a compat i18n library is\n * detected. Undefined when no compat library is present or format is not\n * yet implemented.\n */\n compatSyncConfig: CompatSyncConfig | undefined;\n /**\n * Vite config plugin to inject when a vite-based compat library is\n * detected. Undefined for Next.js/Nuxt-only compat libs or when no compat\n * library requires alias injection.\n */\n compatVitePluginConfig: CompatVitePluginConfig | undefined;\n};\n\n/**\n * Detects the package manager in use by checking for lock files in the\n * project root. Falls back to npm when no lock file is found.\n */\nexport const detectPackageManager = (rootDir: string): PackageManager => {\n if (\n existsSync(join(rootDir, 'bun.lock')) ||\n existsSync(join(rootDir, 'bun.lockb'))\n ) {\n return 'bun';\n }\n if (existsSync(join(rootDir, 'pnpm-lock.yaml'))) {\n return 'pnpm';\n }\n if (existsSync(join(rootDir, 'yarn.lock'))) {\n return 'yarn';\n }\n return 'npm';\n};\n\n/**\n * Returns the install command for the given package manager and package list.\n */\nconst buildInstallCommand = (\n packageManager: PackageManager,\n packages: string[],\n isDev: boolean = false\n): string => {\n const packageList = packages.join(' ');\n switch (packageManager) {\n case 'bun':\n return `bun add ${isDev ? '-d ' : ''}${packageList}`;\n case 'pnpm':\n return `pnpm add ${isDev ? '-D ' : ''}${packageList}`;\n case 'yarn':\n return `yarn add ${isDev ? '-D ' : ''}${packageList}`;\n case 'npm':\n return `npm install ${isDev ? '-D ' : ''}${packageList}`;\n }\n};\n\n/**\n * Analyzes existing project dependencies to determine which intlayer packages\n * are missing and what syncJSON configuration to inject when compat i18n\n * libraries are present.\n */\n/** Extra signals (from a filesystem scan) that refine compat detection. */\nexport type DetectMissingPackagesOptions = {\n /**\n * Catalog format detected for a lingui project (`'po'` or `'json'`), or\n * `null`/undefined when none was found. Decides which sync plugin + dev\n * dependency the lingui compat setup uses.\n */\n linguiCatalogFormat?: 'po' | 'json' | null;\n};\n\nexport const detectMissingIntlayerPackages = (\n allDependencies: Record<string, string>,\n options: DetectMissingPackagesOptions = {}\n): IntlayerPackageAnalysis => {\n const packagesToInstall: string[] = [];\n const devPackagesToInstall: string[] = [];\n let compatSyncConfig: CompatSyncConfig | undefined;\n let compatVitePluginConfig: CompatVitePluginConfig | undefined;\n\n const isInstalled = (packageName: string): boolean =>\n Boolean(allDependencies[packageName]);\n\n const addIfMissing = (packageName: string): void => {\n if (!isInstalled(packageName)) {\n packagesToInstall.push(packageName);\n }\n };\n\n const addDevIfMissing = (packageName: string): void => {\n if (!isInstalled(packageName)) {\n devPackagesToInstall.push(packageName);\n }\n };\n\n // Core package — always required\n addIfMissing('intlayer');\n\n // Framework-specific runtime integrations\n if (isInstalled('next')) {\n addIfMissing('next-intlayer');\n } else if (isInstalled('react')) {\n addIfMissing('react-intlayer');\n }\n\n // React Native / Expo — the Metro plugin lives in react-native-intlayer,\n // installed as a dev dependency (it is only used by the bundler).\n if (isInstalled('react-native') || isInstalled('expo')) {\n addDevIfMissing('react-native-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 // -------------------------------------------------------------------------\n // Compat adapters for existing i18n libraries.\n //\n // Detection order matters: more specific libraries are checked first so that\n // `compatSyncConfig ??=` and `compatVitePluginConfig ??=` capture the most\n // relevant match. Libraries that only affect the Next.js or Nuxt config do\n // not set `compatVitePluginConfig` (handled separately in init/index.ts).\n // Libraries whose JSON format is not yet supported leave `compatSyncConfig`\n // undefined so no syncJSON plugin is injected.\n // -------------------------------------------------------------------------\n\n // next-intl — next.js only, ICU format. Default layout is a single file per\n // locale (`messages/${locale}.json`) whose top-level keys are namespaces;\n // syncJSON `splitKeys` auto-detection (no `${key}` segment) turns each\n // top-level key into its own dictionary. The exact path is refined from\n // `i18n/request.ts` in init/index.ts when present.\n if (isInstalled('next-intl') || isInstalled('@intlayer/next-intl')) {\n addIfMissing('@intlayer/next-intl');\n addIfMissing('next-intl');\n compatSyncConfig ??= {\n format: 'icu',\n sourceTemplate: './messages/${locale}.json',\n // next-intl groups namespaces by the first-level keys of one file.\n splitKeys: true,\n };\n // next config handled via updateNextConfigForNextIntl in init/index.ts\n }\n\n // next-i18next — next.js only, i18next JSON format\n if (isInstalled('next-i18next') || isInstalled('@intlayer/next-i18next')) {\n addIfMissing('@intlayer/next-i18next');\n addIfMissing('next-i18next');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}/${key}.json',\n };\n // next config handled via updateNextConfigForNextI18next in init/index.ts\n }\n\n // next-translate — next.js only, i18next-style flat-namespace JSON\n if (\n isInstalled('next-translate') ||\n isInstalled('@intlayer/next-translate')\n ) {\n addIfMissing('@intlayer/next-translate');\n addIfMissing('next-translate');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './locales/${locale}/${key}.json',\n };\n // next config handled via updateNextConfigForNextTranslate in init/index.ts\n }\n\n // i18next — explicit import from @intlayer/i18next (no alias injection needed)\n if (isInstalled('i18next') || isInstalled('@intlayer/i18next')) {\n addIfMissing('@intlayer/i18next');\n // Ensure the required peer dependency is installed\n addIfMissing('i18next');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}/${key}.json',\n };\n }\n\n // react-i18next — explicit import from @intlayer/react-i18next (no alias)\n if (isInstalled('react-i18next') || isInstalled('@intlayer/react-i18next')) {\n addIfMissing('@intlayer/react-i18next');\n // Ensure the required peer dependency is installed\n addIfMissing('react-i18next');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}/${key}.json',\n };\n }\n\n // vue-i18n — vite alias injection required\n if (isInstalled('vue-i18n') || isInstalled('@intlayer/vue-i18n')) {\n addIfMissing('@intlayer/vue-i18n');\n addIfMissing('vue-i18n');\n compatSyncConfig ??= {\n format: 'vue-i18n',\n sourceTemplate: './locales/${locale}/${key}.json',\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'vueI18nVitePlugin',\n pluginPackageSource: '@intlayer/vue-i18n/plugin',\n };\n }\n\n // use-intl — framework-agnostic React core of next-intl; vite alias\n // injection required, ICU format. Commonly a single `messages/${locale}.json`\n // file whose top-level keys are namespaces, handled by syncJSON `splitKeys`\n // auto-detection (no `${key}` segment in the source template).\n if (isInstalled('use-intl') || isInstalled('@intlayer/use-intl')) {\n addIfMissing('@intlayer/use-intl');\n addIfMissing('use-intl');\n compatSyncConfig ??= {\n format: 'icu',\n sourceTemplate: './messages/${locale}.json',\n // use-intl (the core of next-intl) uses the same single-file namespace model.\n splitKeys: true,\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'useIntlVitePlugin',\n pluginPackageSource: '@intlayer/use-intl/plugin',\n };\n }\n\n // react-intl — vite alias injection required, ICU format\n if (isInstalled('react-intl') || isInstalled('@intlayer/react-intl')) {\n addIfMissing('@intlayer/react-intl');\n addIfMissing('react-intl');\n compatSyncConfig ??= {\n format: 'icu',\n sourceTemplate: './src/i18n/${locale}.json',\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'reactIntlVitePlugin',\n pluginPackageSource: '@intlayer/react-intl/plugin',\n };\n }\n\n // @ngneat/transloco — vite alias injection required\n // @todo syncJSON format not yet implemented for transloco\n if (isInstalled('@ngneat/transloco') || isInstalled('@intlayer/transloco')) {\n addIfMissing('@intlayer/transloco');\n addIfMissing('@ngneat/transloco');\n compatVitePluginConfig ??= {\n pluginFunctionName: 'translocoVitePlugin',\n pluginPackageSource: '@intlayer/transloco/plugin',\n };\n }\n\n // svelte-i18n — vite alias injection required, flat JSON (i18next-compatible)\n if (isInstalled('svelte-i18n') || isInstalled('@intlayer/svelte-i18n')) {\n addIfMissing('@intlayer/svelte-i18n');\n addIfMissing('svelte-i18n');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './src/locales/${locale}.json',\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'svelteI18nVitePlugin',\n pluginPackageSource: '@intlayer/svelte-i18n/plugin',\n };\n }\n\n // node-polyglot — vite alias injection required\n // @todo syncJSON format not yet implemented for polyglot\n if (isInstalled('node-polyglot') || isInstalled('@intlayer/polyglot')) {\n addIfMissing('@intlayer/polyglot');\n addIfMissing('node-polyglot');\n compatVitePluginConfig ??= {\n pluginFunctionName: 'polyglotVitePlugin',\n pluginPackageSource: '@intlayer/polyglot/plugin',\n };\n }\n\n // @nuxtjs/i18n — nuxt module (no vite plugin), vue-i18n JSON format\n if (isInstalled('@nuxtjs/i18n') || isInstalled('@intlayer/nuxtjs-i18n')) {\n addIfMissing('@intlayer/nuxtjs-i18n');\n addIfMissing('@nuxtjs/i18n');\n compatSyncConfig ??= {\n format: 'vue-i18n',\n sourceTemplate: './locales/${locale}/${key}.json',\n };\n // nuxt config handled via updateNuxtConfigForNuxtjsI18n in init/index.ts\n }\n\n // @ngx-translate/core — vite alias injection required, flat JSON (i18next)\n if (\n isInstalled('@ngx-translate/core') ||\n isInstalled('@intlayer/ngx-translate')\n ) {\n addIfMissing('@intlayer/ngx-translate');\n addIfMissing('@ngx-translate/core');\n compatSyncConfig ??= {\n format: 'i18next',\n sourceTemplate: './assets/i18n/${locale}.json',\n };\n compatVitePluginConfig ??= {\n pluginFunctionName: 'ngxTranslateVitePlugin',\n pluginPackageSource: '@intlayer/ngx-translate/plugin',\n };\n }\n\n // @lingui/core — vite alias injection required.\n // lingui keeps one catalog file per locale (default name `messages`), as\n // `.po` (its default) or `.json`. The catalog filename is captured by the\n // `${key}` segment so the produced dictionary key is `messages`, matching the\n // fixed `messages` namespace the lingui compat runtime reads from.\n if (\n isInstalled('@lingui/core') ||\n isInstalled('@lingui/react') ||\n isInstalled('@intlayer/lingui')\n ) {\n addIfMissing('@intlayer/lingui');\n addIfMissing('@lingui/core');\n\n const linguiUsesPo = options.linguiCatalogFormat === 'po';\n compatSyncConfig ??= linguiUsesPo\n ? {\n plugin: 'po',\n format: 'icu',\n sourceTemplate: './src/locales/${locale}/${key}.po',\n }\n : {\n plugin: 'json',\n format: 'icu',\n sourceTemplate: './src/locales/${locale}/${key}.json',\n };\n\n compatVitePluginConfig ??= {\n // `@intlayer/lingui/plugin` exports `lingui` as a drop-in replacement for\n // `@lingui/vite-plugin`, so a fresh project gets `lingui()` injected and a\n // project already using `@lingui/vite-plugin` only has its import source\n // rewritten (see `replacesVitePlugin`).\n pluginFunctionName: 'lingui',\n pluginPackageSource: '@intlayer/lingui/plugin',\n replacesVitePlugin: {\n importName: 'lingui',\n fromPackageSource: '@lingui/vite-plugin',\n },\n };\n }\n\n // i18n-js — vite alias injection required\n // @todo syncJSON format not yet implemented for i18n-js\n if (isInstalled('i18n-js') || isInstalled('@intlayer/i18n-js')) {\n addIfMissing('@intlayer/i18n-js');\n addIfMissing('i18n-js');\n compatVitePluginConfig ??= {\n pluginFunctionName: 'i18nJsVitePlugin',\n pluginPackageSource: '@intlayer/i18n-js/plugin',\n };\n }\n\n if (compatSyncConfig) {\n addDevIfMissing(\n compatSyncConfig.plugin === 'po'\n ? '@intlayer/sync-po-plugin'\n : '@intlayer/sync-json-plugin'\n );\n }\n\n return {\n packagesToInstall,\n devPackagesToInstall,\n compatSyncConfig,\n compatVitePluginConfig,\n };\n};\n\n/**\n * Runs the package install command synchronously.\n * Throws if the install process exits with a non-zero code.\n */\nexport const installPackages = (\n rootDir: string,\n packages: string[],\n packageManager: PackageManager,\n isDev: boolean = false\n): void => {\n const command = buildInstallCommand(packageManager, packages, isDev);\n execSync(command, { cwd: rootDir, stdio: 'inherit' });\n};\n\n/**\n * Determines whether a dependency name belongs to the Intlayer ecosystem.\n *\n * Matches the core `intlayer` package, every scoped `@intlayer/*` package\n * (including compat adapters such as `@intlayer/next-intl`) and the framework\n * runtime integrations that follow the `<framework>-intlayer` convention\n * (e.g. `next-intlayer`, `react-intlayer`, `express-intlayer`).\n */\nexport const isIntlayerPackageName = (packageName: string): boolean =>\n packageName === 'intlayer' ||\n packageName.startsWith('@intlayer/') ||\n /-intlayer$/.test(packageName);\n\n/**\n * Reduces a semver range or full version to its `major.minor.patch` core,\n * stripping range prefixes (`^`, `~`), pre-release identifiers and build\n * metadata. Returns `null` when no `major.minor.patch` can be extracted.\n *\n * @example normalizeVersion('^9.0.0-canary.3') // '9.0.0'\n */\nexport const normalizeVersion = (version?: string): string | null => {\n if (!version || typeof version !== 'string') return null;\n const match = version.match(/(\\d+)\\.(\\d+)\\.(\\d+)/);\n return match ? `${match[1]}.${match[2]}.${match[3]}` : null;\n};\n\n/**\n * Reads the installed version of a package from its `package.json` inside the\n * project's `node_modules`. Returns `null` when the package is not installed or\n * its manifest cannot be read.\n */\nexport const getInstalledPackageVersion = (\n rootDir: string,\n packageName: string\n): string | null => {\n try {\n const manifestPath = join(\n rootDir,\n 'node_modules',\n packageName,\n 'package.json'\n );\n if (!existsSync(manifestPath)) return null;\n const { version } = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n return typeof version === 'string' ? version : null;\n } catch {\n return null;\n }\n};\n\n/**\n * Returns the Intlayer packages from `dependencies` whose installed version is\n * behind `targetVersion` (compared on `major.minor.patch`). Packages that are\n * not installed yet are ignored — those are handled by\n * {@link detectMissingIntlayerPackages}.\n */\nexport const detectOutdatedIntlayerPackages = (\n rootDir: string,\n dependencies: Record<string, string>,\n targetVersion: string\n): string[] => {\n const normalizedTarget = normalizeVersion(targetVersion);\n if (!normalizedTarget) return [];\n\n return Object.keys(dependencies)\n .filter(isIntlayerPackageName)\n .filter((packageName) => {\n const installedVersion = getInstalledPackageVersion(rootDir, packageName);\n const normalizedInstalled = normalizeVersion(\n installedVersion ?? undefined\n );\n if (!normalizedInstalled) return false;\n return compareVersions(normalizedInstalled, '<', normalizedTarget);\n });\n};\n\n/**\n * Upgrades the given packages to `targetVersion` synchronously, preserving the\n * dependency type via the `isDev` flag. Throws if the install process exits\n * with a non-zero code.\n */\nexport const upgradePackages = (\n rootDir: string,\n packages: string[],\n packageManager: PackageManager,\n targetVersion: string,\n isDev: boolean = false\n): void => {\n if (packages.length === 0) return;\n const versionedPackages = packages.map(\n (packageName) => `${packageName}@${targetVersion}`\n );\n const command = buildInstallCommand(packageManager, versionedPackages, isDev);\n execSync(command, { cwd: rootDir, stdio: 'inherit' });\n};\n"],"mappings":"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"}