{"version":3,"file":"cms.mjs","names":[],"sources":["../../../src/init/cms.ts"],"sourcesContent":["import * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, logger, v, x } from '@intlayer/config/logger';\nimport fg from 'fast-glob';\nimport {\n  enableIntlayerEditorConfig,\n  type RoutingMode,\n  setIntlayerConfigCompilerOutput,\n  setIntlayerConfigRoutingMode,\n} from './utils/configManipulation';\nimport { exists, readFileFromRoot, writeFileToRoot } from './utils/fileSystem';\n\n/**\n * Intlayer configuration file candidates, ordered by the precedence used when\n * resolving which file to mutate.\n */\nexport const INTLAYER_CONFIG_FILE_CANDIDATES = [\n  'intlayer.config.ts',\n  'intlayer.config.mjs',\n  'intlayer.config.js',\n  'intlayer.config.cjs',\n  'intlayer.config.json',\n] as const;\n\n/**\n * Environment files searched (most-preferred first) when persisting the CMS\n * credentials. The first existing match is reused; otherwise a `.env` file is\n * created at the project root.\n */\nconst ENV_FILE_CANDIDATES = ['.env', '.env.local', '.env.development'] as const;\n\n/** Credentials returned by the CMS login flow (access key pair). */\nexport type CmsCredentials = {\n  clientId: string;\n  clientSecret: string;\n};\n\n/**\n * Resolves the first existing Intlayer configuration file at the project root,\n * or `undefined` when none is present.\n */\nconst findIntlayerConfigFile = async (\n  rootDir: string\n): Promise<(typeof INTLAYER_CONFIG_FILE_CANDIDATES)[number] | undefined> => {\n  for (const candidate of INTLAYER_CONFIG_FILE_CANDIDATES) {\n    if (await exists(rootDir, candidate)) {\n      return candidate;\n    }\n  }\n\n  return undefined;\n};\n\n/**\n * Locates the environment file the CMS credentials should be written to. Uses\n * `fast-glob` to detect an existing dotenv file at the project root, preferring\n * a plain `.env`; falls back to `.env` (to be created) when none exist.\n */\nexport const findEnvFile = async (rootDir: string): Promise<string> => {\n  const matches = await fg([...ENV_FILE_CANDIDATES], {\n    cwd: rootDir,\n    dot: true,\n    deep: 1,\n    onlyFiles: true,\n  });\n\n  return (\n    ENV_FILE_CANDIDATES.find((candidate) => matches.includes(candidate)) ??\n    '.env'\n  );\n};\n\n/**\n * Inserts or updates a `KEY=value` line in dotenv file content. An existing\n * assignment of the same key is replaced in place; otherwise the line is\n * appended with a trailing newline.\n */\nconst upsertEnvVariable = (\n  content: string,\n  key: string,\n  value: string\n): string => {\n  const line = `${key}=${value}`;\n  const keyMatcher = new RegExp(`^${key}=.*$`, 'm');\n\n  if (keyMatcher.test(content)) {\n    return content.replace(keyMatcher, line);\n  }\n\n  const needsLeadingNewline = content.length > 0 && !content.endsWith('\\n');\n\n  return `${content}${needsLeadingNewline ? '\\n' : ''}${line}\\n`;\n};\n\n/**\n * Persists the CMS access-key credentials to the project's environment file as\n * `INTLAYER_CLIENT_ID` / `INTLAYER_CLIENT_SECRET`. The target file is detected\n * with {@link findEnvFile} and created when missing. Returns the relative path\n * of the file that was written.\n */\nexport const writeCmsCredentialsToEnv = async (\n  rootDir: string,\n  { clientId, clientSecret }: CmsCredentials\n): Promise<string> => {\n  const envFile = await findEnvFile(rootDir);\n\n  let content = '';\n  if (await exists(rootDir, envFile)) {\n    content = await readFileFromRoot(rootDir, envFile);\n  }\n\n  let updatedContent = content;\n  updatedContent = upsertEnvVariable(\n    updatedContent,\n    'INTLAYER_CLIENT_ID',\n    clientId\n  );\n  updatedContent = upsertEnvVariable(\n    updatedContent,\n    'INTLAYER_CLIENT_SECRET',\n    clientSecret\n  );\n\n  await writeFileToRoot(rootDir, envFile, updatedContent);\n  logger(\n    `${v} Saved Intlayer CMS credentials to ${colorizePath(envFile)} (${colorize('INTLAYER_CLIENT_ID', ANSIColors.GREY_LIGHT)}, ${colorize('INTLAYER_CLIENT_SECRET', ANSIColors.GREY_LIGHT)})`\n  );\n\n  return envFile;\n};\n\n/**\n * Enables the Intlayer visual editor in the project's configuration file:\n * flips `editor.enabled` to `true` and wires `clientId` / `clientSecret` to the\n * matching environment variables. JSON configs cannot reference `process.env`,\n * so they are skipped with a warning. Returns the config file that was updated,\n * or `undefined` when no editable config was found.\n */\nexport const enableEditorInConfig = async (\n  rootDir: string\n): Promise<string | undefined> => {\n  const configFile = await findIntlayerConfigFile(rootDir);\n\n  if (!configFile) {\n    logger(\n      `${x} Could not find an Intlayer configuration file to enable the editor.`,\n      { level: 'warn' }\n    );\n    return undefined;\n  }\n\n  const extension = configFile.split('.').pop()!;\n\n  if (extension === 'json') {\n    logger(\n      `${x} ${colorizePath(configFile)} is a JSON config and cannot reference environment variables. Enable the editor and set clientId/clientSecret manually.`,\n      { level: 'warn' }\n    );\n    return undefined;\n  }\n\n  const content = await readFileFromRoot(rootDir, configFile);\n  const updatedContent = enableIntlayerEditorConfig(content);\n\n  if (updatedContent !== content) {\n    await writeFileToRoot(rootDir, configFile, updatedContent);\n    logger(`${v} Enabled the Intlayer editor in ${colorizePath(configFile)}`);\n  } else {\n    logger(\n      `${v} ${colorizePath(configFile)} already has the Intlayer editor enabled`\n    );\n  }\n\n  return configFile;\n};\n\n/**\n * Sets `routing.mode` in the project's Intlayer configuration file. Returns the\n * config file that was updated, or `undefined` when none was found.\n */\nexport const setRoutingModeInConfig = async (\n  rootDir: string,\n  mode: RoutingMode\n): Promise<string | undefined> => {\n  const configFile = await findIntlayerConfigFile(rootDir);\n\n  if (!configFile) return undefined;\n\n  const extension = configFile.split('.').pop()!;\n  const content = await readFileFromRoot(rootDir, configFile);\n  const updatedContent = setIntlayerConfigRoutingMode(content, extension, mode);\n\n  if (updatedContent !== content) {\n    await writeFileToRoot(rootDir, configFile, updatedContent);\n    logger(\n      `${v} Set ${colorize(`routing.mode = '${mode}'`, ANSIColors.GREY_LIGHT)} in ${colorizePath(configFile)}`\n    );\n  }\n\n  return configFile;\n};\n\n/**\n * Sets `compiler.output` in the project's Intlayer configuration file to the\n * given `{{variable}}` path template. Returns the config file that was updated,\n * or `undefined` when none was found.\n */\nexport const setCompilerOutputInConfig = async (\n  rootDir: string,\n  outputTemplate: string\n): Promise<string | undefined> => {\n  const configFile = await findIntlayerConfigFile(rootDir);\n\n  if (!configFile) return undefined;\n\n  const extension = configFile.split('.').pop()!;\n  const content = await readFileFromRoot(rootDir, configFile);\n  const updatedContent = setIntlayerConfigCompilerOutput(\n    content,\n    extension,\n    outputTemplate\n  );\n\n  if (updatedContent !== content) {\n    await writeFileToRoot(rootDir, configFile, updatedContent);\n    logger(\n      `${v} Set ${colorize(`compiler.output = '${outputTemplate}'`, ANSIColors.GREY_LIGHT)} in ${colorizePath(configFile)}`\n    );\n  }\n\n  return configFile;\n};\n\n/**\n * Completes the CMS setup once credentials are received from the login flow:\n * writes them to the environment file and enables the editor in the Intlayer\n * configuration file.\n */\nexport const setupCmsCredentials = async (\n  rootDir: string,\n  credentials: CmsCredentials\n): Promise<void> => {\n  await writeCmsCredentialsToEnv(rootDir, credentials);\n  await enableEditorInConfig(rootDir);\n};\n"],"mappings":"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"}