@intlayer/engine 9.5.0 → 9.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/init/index.cjs.map +1 -1
- package/dist/cjs/init/utils/devScript.cjs +2 -3
- package/dist/cjs/init/utils/devScript.cjs.map +1 -1
- package/dist/cjs/init/utils/githubActions.cjs +17 -16
- package/dist/cjs/init/utils/githubActions.cjs.map +1 -1
- package/dist/cjs/utils/contentWatcherLock.cjs +4 -5
- package/dist/cjs/utils/contentWatcherLock.cjs.map +1 -1
- package/dist/esm/init/index.mjs.map +1 -1
- package/dist/esm/init/utils/devScript.mjs +2 -3
- package/dist/esm/init/utils/devScript.mjs.map +1 -1
- package/dist/esm/init/utils/githubActions.mjs +17 -16
- package/dist/esm/init/utils/githubActions.mjs.map +1 -1
- package/dist/esm/utils/contentWatcherLock.mjs +4 -5
- package/dist/esm/utils/contentWatcherLock.mjs.map +1 -1
- package/dist/types/formatDictionary.d.ts +3 -2
- package/dist/types/formatDictionary.d.ts.map +1 -1
- package/dist/types/init/utils/devScript.d.ts +2 -3
- package/dist/types/init/utils/devScript.d.ts.map +1 -1
- package/dist/types/init/utils/githubActions.d.ts +9 -11
- package/dist/types/init/utils/githubActions.d.ts.map +1 -1
- package/dist/types/intlayer/dist/types/index.d.ts +14 -0
- package/dist/types/listDictionariesPath.d.ts +1 -1
- package/dist/types/utils/contentWatcherLock.d.ts +2 -13
- package/dist/types/utils/contentWatcherLock.d.ts.map +1 -1
- package/package.json +11 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contentWatcherLock.cjs","names":["join","readFile","rm","mkdir","dirname","writeFile","colorize","ANSIColors"],"sources":["../../../src/utils/contentWatcherLock.ts"],"sourcesContent":["import { rmSync } from 'node:fs';\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\n/**\n * What kind of process is watching the content declarations.\n *\n * - `cli` — the `intlayer watch` / `intlayer build --watch` command.\n * - `bundler` — an Intlayer bundler plugin (e.g. `withIntlayer` on Next.js).\n */\nexport type ContentWatcherSource = 'cli' | 'bundler';\n\nexport type ContentWatcherOwner = {\n /** PID of the process holding the watcher. */\n pid: number;\n source: ContentWatcherSource;\n /**\n * How the owner should be named in a message, e.g. `intlayer watch` or\n * `next-intlayer`.\n */\n label: string;\n /**\n * Release from which a `bundler` owner watches the content declarations on\n * its own, so a CLI watcher finding it can name the version that made the\n * parallel command redundant without keeping a table of its own.\n */\n since?: string;\n};\n\n/**\n * Set on every process spawned by `intlayer watch --with` (and\n * `intlayer build --watch --with`), so a bundler plugin running as that child\n * knows a CLI watcher is already covering the project — without having to\n * depend on the two processes reaching the lock file in a given order.\n *\n * Holds the label of the command that set it.\n */\nexport const CLI_CONTENT_WATCHER_ENV_VAR = 'INTLAYER_CLI_CONTENT_WATCHER';\n\n/** Name of the file materialising the ownership of the content watcher. */\nconst CONTENT_WATCHER_LOCK_FILE_NAME = 'intlayer-content-watcher.lock';\n\n/** How many times taking the lock may follow up on a reclaimed stale one. */\nconst ACQUIRE_ATTEMPTS = 3;\n\n/** Lock owned by this process, released on exit. */\nlet ownedLockFilePath: string | undefined;\n\n// The one place that has to stay synchronous: Node runs `exit` listeners to\n// completion without an event loop turn, so a promise scheduled here would\n// never settle and the lock would outlive the process — stalling the next dev\n// session until its PID check reclaims it.\nprocess.on('exit', () => {\n if (!ownedLockFilePath) return;\n\n try {\n rmSync(ownedLockFilePath, { force: true });\n } catch {}\n});\n\n/**\n * Path of the lock coordinating every content watcher of one project.\n *\n * Deliberately at the root of `.intlayer` rather than in its `cache`\n * subdirectory: `cleanOutputDir` wipes every subdirectory of `.intlayer`, so a\n * lock kept in `cache` would be erased by any `prepareIntlayer` that cleans —\n * after which a bundler plugin would see no owner and start a second watcher\n * next to the `intlayer watch` still running. The root itself is never removed.\n *\n * @param configuration - The resolved Intlayer configuration.\n */\nexport const getContentWatcherLockFilePath = (\n configuration: IntlayerConfig\n): string => join(configuration.system.tempDir, CONTENT_WATCHER_LOCK_FILE_NAME);\n\n/**\n * Whether a PID belongs to a process that is still running.\n */\nconst getIsProcessAlive = (pid: number): boolean => {\n if (pid === process.pid) return true;\n\n try {\n // Signal 0 checks for existence without delivering a signal.\n process.kill(pid, 0);\n return true;\n } catch (error) {\n // EPERM means the process exists but belongs to another user.\n return (error as NodeJS.ErrnoException).code === 'EPERM';\n }\n};\n\n/**\n * Reads the lock, removing it when it describes a process that has since died.\n *\n * An unreadable or malformed lock is treated as abandoned: a corrupted file\n * must never block the watcher for a whole dev session.\n *\n * @returns The live owner, or `null` when nothing is watching.\n */\nexport const getContentWatcherOwner = async (\n configuration: IntlayerConfig\n): Promise<ContentWatcherOwner | null> => {\n const lockFilePath = getContentWatcherLockFilePath(configuration);\n\n let owner: Partial<ContentWatcherOwner>;\n\n try {\n owner = JSON.parse(await readFile(lockFilePath, 'utf8'));\n } catch {\n return null;\n }\n\n if (owner.pid && getIsProcessAlive(owner.pid)) {\n return {\n pid: owner.pid,\n source: owner.source ?? 'bundler',\n label: owner.label ?? 'Intlayer',\n since: owner.since,\n };\n }\n\n try {\n await rm(lockFilePath, { force: true });\n } catch {}\n\n return null;\n};\n\n/**\n * Tries to become the process that watches this project's content\n * declarations.\n *\n * Creating the file with `wx` is atomic, so exactly one process wins even when\n * several try at the same moment — which is the normal case on Next.js, where\n * `next.config.*` is evaluated in more than one process.\n *\n * @param configuration - The resolved Intlayer configuration.\n * @param owner - How this process should describe itself to the others.\n * @returns `true` when this process now owns the watcher.\n */\nexport const acquireContentWatcherLock = async (\n configuration: IntlayerConfig,\n owner: Omit<ContentWatcherOwner, 'pid'>\n): Promise<boolean> => {\n const lockFilePath = getContentWatcherLockFilePath(configuration);\n const data = JSON.stringify({\n pid: process.pid,\n ...owner,\n } satisfies ContentWatcherOwner);\n\n // Bounded rather than recursive: the retry only exists to follow up on a lock\n // `getContentWatcherOwner` has just reclaimed, and two processes reclaiming\n // and re-taking it in turn must not spin forever.\n for (let attempt = 0; attempt < ACQUIRE_ATTEMPTS; attempt++) {\n try {\n await mkdir(dirname(lockFilePath), { recursive: true });\n await writeFile(lockFilePath, data, { flag: 'wx' });\n\n ownedLockFilePath = lockFilePath;\n\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') return false;\n\n // A lock left behind by a dead process is reclaimed by\n // `getContentWatcherOwner`, leaving the next attempt free to take it.\n if (await getContentWatcherOwner(configuration)) return false;\n }\n }\n\n return false;\n};\n\n/**\n * The CLI watcher command this process was spawned by, or `null` when it was\n * not started through `intlayer watch --with`.\n */\nexport const getCliContentWatcherLabel = (): string | null =>\n process.env[CLI_CONTENT_WATCHER_ENV_VAR] || null;\n\n/**\n * Registers a CLI command as the content watcher of this project.\n *\n * Call it *before* spawning a `--with` child: taking the lock first means the\n * bundler plugin inside that child never races the command for it, and the\n * environment marker makes the child's decision independent of the filesystem\n * altogether.\n *\n * @param configuration - The resolved Intlayer configuration.\n * @param label - How the command should be named in a message.\n * @returns The bundler already watching this project, or `null` when the\n * command took the watcher for itself. A CLI watcher never stands down on that\n * answer — watching is what it was asked to do — but reporting it lets the user\n * know the parallel command has become redundant.\n */\nexport const claimCliContentWatcher = async (\n configuration: IntlayerConfig,\n label: string\n): Promise<ContentWatcherOwner | null> => {\n // Set before the first `await`: a `--with` child spawned while the lock is\n // still being written must already see the marker in its environment.\n process.env[CLI_CONTENT_WATCHER_ENV_VAR] = label;\n\n const hasAcquiredLock = await acquireContentWatcherLock(configuration, {\n source: 'cli',\n label,\n });\n\n if (hasAcquiredLock) return null;\n\n const owner = await getContentWatcherOwner(configuration);\n\n return owner?.source === 'bundler' ? owner : null;\n};\n\n/** Keeps the redundant-watcher notice to one per process. */\nlet hasReportedRedundantWatcher = false;\n\ntype RedundantWatcherReport = {\n /** Name of the CLI command watching, e.g. `intlayer watch`. */\n cliLabel: string;\n /** Name of the bundler integration watching, e.g. `next-intlayer`. */\n bundlerLabel: string;\n /**\n * Release from which the bundler integration watches on its own. Omitted when\n * the integration did not report one.\n */\n since?: string;\n};\n\n/**\n * Reports a CLI watcher running alongside a bundler integration that watches\n * on its own, and points at the parallel command as the part to drop.\n *\n * Emitted at most once per process: a bundler config is evaluated several times\n * per command, and callers may re-enter this on a retry loop.\n *\n * @param configuration - The resolved Intlayer configuration.\n * @param report - Who is watching, and from which release it became redundant.\n */\nexport const reportRedundantContentWatcher = (\n configuration: IntlayerConfig,\n { cliLabel, bundlerLabel, since }: RedundantWatcherReport\n): void => {\n if (hasReportedRedundantWatcher) return;\n hasReportedRedundantWatcher = true;\n\n getAppLogger(configuration)(\n [\n colorize(cliLabel, ANSIColors.BLUE, ANSIColors.BEIGE),\n 'is watching your content declarations in parallel with',\n `${colorize(bundlerLabel, ANSIColors.BLUE, ANSIColors.BEIGE)},`,\n since\n ? `which watches them on its own since ${colorize(`${bundlerLabel}@${since}`, ANSIColors.GREY_LIGHT, ANSIColors.BEIGE)}.`\n : 'which watches them on its own.',\n 'The parallel watch command is no longer needed — running your dev server on its own is enough.',\n ],\n { level: 'warn' }\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwCA,MAAa,8BAA8B;;AAG3C,MAAM,iCAAiC;;AAGvC,MAAM,mBAAmB;;AAGzB,IAAI;AAMJ,QAAQ,GAAG,cAAc;CACvB,IAAI,CAAC,mBAAmB;CAExB,IAAI;EACF,oBAAO,mBAAmB,EAAE,OAAO,KAAK,CAAC;CAC3C,QAAQ,CAAC;AACX,CAAC;;;;;;;;;;;;AAaD,MAAa,iCACX,sBACWA,gBAAK,cAAc,OAAO,SAAS,8BAA8B;;;;AAK9E,MAAM,qBAAqB,QAAyB;CAClD,IAAI,QAAQ,QAAQ,KAAK,OAAO;CAEhC,IAAI;EAEF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,SAAS,OAAO;EAEd,OAAQ,MAAgC,SAAS;CACnD;AACF;;;;;;;;;AAUA,MAAa,yBAAyB,OACpC,kBACwC;CACxC,MAAM,eAAe,8BAA8B,aAAa;CAEhE,IAAI;CAEJ,IAAI;EACF,QAAQ,KAAK,MAAM,UAAMC,2BAAS,cAAc,MAAM,CAAC;CACzD,QAAQ;EACN,OAAO;CACT;CAEA,IAAI,MAAM,OAAO,kBAAkB,MAAM,GAAG,GAC1C,OAAO;EACL,KAAK,MAAM;EACX,QAAQ,MAAM,UAAU;EACxB,OAAO,MAAM,SAAS;EACtB,OAAO,MAAM;CACf;CAGF,IAAI;EACF,UAAMC,qBAAG,cAAc,EAAE,OAAO,KAAK,CAAC;CACxC,QAAQ,CAAC;CAET,OAAO;AACT;;;;;;;;;;;;;AAcA,MAAa,4BAA4B,OACvC,eACA,UACqB;CACrB,MAAM,eAAe,8BAA8B,aAAa;CAChE,MAAM,OAAO,KAAK,UAAU;EAC1B,KAAK,QAAQ;EACb,GAAG;CACL,CAA+B;CAK/B,KAAK,IAAI,UAAU,GAAG,UAAU,kBAAkB,WAChD,IAAI;EACF,UAAMC,4BAAMC,mBAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;EACtD,UAAMC,4BAAU,cAAc,MAAM,EAAE,MAAM,KAAK,CAAC;EAElD,oBAAoB;EAEpB,OAAO;CACT,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO;EAI/D,IAAI,MAAM,uBAAuB,aAAa,GAAG,OAAO;CAC1D;CAGF,OAAO;AACT;;;;;AAMA,MAAa,kCACX,QAAQ,uCAAoC;;;;;;;;;;;;;;;;AAiB9C,MAAa,yBAAyB,OACpC,eACA,UACwC;CAGxC,QAAQ,IAAI,+BAA+B;CAO3C,IAAI,MAL0B,0BAA0B,eAAe;EACrE,QAAQ;EACR;CACF,CAAC,GAEoB,OAAO;CAE5B,MAAM,QAAQ,MAAM,uBAAuB,aAAa;CAExD,OAAO,OAAO,WAAW,YAAY,QAAQ;AAC/C;;AAGA,IAAI,8BAA8B;;;;;;;;;;;AAwBlC,MAAa,iCACX,eACA,EAAE,UAAU,cAAc,YACjB;CACT,IAAI,6BAA6B;CACjC,8BAA8B;CAE9B,0CAAa,aAAa,CAAC,CACzB;MACEC,kCAAS,UAAUC,wBAAW,MAAMA,wBAAW,KAAK;EACpD;EACA,OAAGD,kCAAS,cAAcC,wBAAW,MAAMA,wBAAW,KAAK,EAAE;EAC7D,QACI,2CAAuCD,kCAAS,GAAG,aAAa,GAAG,SAASC,wBAAW,YAAYA,wBAAW,KAAK,EAAE,KACrH;EACJ;CACF,GACA,EAAE,OAAO,OAAO,CAClB;AACF"}
|
|
1
|
+
{"version":3,"file":"contentWatcherLock.cjs","names":["join","readFile","rm","mkdir","dirname","writeFile","colorize","ANSIColors"],"sources":["../../../src/utils/contentWatcherLock.ts"],"sourcesContent":["import { rmSync } from 'node:fs';\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\n/**\n * What kind of process is watching the content declarations.\n *\n * - `cli` — the `intlayer watch` / `intlayer build --watch` command.\n * - `bundler` — an Intlayer bundler plugin (e.g. `withIntlayer` on Next.js).\n */\nexport type ContentWatcherSource = 'cli' | 'bundler';\n\nexport type ContentWatcherOwner = {\n /** PID of the process holding the watcher. */\n pid: number;\n source: ContentWatcherSource;\n /**\n * How the owner should be named in a message, e.g. `intlayer watch` or\n * `next-intlayer`.\n */\n label: string;\n};\n\n/**\n * Set on every process spawned by `intlayer watch --with` (and\n * `intlayer build --watch --with`), so a bundler plugin running as that child\n * knows a CLI watcher is already covering the project — without having to\n * depend on the two processes reaching the lock file in a given order.\n *\n * Holds the label of the command that set it.\n */\nexport const CLI_CONTENT_WATCHER_ENV_VAR = 'INTLAYER_CLI_CONTENT_WATCHER';\n\n/** Name of the file materialising the ownership of the content watcher. */\nconst CONTENT_WATCHER_LOCK_FILE_NAME = 'intlayer-content-watcher.lock';\n\n/** How many times taking the lock may follow up on a reclaimed stale one. */\nconst ACQUIRE_ATTEMPTS = 3;\n\n/** Lock owned by this process, released on exit. */\nlet ownedLockFilePath: string | undefined;\n\n// The one place that has to stay synchronous: Node runs `exit` listeners to\n// completion without an event loop turn, so a promise scheduled here would\n// never settle and the lock would outlive the process — stalling the next dev\n// session until its PID check reclaims it.\nprocess.on('exit', () => {\n if (!ownedLockFilePath) return;\n\n try {\n rmSync(ownedLockFilePath, { force: true });\n } catch {}\n});\n\n/**\n * Path of the lock coordinating every content watcher of one project.\n *\n * Deliberately at the root of `.intlayer` rather than in its `cache`\n * subdirectory: `cleanOutputDir` wipes every subdirectory of `.intlayer`, so a\n * lock kept in `cache` would be erased by any `prepareIntlayer` that cleans —\n * after which a bundler plugin would see no owner and start a second watcher\n * next to the `intlayer watch` still running. The root itself is never removed.\n *\n * @param configuration - The resolved Intlayer configuration.\n */\nexport const getContentWatcherLockFilePath = (\n configuration: IntlayerConfig\n): string => join(configuration.system.tempDir, CONTENT_WATCHER_LOCK_FILE_NAME);\n\n/**\n * Whether a PID belongs to a process that is still running.\n */\nconst getIsProcessAlive = (pid: number): boolean => {\n if (pid === process.pid) return true;\n\n try {\n // Signal 0 checks for existence without delivering a signal.\n process.kill(pid, 0);\n return true;\n } catch (error) {\n // EPERM means the process exists but belongs to another user.\n return (error as NodeJS.ErrnoException).code === 'EPERM';\n }\n};\n\n/**\n * Reads the lock, removing it when it describes a process that has since died.\n *\n * An unreadable or malformed lock is treated as abandoned: a corrupted file\n * must never block the watcher for a whole dev session.\n *\n * @returns The live owner, or `null` when nothing is watching.\n */\nexport const getContentWatcherOwner = async (\n configuration: IntlayerConfig\n): Promise<ContentWatcherOwner | null> => {\n const lockFilePath = getContentWatcherLockFilePath(configuration);\n\n let owner: Partial<ContentWatcherOwner>;\n\n try {\n owner = JSON.parse(await readFile(lockFilePath, 'utf8'));\n } catch {\n return null;\n }\n\n if (owner.pid && getIsProcessAlive(owner.pid)) {\n return {\n pid: owner.pid,\n source: owner.source ?? 'bundler',\n label: owner.label ?? 'Intlayer',\n };\n }\n\n try {\n await rm(lockFilePath, { force: true });\n } catch {}\n\n return null;\n};\n\n/**\n * Tries to become the process that watches this project's content\n * declarations.\n *\n * Creating the file with `wx` is atomic, so exactly one process wins even when\n * several try at the same moment — which is the normal case on Next.js, where\n * `next.config.*` is evaluated in more than one process.\n *\n * @param configuration - The resolved Intlayer configuration.\n * @param owner - How this process should describe itself to the others.\n * @returns `true` when this process now owns the watcher.\n */\nexport const acquireContentWatcherLock = async (\n configuration: IntlayerConfig,\n owner: Omit<ContentWatcherOwner, 'pid'>\n): Promise<boolean> => {\n const lockFilePath = getContentWatcherLockFilePath(configuration);\n const data = JSON.stringify({\n pid: process.pid,\n ...owner,\n } satisfies ContentWatcherOwner);\n\n // Bounded rather than recursive: the retry only exists to follow up on a lock\n // `getContentWatcherOwner` has just reclaimed, and two processes reclaiming\n // and re-taking it in turn must not spin forever.\n for (let attempt = 0; attempt < ACQUIRE_ATTEMPTS; attempt++) {\n try {\n await mkdir(dirname(lockFilePath), { recursive: true });\n await writeFile(lockFilePath, data, { flag: 'wx' });\n\n ownedLockFilePath = lockFilePath;\n\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') return false;\n\n // A lock left behind by a dead process is reclaimed by\n // `getContentWatcherOwner`, leaving the next attempt free to take it.\n if (await getContentWatcherOwner(configuration)) return false;\n }\n }\n\n return false;\n};\n\n/**\n * The CLI watcher command this process was spawned by, or `null` when it was\n * not started through `intlayer watch --with`.\n */\nexport const getCliContentWatcherLabel = (): string | null =>\n process.env[CLI_CONTENT_WATCHER_ENV_VAR] || null;\n\n/**\n * Registers a CLI command as the content watcher of this project.\n *\n * Call it *before* spawning a `--with` child: taking the lock first means the\n * bundler plugin inside that child never races the command for it, and the\n * environment marker makes the child's decision independent of the filesystem\n * altogether.\n *\n * @param configuration - The resolved Intlayer configuration.\n * @param label - How the command should be named in a message.\n * @returns The bundler already watching this project, or `null` when the\n * command took the watcher for itself. A CLI watcher never stands down on that\n * answer — watching is what it was asked to do — but reporting it lets the user\n * know the parallel command has become redundant.\n */\nexport const claimCliContentWatcher = async (\n configuration: IntlayerConfig,\n label: string\n): Promise<ContentWatcherOwner | null> => {\n // Set before the first `await`: a `--with` child spawned while the lock is\n // still being written must already see the marker in its environment.\n process.env[CLI_CONTENT_WATCHER_ENV_VAR] = label;\n\n const hasAcquiredLock = await acquireContentWatcherLock(configuration, {\n source: 'cli',\n label,\n });\n\n if (hasAcquiredLock) return null;\n\n const owner = await getContentWatcherOwner(configuration);\n\n return owner?.source === 'bundler' ? owner : null;\n};\n\n/** Keeps the redundant-watcher notice to one per process. */\nlet hasReportedRedundantWatcher = false;\n\ntype RedundantWatcherReport = {\n /** Name of the CLI command watching, e.g. `intlayer watch`. */\n cliLabel: string;\n /** Name of the bundler integration watching, e.g. `next-intlayer`. */\n bundlerLabel: string;\n};\n\n/**\n * Reports a CLI watcher running alongside a bundler integration that watches\n * on its own, and points at the parallel command as the part to drop.\n *\n * Emitted at most once per process: a bundler config is evaluated several times\n * per command, and callers may re-enter this on a retry loop.\n *\n * @param configuration - The resolved Intlayer configuration.\n * @param report - Who is watching in parallel with whom.\n */\nexport const reportRedundantContentWatcher = (\n configuration: IntlayerConfig,\n { cliLabel, bundlerLabel }: RedundantWatcherReport\n): void => {\n if (hasReportedRedundantWatcher) return;\n hasReportedRedundantWatcher = true;\n\n getAppLogger(configuration)(\n [\n colorize(cliLabel, ANSIColors.BLUE, ANSIColors.BEIGE),\n 'is watching your content declarations in parallel with',\n `${colorize(bundlerLabel, ANSIColors.BLUE, ANSIColors.BEIGE)},`,\n 'which watches them on its own.',\n 'The parallel watch command is no longer needed — running your dev server on its own is enough.',\n ],\n { level: 'warn' }\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkCA,MAAa,8BAA8B;;AAG3C,MAAM,iCAAiC;;AAGvC,MAAM,mBAAmB;;AAGzB,IAAI;AAMJ,QAAQ,GAAG,cAAc;CACvB,IAAI,CAAC,mBAAmB;CAExB,IAAI;EACF,oBAAO,mBAAmB,EAAE,OAAO,KAAK,CAAC;CAC3C,QAAQ,CAAC;AACX,CAAC;;;;;;;;;;;;AAaD,MAAa,iCACX,sBACWA,gBAAK,cAAc,OAAO,SAAS,8BAA8B;;;;AAK9E,MAAM,qBAAqB,QAAyB;CAClD,IAAI,QAAQ,QAAQ,KAAK,OAAO;CAEhC,IAAI;EAEF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,SAAS,OAAO;EAEd,OAAQ,MAAgC,SAAS;CACnD;AACF;;;;;;;;;AAUA,MAAa,yBAAyB,OACpC,kBACwC;CACxC,MAAM,eAAe,8BAA8B,aAAa;CAEhE,IAAI;CAEJ,IAAI;EACF,QAAQ,KAAK,MAAM,UAAMC,2BAAS,cAAc,MAAM,CAAC;CACzD,QAAQ;EACN,OAAO;CACT;CAEA,IAAI,MAAM,OAAO,kBAAkB,MAAM,GAAG,GAC1C,OAAO;EACL,KAAK,MAAM;EACX,QAAQ,MAAM,UAAU;EACxB,OAAO,MAAM,SAAS;CACxB;CAGF,IAAI;EACF,UAAMC,qBAAG,cAAc,EAAE,OAAO,KAAK,CAAC;CACxC,QAAQ,CAAC;CAET,OAAO;AACT;;;;;;;;;;;;;AAcA,MAAa,4BAA4B,OACvC,eACA,UACqB;CACrB,MAAM,eAAe,8BAA8B,aAAa;CAChE,MAAM,OAAO,KAAK,UAAU;EAC1B,KAAK,QAAQ;EACb,GAAG;CACL,CAA+B;CAK/B,KAAK,IAAI,UAAU,GAAG,UAAU,kBAAkB,WAChD,IAAI;EACF,UAAMC,4BAAMC,mBAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;EACtD,UAAMC,4BAAU,cAAc,MAAM,EAAE,MAAM,KAAK,CAAC;EAElD,oBAAoB;EAEpB,OAAO;CACT,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO;EAI/D,IAAI,MAAM,uBAAuB,aAAa,GAAG,OAAO;CAC1D;CAGF,OAAO;AACT;;;;;AAMA,MAAa,kCACX,QAAQ,uCAAoC;;;;;;;;;;;;;;;;AAiB9C,MAAa,yBAAyB,OACpC,eACA,UACwC;CAGxC,QAAQ,IAAI,+BAA+B;CAO3C,IAAI,MAL0B,0BAA0B,eAAe;EACrE,QAAQ;EACR;CACF,CAAC,GAEoB,OAAO;CAE5B,MAAM,QAAQ,MAAM,uBAAuB,aAAa;CAExD,OAAO,OAAO,WAAW,YAAY,QAAQ;AAC/C;;AAGA,IAAI,8BAA8B;;;;;;;;;;;AAmBlC,MAAa,iCACX,eACA,EAAE,UAAU,mBACH;CACT,IAAI,6BAA6B;CACjC,8BAA8B;CAE9B,0CAAa,aAAa,CAAC,CACzB;MACEC,kCAAS,UAAUC,wBAAW,MAAMA,wBAAW,KAAK;EACpD;EACA,OAAGD,kCAAS,cAAcC,wBAAW,MAAMA,wBAAW,KAAK,EAAE;EAC7D;EACA;CACF,GACA,EAAE,OAAO,OAAO,CAClB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/init/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, logger, v, x } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport { getAlias } from '@intlayer/config/utils';\nimport { initConfig } from '../initConfig';\nimport { setCompilerOutputInConfig, setRoutingModeInConfig } from './cms';\nimport { setupFramework } from './frameworkSetup';\nimport type { CompatSyncConfig, RoutingMode } from './utils';\nimport {\n BACKEND_INTLAYER_PACKAGES,\n detectJsonLocalePattern,\n detectLinguiCatalogPattern,\n detectMissingIntlayerPackages,\n detectNextIntlMessagesPattern,\n detectOutdatedIntlayerPackages,\n detectPackageManager,\n ensureDirectory,\n exists,\n findTsConfigFiles,\n getGithubWorkflows,\n getMetroConfigTemplate,\n hasIntlayerVitePlugin,\n hasLintTooling,\n installPackages,\n parseJSONWithComments,\n readFileFromRoot,\n replaceViteConfigPluginImportSource,\n resolveDevScript,\n resolveGithubWorkflowsContext,\n setupNextCompilerBabelConfig,\n updateAstroConfig,\n updateIntlayerConfigWithSyncPlugin,\n updateMetroConfig,\n updateNextConfig,\n updateNextConfigForNextI18next,\n updateNextConfigForNextIntl,\n updateNextConfigForNextTranslate,\n updateNuxtConfig,\n updateNuxtConfigForNuxtjsI18n,\n updateViteConfig,\n updateViteConfigForCompatPlugin,\n upgradePackages,\n writeFileToRoot,\n} from './utils';\n\n/**\n * Documentation URL Constants\n */\nconst DocumentationRouter = {\n NextJS: 'https://intlayer.org/doc/environment/nextjs.md',\n NextJS_15: 'https://intlayer.org/doc/environment/nextjs/15.md',\n NextJS_14: 'https://intlayer.org/doc/environment/nextjs/14.md',\n CRA: 'https://intlayer.org/doc/environment/create-react-app.md',\n Astro: 'https://intlayer.org/doc/environment/astro.md',\n ViteAndReact: 'https://intlayer.org/doc/environment/vite-and-react.md',\n ViteAndReact_ReactRouterV7:\n 'https://intlayer.org/doc/environment/vite-and-react/react-router-v7.md',\n ViteAndReact_ReactRouterV7_FSRoutes:\n 'https://intlayer.org/doc/environment/vite-and-react/react-router-v7-fs-routes.md',\n ViteAndVue: 'https://intlayer.org/doc/environment/vite-and-vue.md',\n ViteAndSolid: 'https://intlayer.org/doc/environment/vite-and-solid.md',\n ViteAndSvelte: 'https://intlayer.org/doc/environment/vite-and-svelte.md',\n ViteAndPreact: 'https://intlayer.org/doc/environment/vite-and-preact.md',\n TanStackRouter: 'https://intlayer.org/doc/environment/tanstack-start.md',\n NuxtAndVue: 'https://intlayer.org/doc/environment/nuxt-and-vue.md',\n Angular: 'https://intlayer.org/doc/environment/angular.md',\n SvelteKit: 'https://intlayer.org/doc/environment/sveltekit.md',\n ReactNativeAndExpo:\n 'https://intlayer.org/doc/environment/react-native-and-expo.md',\n Lynx: 'https://intlayer.org/doc/environment/lynx-and-react.md',\n Express: 'https://intlayer.org/doc/environment/express.md',\n NestJS: 'https://intlayer.org/doc/environment/nest.md',\n Fastify: 'https://intlayer.org/doc/environment/fastify.md',\n Hono: 'https://intlayer.org/doc/environment/hono.md',\n AdonisJS: 'https://intlayer.org/doc/environment/adonisjs.md',\n Elysia: 'https://intlayer.org/doc/environment/elysia.md',\n Default: 'https://intlayer.org/doc/get-started',\n\n // Intlayer Language Server (Go-to-Definition from getter keys to .content files)\n LSP: 'https://intlayer.org/doc/lsp.md',\n\n // Lint rules for hardcoded text and non-optimizable dynamic calls\n ESLint: 'https://intlayer.org/doc/eslint.md',\n\n // Check for competitors libs\n NextIntl: 'https://intlayer.org/blog/intlayer-with-next-intl.md',\n ReactI18Next: 'https://intlayer.org/blog/intlayer-with-react-i18next.md',\n ReactIntl: 'https://intlayer.org/blog/intlayer-with-react-intl.md',\n NextI18Next: 'https://intlayer.org/blog/intlayer-with-next-i18next.md',\n VueI18n: 'https://intlayer.org/blog/intlayer-with-vue-i18n.md',\n};\n\n/**\n * The Intlayer lint setup, as `intlayer init` writes it into `.oxlintrc.json`.\n *\n * Declared locally on purpose: `@intlayer/engine` is installed by every Intlayer\n * user, so it must not depend on a lint plugin to write a handful of strings\n * into a JSON file. oxlint resolves a plugin's *rules* but not its shipped\n * *configs*, so the preset cannot be referenced by name there either.\n *\n * Mirrors the `recommended` preset of `eslint-plugin-intlayer`; keep in sync\n * when a rule is enabled by default there.\n */\nconst LINT_PLUGIN_PACKAGE_NAME = 'eslint-plugin-intlayer';\n\n/** Intlayer's generated output — never source, never worth linting. */\nconst GENERATED_OUTPUT_GLOBS = ['**/.intlayer/**'];\n\n/** Rules of the `recommended` preset, prefixed by the plugin namespace. */\nconst getRecommendedRules = (): Record<string, string> => ({\n 'intlayer/no-raw-text': 'warn',\n 'intlayer/static-dictionary-key': 'error',\n 'intlayer/no-dynamic-field-access': 'error',\n});\n\n/**\n * Helper: Detects the environment and returns the doc URL\n */\nconst getDocumentationUrl = (packageJson: any): string => {\n const deps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n\n /**\n * Helper to check if a version string matches a specific major version\n * Matches: \"15\", \"^15.0.0\", \"~15.2\", \"15.0.0-beta\"\n */\n const isVersion = (versionString: string, major: number): boolean => {\n if (!versionString || typeof versionString !== 'string') return false;\n const regex = new RegExp(`^[\\\\^~]?${major}(?:\\\\.|$)`);\n return regex.test(versionString);\n };\n\n // Mobile / Cross-platform\n if (deps['@lynx-js/react'] || deps['@lynx-js/core']) {\n return DocumentationRouter.Lynx;\n }\n if (deps['react-native'] || deps.expo) {\n return DocumentationRouter.ReactNativeAndExpo;\n }\n\n // Meta-frameworks (Next, Nuxt, Astro, SvelteKit)\n if (deps.next) {\n const version = deps.next;\n\n if (isVersion(version, 14)) {\n return DocumentationRouter.NextJS_14;\n }\n\n if (isVersion(version, 15)) {\n return DocumentationRouter.NextJS_15;\n }\n\n return DocumentationRouter.NextJS;\n }\n\n if (deps.nuxt) return DocumentationRouter.NuxtAndVue;\n if (deps.astro) return DocumentationRouter.Astro;\n if (deps['@sveltejs/kit']) return DocumentationRouter.SvelteKit;\n\n // Routers (TanStack & React Router v7)\n if (deps['@tanstack/react-router']) {\n return DocumentationRouter.TanStackRouter;\n }\n\n // Check for React Router v7\n const reactRouterVersion = deps['react-router'];\n if (reactRouterVersion && typeof reactRouterVersion === 'string') {\n // Distinguish between standard v7 and v7 with FS routes\n if (deps['@react-router/fs-routes']) {\n return DocumentationRouter.ViteAndReact_ReactRouterV7_FSRoutes;\n }\n\n // Use Regex to ensure it is v7\n if (isVersion(reactRouterVersion, 7)) {\n return DocumentationRouter.ViteAndReact_ReactRouterV7;\n }\n }\n\n // Vite Ecosystem (General)\n if (deps.vite) {\n if (deps.vue) return DocumentationRouter.ViteAndVue;\n if (deps['solid-js']) return DocumentationRouter.ViteAndSolid;\n if (deps.svelte) return DocumentationRouter.ViteAndSvelte;\n if (deps.preact) return DocumentationRouter.ViteAndPreact;\n\n // Default to React if Vite is present but specific other frameworks aren't found\n return DocumentationRouter.ViteAndReact;\n }\n\n // Other Web Frameworks\n if (deps['react-scripts']) return DocumentationRouter.CRA;\n if (deps['@angular/core']) return DocumentationRouter.Angular;\n\n // Backend\n // NestJS first: it runs on top of Express (or Fastify), so both dependencies\n // are present and the more specific one has to win.\n if (deps['@nestjs/core']) return DocumentationRouter.NestJS;\n if (deps['@adonisjs/core']) return DocumentationRouter.AdonisJS;\n if (deps.elysia) return DocumentationRouter.Elysia;\n if (deps.hono) return DocumentationRouter.Hono;\n if (deps.express) return DocumentationRouter.Express;\n if (deps.fastify) return DocumentationRouter.Fastify;\n\n // Competitor Libs (Migration Guides)\n // We check these last as specific environment setup is usually higher priority,\n // but if no specific framework logic matched (or as a fallback), we guide to migration.\n if (deps['next-intl']) return DocumentationRouter.NextIntl;\n if (deps['react-i18next'] || deps.i18next)\n return DocumentationRouter.ReactI18Next;\n if (deps['react-intl']) return DocumentationRouter.ReactIntl;\n if (deps['next-i18next']) return DocumentationRouter.NextI18Next;\n if (deps['vue-i18n']) return DocumentationRouter.VueI18n;\n\n return DocumentationRouter.Default;\n};\n\n/**\n * Resolves the default dev-server URL of the detected framework, used as the\n * `editor.applicationURL` of the generated configuration file. Frameworks that\n * embed Vite (Astro, SvelteKit, …) are checked before the bare Vite fallback so\n * their own dev port wins.\n */\nexport const getDefaultApplicationURL = (\n dependencies: Record<string, string>\n): string => {\n if (dependencies.next) return 'http://localhost:3000';\n if (dependencies.astro) return 'http://localhost:4321';\n if (dependencies['@angular/core']) return 'http://localhost:4200';\n if (dependencies.nuxt) return 'http://localhost:3000';\n if (dependencies['@sveltejs/kit']) return 'http://localhost:5173';\n if (dependencies['react-scripts']) return 'http://localhost:3000';\n if (dependencies.vite) return 'http://localhost:5173';\n\n return 'http://localhost:3000';\n};\n\n/**\n * How the interactive init organizes autogenerated content (compiler output /\n * autofill):\n * - `per-component` — multilingual `.content` files co-located with components\n * (the configuration template default).\n * - `centralized` — per-locale JSON dictionaries under `/locales/{locale}/`.\n * - `json-namespaces` — plain per-locale JSON namespace files kept in sync\n * through `@intlayer/sync-json-plugin`.\n */\nexport type ContentStrategy =\n | 'per-component'\n | 'centralized'\n | 'json-namespaces';\n\n/**\n * OPTIONS\n */\nexport type InitOptions = {\n noGitignore?: boolean;\n /** Skip scaffolding the `fill` and `test` GitHub Actions workflows. */\n noGithubActions?: boolean;\n /**\n * Skip installing missing Intlayer dependencies and upgrading outdated ones.\n */\n noInstallPackages?: boolean;\n /** Skip adding the Intlayer extension to `.vscode/extensions.json`. */\n noVscodeExtension?: boolean;\n /** Skip writing the Intlayer LSP configuration to `.vscode/settings.json`. */\n noLsp?: boolean;\n /** Skip enabling the Intlayer lint rules (ESLint / oxlint). */\n noEslint?: boolean;\n /**\n * Skip framework-specific scaffolding (middleware/proxy, providers in\n * layout/page, example content). Defaults to enabled.\n */\n noFrameworkSetup?: boolean;\n /**\n * Version that outdated Intlayer packages should be upgraded to (typically the\n * running CLI version). When omitted, installed packages are left untouched and\n * only missing ones are installed.\n */\n upgradeToVersion?: string;\n /**\n * Locale routing strategy to write to `routing.mode` in the generated/existing\n * Intlayer configuration file. When omitted, the configuration default\n * (`prefix-no-default`) is kept.\n */\n routingMode?: RoutingMode;\n /**\n * Content organization strategy chosen by the interactive init prompt. Drives\n * the `compiler.output` template (`centralized`) or the injection of the\n * syncJSON plugin (`json-namespaces`). When omitted (or `per-component`), the\n * configuration template default is kept.\n */\n contentStrategy?: ContentStrategy;\n};\n\n/**\n * MAIN LOGIC\n */\nexport const initIntlayer = async (rootDir: string, options?: InitOptions) => {\n logger(colorize('Checking Intlayer configuration...', ANSIColors.CYAN));\n\n // READ PACKAGE.JSON\n const packageJsonPath = 'package.json';\n if (!(await exists(rootDir, packageJsonPath))) {\n logger(\n `${x} No ${colorizePath('package.json')} found. Please run this script from the project root.`,\n { level: 'error' }\n );\n process.exit(1);\n }\n\n const packageJsonContent = await readFileFromRoot(rootDir, packageJsonPath);\n let packageJson: Record<string, any>;\n try {\n packageJson = JSON.parse(packageJsonContent);\n } catch {\n logger(`${x} Could not parse ${colorizePath('package.json')}.`, {\n level: 'error',\n });\n process.exit(1);\n }\n\n // Determine the correct documentation URL based on dependencies\n const guideUrl = getDocumentationUrl(packageJson);\n\n const allDeps: Record<string, string> = {\n ...(packageJson.dependencies ?? {}),\n ...(packageJson.devDependencies ?? {}),\n };\n\n // INSTALL MISSING INTLAYER DEPENDENCIES\n const packageManager = detectPackageManager(rootDir);\n\n // lingui keeps its catalogs as `.po` (default) or `.json`. Detect which so the\n // matching sync plugin (`syncPO` / `syncJSON`) + dev dependency are chosen.\n const linguiPresent = Boolean(\n allDeps['@lingui/core'] ||\n allDeps['@lingui/react'] ||\n allDeps['@intlayer/lingui']\n );\n\n const linguiCatalog = linguiPresent\n ? await detectLinguiCatalogPattern(rootDir)\n : null;\n\n // `linguiCatalogFormat` resolution:\n // 1. Filesystem scan result (authoritative when catalogs already exist)\n // 2. Fallback: 'po' (lingui's default) when lingui is installed but no\n // catalogs have been generated yet.\n const linguiCatalogFormat: 'po' | 'json' | null =\n linguiCatalog?.format ?? (linguiPresent ? 'po' : null);\n\n const {\n packagesToInstall,\n devPackagesToInstall,\n compatSyncConfig,\n compatVitePluginConfig,\n } = detectMissingIntlayerPackages(allDeps, {\n linguiCatalogFormat,\n });\n\n // The `json-namespaces` content strategy uses the same syncJSON pipeline as\n // the compat libraries: plain per-locale JSON namespace files synced through\n // `@intlayer/sync-json-plugin`. A detected compat library keeps precedence —\n // its own catalog format/template is authoritative.\n const wantsJsonNamespaces =\n options?.contentStrategy === 'json-namespaces' && !compatSyncConfig;\n\n if (\n wantsJsonNamespaces &&\n !allDeps['@intlayer/sync-json-plugin'] &&\n !devPackagesToInstall.includes('@intlayer/sync-json-plugin')\n ) {\n devPackagesToInstall.push('@intlayer/sync-json-plugin');\n }\n\n if (!options?.noInstallPackages) {\n const withVersion = (packages: string[]): string[] =>\n options?.upgradeToVersion\n ? packages.map((pkg) => `${pkg}@${options.upgradeToVersion}`)\n : packages;\n\n if (packagesToInstall.length > 0) {\n logger(\n colorize('Installing missing Intlayer dependencies...', ANSIColors.CYAN)\n );\n try {\n installPackages(\n rootDir,\n withVersion(packagesToInstall),\n packageManager\n );\n logger(\n `${v} Installed: ${packagesToInstall.map((pkg) => colorize(pkg, ANSIColors.MAGENTA)).join(', ')}`\n );\n } catch {\n logger(\n `${x} Failed to install packages. Please install manually: ${packagesToInstall.join(' ')}`,\n { level: 'warn' }\n );\n }\n }\n\n if (devPackagesToInstall.length > 0) {\n logger(\n colorize(\n 'Installing missing Intlayer dev dependencies...',\n ANSIColors.CYAN\n )\n );\n try {\n installPackages(\n rootDir,\n withVersion(devPackagesToInstall),\n packageManager,\n true\n );\n logger(\n `${v} Installed: ${devPackagesToInstall.map((pkg) => colorize(pkg, ANSIColors.MAGENTA)).join(', ')}`\n );\n } catch {\n logger(\n `${x} Failed to install dev packages. Please install manually: ${devPackagesToInstall.join(' ')}`,\n { level: 'warn' }\n );\n }\n }\n\n // UPGRADE OUTDATED INTLAYER DEPENDENCIES\n // Only runs when a target version is provided (typically the running CLI\n // version). Already up-to-date packages are skipped. Prod and dev\n // dependencies are upgraded separately so their dependency type is kept.\n if (options?.upgradeToVersion) {\n const outdatedDependencies = detectOutdatedIntlayerPackages(\n rootDir,\n packageJson.dependencies ?? {},\n options.upgradeToVersion\n );\n const outdatedDevDependencies = detectOutdatedIntlayerPackages(\n rootDir,\n packageJson.devDependencies ?? {},\n options.upgradeToVersion\n );\n\n const allOutdated = [...outdatedDependencies, ...outdatedDevDependencies];\n\n if (allOutdated.length > 0) {\n logger(\n colorize(\n `Upgrading outdated Intlayer dependencies to ${options.upgradeToVersion}...`,\n ANSIColors.CYAN\n )\n );\n try {\n upgradePackages(\n rootDir,\n outdatedDependencies,\n packageManager,\n options.upgradeToVersion\n );\n upgradePackages(\n rootDir,\n outdatedDevDependencies,\n packageManager,\n options.upgradeToVersion,\n true\n );\n logger(\n `${v} Upgraded: ${allOutdated.map((pkg) => colorize(pkg, ANSIColors.MAGENTA)).join(', ')}`\n );\n } catch {\n logger(\n `${x} Failed to upgrade packages. Please upgrade manually: ${allOutdated.join(' ')}`,\n { level: 'warn' }\n );\n }\n } else {\n logger(`${v} Intlayer dependencies are up to date`);\n }\n }\n }\n\n // CHECK .GITIGNORE\n const gitignorePath = '.gitignore';\n if (!options?.noGitignore && (await exists(rootDir, gitignorePath))) {\n const gitignoreContent = await readFileFromRoot(rootDir, gitignorePath);\n\n if (!gitignoreContent.includes('intlayer')) {\n const newContent = `${gitignoreContent}\\n# Intlayer\\n.intlayer\\n`;\n await writeFileToRoot(rootDir, gitignorePath, newContent);\n logger(\n `${v} Added ${colorizePath('.intlayer')} to ${colorizePath(gitignorePath)}`\n );\n } else {\n logger(`${v} ${colorizePath(gitignorePath)} already includes .intlayer`);\n }\n }\n\n // SCAFFOLD GITHUB ACTIONS WORKFLOWS (fill + test)\n // Generate two workflows whose commands match the detected package manager:\n // - intlayer-fill.yml: auto-fills missing translations on pull requests\n // - intlayer-test.yml: fails the PR when required locales are missing\n // GitHub only triggers workflows stored at the repository root, so in a\n // monorepo the files are written to the git root and the commands go through\n // `intlayer ci`, which iterates over every Intlayer project of the\n // repository (see resolveGithubWorkflowsContext).\n if (!options?.noGithubActions) {\n const workflowsContext = await resolveGithubWorkflowsContext(\n rootDir,\n packageManager\n );\n const workflows = getGithubWorkflows(\n workflowsContext.packageManager,\n workflowsContext.options\n );\n\n for (const workflow of workflows) {\n if (await exists(workflowsContext.workflowsRootDir, workflow.filePath)) {\n logger(`${v} ${colorizePath(workflow.filePath)} already exists`);\n continue;\n }\n\n try {\n await ensureDirectory(\n workflowsContext.workflowsRootDir,\n join('.github', 'workflows')\n );\n await writeFileToRoot(\n workflowsContext.workflowsRootDir,\n workflow.filePath,\n workflow.content\n );\n logger(\n `${v} Added GitHub Actions workflow ${colorizePath(workflow.filePath)}`\n );\n } catch {\n logger(\n `${x} Could not create ${colorizePath(workflow.filePath)}. You may need to add it manually.`,\n { level: 'warn' }\n );\n }\n }\n }\n\n // CHECK VS CODE EXTENSION RECOMMENDATIONS\n const vscodeDir = '.vscode';\n const extensionsJsonPath = join(vscodeDir, 'extensions.json');\n const extensionId = 'intlayer.intlayer-vs-code-extension';\n\n if (!options?.noVscodeExtension) {\n try {\n let extensionsConfig: { recommendations: string[] } = {\n recommendations: [],\n };\n\n if (await exists(rootDir, extensionsJsonPath)) {\n const content = await readFileFromRoot(rootDir, extensionsJsonPath);\n extensionsConfig = parseJSONWithComments(content);\n } else {\n await ensureDirectory(rootDir, vscodeDir);\n }\n\n if (!extensionsConfig.recommendations) {\n extensionsConfig.recommendations = [];\n }\n\n if (!extensionsConfig.recommendations.includes(extensionId)) {\n extensionsConfig.recommendations.push(extensionId);\n await writeFileToRoot(\n rootDir,\n extensionsJsonPath,\n JSON.stringify(extensionsConfig, null, 2)\n );\n logger(\n `${v} Added ${colorize(extensionId, ANSIColors.MAGENTA)} to ${colorizePath(extensionsJsonPath)}`\n );\n } else {\n logger(\n `${v} ${colorizePath(extensionsJsonPath)} already includes ${colorize(extensionId, ANSIColors.MAGENTA)}`\n );\n }\n } catch {\n logger(\n `${x} Could not update ${colorizePath(extensionsJsonPath)}. You may need to add ${colorize(extensionId, ANSIColors.MAGENTA)} manually.`,\n { level: 'warn' }\n );\n }\n }\n\n // CHECK VS CODE LSP SETTINGS\n const settingsJsonPath = join(vscodeDir, 'settings.json');\n\n if (!options?.noLsp) {\n try {\n let settingsConfig: Record<string, unknown> = {};\n\n if (await exists(rootDir, settingsJsonPath)) {\n const content = await readFileFromRoot(rootDir, settingsJsonPath);\n settingsConfig = parseJSONWithComments(content);\n } else {\n await ensureDirectory(rootDir, vscodeDir);\n }\n\n let settingsUpdated = false;\n\n if (!settingsConfig['intlayer.languageServer.command']) {\n settingsConfig['intlayer.languageServer.command'] = 'npx';\n settingsUpdated = true;\n }\n\n if (!settingsConfig['intlayer.languageServer.args']) {\n settingsConfig['intlayer.languageServer.args'] = ['@intlayer/lsp'];\n settingsUpdated = true;\n }\n\n if (settingsUpdated) {\n await writeFileToRoot(\n rootDir,\n settingsJsonPath,\n JSON.stringify(settingsConfig, null, 2)\n );\n logger(\n `${v} Updated ${colorizePath(settingsJsonPath)} with LSP configuration`\n );\n } else {\n logger(\n `${v} ${colorizePath(settingsJsonPath)} already includes LSP configuration`\n );\n }\n } catch {\n logger(\n `${x} Could not update ${colorizePath(settingsJsonPath)}. You may need to add the LSP settings manually.`,\n { level: 'warn' }\n );\n }\n }\n\n // CHECK LINT RULES (eslint-plugin-intlayer)\n //\n // Gated on the project already having a linter installed: with no ESLint or\n // oxlint there is nothing to plug the rules into, and the guidance below\n // would just be noise. The same check gates the install in\n // `detectMissingIntlayerPackages`, so the two never disagree.\n //\n // Only the oxlint config is patched here: `.oxlintrc.json` is plain JSON and\n // safe to edit, whereas an ESLint flat config is arbitrary JavaScript that\n // cannot be rewritten reliably — that one is left to the user, with the\n // snippet printed below.\n if (!options?.noEslint && hasLintTooling(allDeps)) {\n const oxlintConfigPath = '.oxlintrc.json';\n const hasOxlintConfig = await exists(rootDir, oxlintConfigPath);\n\n if (hasOxlintConfig) {\n try {\n const content = await readFileFromRoot(rootDir, oxlintConfigPath);\n const oxlintConfig: {\n jsPlugins?: string[];\n rules?: Record<string, unknown>;\n ignorePatterns?: string[];\n } = parseJSONWithComments(content);\n\n oxlintConfig.jsPlugins ??= [];\n oxlintConfig.rules ??= {};\n oxlintConfig.ignorePatterns ??= [];\n\n let oxlintUpdated = false;\n\n // Generated dictionaries are not source; linting them is wasted work.\n for (const glob of GENERATED_OUTPUT_GLOBS) {\n if (!oxlintConfig.ignorePatterns.includes(glob)) {\n oxlintConfig.ignorePatterns.push(glob);\n oxlintUpdated = true;\n }\n }\n\n if (!oxlintConfig.jsPlugins.includes(LINT_PLUGIN_PACKAGE_NAME)) {\n oxlintConfig.jsPlugins.push(LINT_PLUGIN_PACKAGE_NAME);\n oxlintUpdated = true;\n }\n\n for (const [ruleName, severity] of Object.entries(\n getRecommendedRules()\n )) {\n if (!oxlintConfig.rules[ruleName]) {\n oxlintConfig.rules[ruleName] = severity;\n oxlintUpdated = true;\n }\n }\n\n if (oxlintUpdated) {\n await writeFileToRoot(\n rootDir,\n oxlintConfigPath,\n JSON.stringify(oxlintConfig, null, 2)\n );\n logger(\n `${v} Added the Intlayer lint rules to ${colorizePath(oxlintConfigPath)}`\n );\n } else {\n logger(\n `${v} ${colorizePath(oxlintConfigPath)} already includes the Intlayer lint rules`\n );\n }\n } catch {\n logger(\n `${x} Could not update ${colorizePath(oxlintConfigPath)}. You may need to add ${colorize(LINT_PLUGIN_PACKAGE_NAME, ANSIColors.MAGENTA)} manually.`,\n { level: 'warn' }\n );\n }\n } else {\n // ESLint flat config — print the snippet instead of rewriting the file.\n logger([\n colorize('Intlayer lint rules →', ANSIColors.MAGENTA),\n colorize(\n `Add ${LINT_PLUGIN_PACKAGE_NAME} to your ESLint flat config to catch hardcoded text and dynamic calls the compiler cannot optimize:`,\n ANSIColors.GREY_LIGHT\n ),\n colorize(\n ` import intlayer from '${LINT_PLUGIN_PACKAGE_NAME}';\\n export default [...intlayer.configs.recommended];`,\n ANSIColors.GREY_LIGHT\n ),\n colorizePath(DocumentationRouter.ESLint),\n ]);\n }\n }\n\n // CHECK TSCONFIGS\n const tsConfigFiles = await findTsConfigFiles(rootDir);\n let hasTsConfig = false;\n\n for (const fileName of tsConfigFiles) {\n if (await exists(rootDir, fileName)) {\n hasTsConfig = true;\n try {\n const fileContent = await readFileFromRoot(rootDir, fileName);\n const config = parseJSONWithComments(fileContent);\n const typeDefinition = './.intlayer/**/*.ts';\n\n let updated = false;\n\n if (!config.include) {\n // Skip if no include array (solution-style)\n } else if (\n Array.isArray(config.include) &&\n !(config.include as string[]).some((pattern: string) =>\n pattern.includes('.intlayer')\n )\n ) {\n config.include.push(typeDefinition);\n updated = true;\n } else if (config.include.includes(typeDefinition)) {\n logger(\n `${v} ${colorizePath(fileName)} already includes intlayer types`\n );\n }\n\n if (updated) {\n await writeFileToRoot(\n rootDir,\n fileName,\n JSON.stringify(config, null, 2)\n );\n logger(\n `${v} Updated ${colorizePath(fileName)} to include intlayer types`\n );\n }\n } catch {\n logger(\n `${x} Could not parse or update ${colorizePath(fileName)}. You may need to add ${colorizePath('.intlayer/types/**/*.ts')} manually.`,\n { level: 'warn' }\n );\n }\n }\n }\n\n // INITIALIZE CONFIG FILE\n const format = hasTsConfig ? 'intlayer.config.ts' : 'intlayer.config.mjs';\n\n // Detect the locale JSON file pattern already in the project so we can\n // insert the matching locales into the config and produce the most\n // accurate source template for compat libraries.\n const detectedPattern = await detectJsonLocalePattern(rootDir);\n\n await initConfig(format, rootDir, {\n locales: detectedPattern?.locales,\n applicationURL: getDefaultApplicationURL(allDeps),\n });\n\n // APPLY CONTENT STRATEGY — CENTRALIZED\n // Point `compiler.output` at per-locale JSON dictionaries under `/locales/`\n // instead of the per-component template default.\n if (options?.contentStrategy === 'centralized') {\n await setCompilerOutputInConfig(\n rootDir,\n '/locales/{{locale}}/{{key}}.content.json'\n );\n }\n\n // INJECT SYNC-JSON PLUGIN FOR COMPAT LIBRARIES / JSON-NAMESPACES STRATEGY\n const syncConfigToInject: CompatSyncConfig | undefined =\n compatSyncConfig ??\n (wantsJsonNamespaces\n ? {\n format: 'i18next',\n sourceTemplate:\n detectedPattern?.template ?? './locales/${locale}/${key}.json',\n }\n : undefined);\n\n if (syncConfigToInject) {\n // For next-intl / use-intl, the messages path is authoritatively declared\n // in `i18n/request.ts` (e.g. `import(`../messages/${locale}.json`)`), so we\n // read it directly. It usually resolves to a single file per locale (no\n // `${key}` segment), whose first-level keys are namespaces — so the compat\n // config carries `splitKeys: true` to emit one dictionary per namespace.\n // Falls back to file-system globbing otherwise.\n const nextIntlMessagesPattern =\n allDeps['next-intl'] ||\n allDeps['@intlayer/next-intl'] ||\n allDeps['use-intl'] ||\n allDeps['@intlayer/use-intl']\n ? await detectNextIntlMessagesPattern(rootDir)\n : null;\n\n // lingui authoritatively resolves to `…/{locale}/messages.{po,json}`, where\n // the `messages` filename becomes the dictionary key. Prefer it so a `.po`\n // project gets `syncPO` (the generic JSON glob only sees `.json`).\n const sourceTemplate =\n linguiCatalog?.template ??\n nextIntlMessagesPattern?.template ??\n detectedPattern?.template;\n\n const resolvedSyncConfig = {\n ...syncConfigToInject,\n ...(sourceTemplate ? { sourceTemplate } : {}),\n };\n\n const syncPluginPackage =\n resolvedSyncConfig.plugin === 'po'\n ? '@intlayer/sync-po-plugin'\n : '@intlayer/sync-json-plugin';\n const syncPluginName =\n resolvedSyncConfig.plugin === 'po' ? 'syncPO' : 'syncJSON';\n\n // `splitKeys` only makes sense for a single file holding several namespaces.\n // If the resolved template addresses one namespace per file (`${key}`\n // segment), each file is already a single namespace — drop the flag so\n // syncJSON keeps one dictionary per file.\n if (\n resolvedSyncConfig.splitKeys &&\n resolvedSyncConfig.sourceTemplate.includes('${key}')\n ) {\n resolvedSyncConfig.splitKeys = false;\n }\n\n const intlayerConfigCandidates = [\n 'intlayer.config.ts',\n 'intlayer.config.mjs',\n 'intlayer.config.js',\n 'intlayer.config.cjs',\n ];\n\n for (const configFile of intlayerConfigCandidates) {\n if (await exists(rootDir, configFile)) {\n const configContent = await readFileFromRoot(rootDir, configFile);\n\n if (!configContent.includes(syncPluginPackage)) {\n const extension = configFile.split('.').pop()!;\n const updatedConfigContent = updateIntlayerConfigWithSyncPlugin(\n configContent,\n extension,\n resolvedSyncConfig\n );\n await writeFileToRoot(rootDir, configFile, updatedConfigContent);\n logger(\n `${v} Updated ${colorizePath(configFile)} with ${syncPluginName} compat plugin`\n );\n } else {\n logger(\n `${v} ${colorizePath(configFile)} already includes ${syncPluginName} plugin`\n );\n }\n break;\n }\n }\n }\n\n // APPLY ROUTING MODE\n // When a routing strategy was chosen (interactive init), write it to\n // `routing.mode` in the configuration file. Idempotent and comment-preserving.\n if (options?.routingMode) {\n await setRoutingModeInConfig(rootDir, options.routingMode);\n }\n\n let hasAliasConfiguration = false;\n\n // CHECK VITE CONFIG\n const viteConfigs = ['vite.config.ts', 'vite.config.js', 'vite.config.mjs'];\n\n for (const file of viteConfigs) {\n if (await exists(rootDir, file)) {\n hasAliasConfiguration = true;\n const content = await readFileFromRoot(rootDir, file);\n const extension = file.split('.').pop()!;\n\n if (compatVitePluginConfig) {\n const { replacesVitePlugin } = compatVitePluginConfig;\n\n if (content.includes(compatVitePluginConfig.pluginPackageSource)) {\n logger(\n `${v} ${colorizePath(file)} already includes ${compatVitePluginConfig.pluginPackageSource}`\n );\n } else if (\n replacesVitePlugin &&\n content.includes(replacesVitePlugin.fromPackageSource)\n ) {\n // Drop-in replacement: rewrite the original i18n vite plugin's import\n // source, keeping its binding and call site (e.g. lingui).\n const updatedContent = replaceViteConfigPluginImportSource(\n content,\n replacesVitePlugin.importName,\n replacesVitePlugin.fromPackageSource,\n compatVitePluginConfig.pluginPackageSource\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to import ${replacesVitePlugin.importName} from ${compatVitePluginConfig.pluginPackageSource}`\n );\n } else {\n const updatedContent = updateViteConfigForCompatPlugin(\n content,\n extension,\n compatVitePluginConfig\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include ${compatVitePluginConfig.pluginFunctionName} compat plugin`\n );\n }\n } else if (hasIntlayerVitePlugin(content)) {\n // A compat adapter plugin (e.g. `reactI18nextVitePlugin`) already wraps\n // `intlayer()` internally, so skip adding a redundant standalone plugin.\n logger(\n `${v} ${colorizePath(file)} already includes an Intlayer plugin`\n );\n } else {\n const updatedContent = updateViteConfig(content, extension);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(`${v} Updated ${colorizePath(file)} to include Intlayer plugin`);\n }\n break;\n }\n }\n\n // CHECK NEXT CONFIG\n const nextConfigs = ['next.config.js', 'next.config.mjs', 'next.config.ts'];\n let isNextJsProject = false;\n\n for (const file of nextConfigs) {\n if (await exists(rootDir, file)) {\n isNextJsProject = true;\n hasAliasConfiguration = true;\n const content = await readFileFromRoot(rootDir, file);\n const extension = file.split('.').pop()!;\n\n if (allDeps['next-i18next']) {\n if (!content.includes('@intlayer/next-i18next')) {\n const updatedContent = updateNextConfigForNextI18next(\n content,\n extension\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include Intlayer next-i18next compat plugin`\n );\n } else {\n logger(\n `${v} ${colorizePath(file)} already includes @intlayer/next-i18next`\n );\n }\n } else if (allDeps['next-intl']) {\n if (!content.includes('@intlayer/next-intl/plugin')) {\n const updatedContent = updateNextConfigForNextIntl(\n content,\n extension\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include Intlayer next-intl compat plugin`\n );\n } else {\n logger(\n `${v} ${colorizePath(file)} already includes @intlayer/next-intl/plugin`\n );\n }\n } else if (allDeps['next-translate']) {\n if (!content.includes('@intlayer/next-translate')) {\n const updatedContent = updateNextConfigForNextTranslate(\n content,\n extension\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include Intlayer next-translate compat plugin`\n );\n } else {\n logger(\n `${v} ${colorizePath(file)} already includes @intlayer/next-translate`\n );\n }\n } else if (!content.includes('next-intlayer')) {\n const updatedContent = updateNextConfig(content, extension);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(`${v} Updated ${colorizePath(file)} to include Intlayer plugin`);\n }\n break;\n }\n }\n\n // CHECK NEXT.JS COMPILER (BABEL)\n // When a Next.js project already uses Babel, install @intlayer/babel and print\n // the compiler + build-optimization plugins (extract, purge, minify, optimize)\n // to add. SWC projects need no setup: withIntlayer injects @intlayer/swc.\n if (isNextJsProject) {\n await setupNextCompilerBabelConfig({\n rootDir,\n packageManager,\n allDeps,\n skipInstall: Boolean(options?.noInstallPackages),\n });\n }\n\n // CHECK OTHER FRAMEWORKS CONFIG\n const astroConfigs = [\n 'astro.config.mjs',\n 'astro.config.js',\n 'astro.config.ts',\n 'astro.config.cjs',\n ];\n\n for (const file of astroConfigs) {\n if (await exists(rootDir, file)) {\n hasAliasConfiguration = true;\n\n if (file.startsWith('astro.config.')) {\n const content = await readFileFromRoot(rootDir, file);\n\n if (!content.includes('astro-intlayer')) {\n const extension = file.split('.').pop()!;\n const updatedContent = updateAstroConfig(content, extension);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include Intlayer integration`\n );\n }\n }\n break;\n }\n }\n\n const nuxtConfigs = ['nuxt.config.js', 'nuxt.config.ts'];\n for (const file of nuxtConfigs) {\n if (await exists(rootDir, file)) {\n hasAliasConfiguration = true;\n\n const content = await readFileFromRoot(rootDir, file);\n\n if (allDeps['@nuxtjs/i18n']) {\n if (!content.includes('@intlayer/nuxtjs-i18n')) {\n const updatedContent = updateNuxtConfigForNuxtjsI18n(content);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include @intlayer/nuxtjs-i18n module`\n );\n } else {\n logger(\n `${v} ${colorizePath(file)} already includes @intlayer/nuxtjs-i18n`\n );\n }\n } else if (!content.includes('nuxt-intlayer')) {\n const updatedContent = updateNuxtConfig(content);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(`${v} Updated ${colorizePath(file)} to include Intlayer module`);\n }\n break;\n }\n }\n\n // CHECK METRO CONFIG (React Native / Expo)\n // Metro is the React Native bundler. The Intlayer Metro plugin\n // (`react-native-intlayer/metro`) handles dictionary aliasing and building.\n // When no config exists we scaffold one; when one exists we safely wrap its\n // exported config with `configMetroIntlayerSync`, leaving custom async\n // (IIFE) configs untouched so existing setups are never broken.\n const isReactNativeProject = Boolean(allDeps['react-native'] || allDeps.expo);\n\n if (isReactNativeProject) {\n // Metro resolves Intlayer aliases itself, so skip the alias fallback below.\n hasAliasConfiguration = true;\n\n const metroConfigs = [\n 'metro.config.js',\n 'metro.config.cjs',\n 'metro.config.mjs',\n 'metro.config.ts',\n ];\n\n let metroConfigFile: string | undefined;\n\n for (const file of metroConfigs) {\n if (await exists(rootDir, file)) {\n metroConfigFile = file;\n break;\n }\n }\n\n if (metroConfigFile) {\n const content = await readFileFromRoot(rootDir, metroConfigFile);\n\n if (content.includes('react-native-intlayer')) {\n logger(\n `${v} ${colorizePath(metroConfigFile)} already includes the Intlayer Metro plugin`\n );\n } else {\n const extension = metroConfigFile.split('.').pop()!;\n const updatedContent = updateMetroConfig(content, extension);\n\n if (updatedContent !== content) {\n await writeFileToRoot(rootDir, metroConfigFile, updatedContent);\n logger(\n `${v} Updated ${colorizePath(metroConfigFile)} to include the Intlayer Metro plugin`\n );\n } else {\n logger(\n `${x} Could not automatically update ${colorizePath(metroConfigFile)}. Wrap your exported config with ${colorize('configMetroIntlayer', ANSIColors.MAGENTA)}: ${colorizePath(DocumentationRouter.ReactNativeAndExpo)}`,\n { level: 'warn' }\n );\n }\n }\n } else {\n const newMetroConfigFile = 'metro.config.js';\n await writeFileToRoot(\n rootDir,\n newMetroConfigFile,\n getMetroConfigTemplate(Boolean(allDeps.expo))\n );\n logger(\n `${v} Created ${colorizePath(newMetroConfigFile)} with the Intlayer Metro plugin`\n );\n }\n }\n\n // UPDATE PACKAGE.JSON DEV SCRIPT\n // Only frameworks with no bundler plugin to host the content watcher get\n // their dev server wrapped; a Next.js app is always left alone. See\n // `resolveDevScript`.\n const newDevScript = resolveDevScript({\n devScript: packageJson.scripts?.dev,\n allDeps,\n isNextJsProject,\n });\n\n if (newDevScript) {\n packageJson.scripts.dev = newDevScript;\n\n await writeFileToRoot(\n rootDir,\n packageJsonPath,\n JSON.stringify(packageJson, null, 2)\n );\n\n logger(\n `${v} Updated ${colorizePath('package.json')} dev script to run intlayer watch`\n );\n }\n\n // CHECK WEBPACK CONFIG\n const webpackConfigs = [\n 'webpack.config.js',\n 'webpack.config.ts',\n 'webpack.config.mjs',\n 'webpack.config.cjs',\n ];\n\n for (const file of webpackConfigs) {\n if (await exists(rootDir, file)) {\n hasAliasConfiguration = true;\n logger(\n `${v} Found ${colorizePath(\n file\n )}. Make sure to configure aliases manually or use the Intlayer Webpack plugin.`\n );\n break;\n }\n }\n\n // The server frameworks themselves, alongside their Intlayer packages: a\n // project may be mid-setup and carry only one of the two.\n const backendConfigPackages = [\n 'express',\n 'fastify',\n '@adonisjs/core',\n 'hono',\n 'elysia',\n ...BACKEND_INTLAYER_PACKAGES,\n ];\n\n if (backendConfigPackages.some((pkg) => allDeps[pkg])) {\n hasAliasConfiguration = true;\n }\n\n if (!hasAliasConfiguration) {\n const configuration = getConfiguration({ baseDir: rootDir });\n // `tsconfig`/`jsconfig` `paths` and `package.json` `imports` both expect\n // explicitly relative specifiers, so bare relative paths get a `./` prefix.\n const aliases = getAlias({\n configuration,\n formatter: (value) =>\n value.startsWith('./') || value.startsWith('../')\n ? value\n : `./${value}`,\n });\n\n if (hasTsConfig && tsConfigFiles.length > 0) {\n const tsConfigPath =\n tsConfigFiles.find((file) => file === 'tsconfig.json') ||\n tsConfigFiles[0];\n\n if (tsConfigPath) {\n const tsConfigContent = await readFileFromRoot(rootDir, tsConfigPath);\n const config = parseJSONWithComments(tsConfigContent);\n\n config.compilerOptions ??= {};\n config.compilerOptions.paths ??= {};\n\n let updated = false;\n\n Object.entries(aliases).forEach(([alias, path]) => {\n if (!config.compilerOptions.paths[alias]) {\n config.compilerOptions.paths[alias] = [path];\n updated = true;\n }\n });\n\n if (updated) {\n await writeFileToRoot(\n rootDir,\n tsConfigPath,\n JSON.stringify(config, null, 2)\n );\n\n logger(\n `${v} Updated ${colorizePath(\n tsConfigPath\n )} to include Intlayer aliases`\n );\n }\n }\n } else {\n const jsConfigPath = 'jsconfig.json';\n\n if (await exists(rootDir, jsConfigPath)) {\n const jsConfigContent = await readFileFromRoot(rootDir, jsConfigPath);\n const config = parseJSONWithComments(jsConfigContent);\n\n config.compilerOptions ??= {};\n config.compilerOptions.paths ??= {};\n\n let updated = false;\n\n Object.entries(aliases).forEach(([alias, path]) => {\n if (!config.compilerOptions.paths[alias]) {\n config.compilerOptions.paths[alias] = [path];\n updated = true;\n }\n });\n\n if (updated) {\n await writeFileToRoot(\n rootDir,\n jsConfigPath,\n JSON.stringify(config, null, 2)\n );\n logger(\n `${v} Updated ${colorizePath(\n jsConfigPath\n )} to include Intlayer aliases`\n );\n }\n } else {\n packageJson.imports ??= {};\n\n let updated = false;\n\n Object.entries(aliases).forEach(([alias, path]) => {\n const importAlias = alias.replace('@', '#');\n\n if (!packageJson.imports[importAlias]) {\n packageJson.imports[importAlias] = path;\n updated = true;\n }\n });\n\n if (updated) {\n await writeFileToRoot(\n rootDir,\n packageJsonPath,\n JSON.stringify(packageJson, null, 2)\n );\n logger(\n `${v} Updated ${colorizePath(\n packageJsonPath\n )} to include Intlayer imports`\n );\n }\n }\n }\n }\n\n // FRAMEWORK-SPECIFIC SCAFFOLDING\n // Sets up middleware/proxy and wraps the layout/page with the Intlayer\n // providers for the detected framework (Next.js App Router today). Idempotent\n // and non-destructive: it never overwrites user code it cannot safely\n // transform, skipping with guidance instead.\n //\n // Skipped entirely when a compat i18n library drives the app (i18next,\n // next-intl, vue-i18n, …): the app already owns its routing, providers and\n // layouts, and Intlayer only syncs its catalogs — restructuring routes or\n // injecting providers would break the existing setup.\n const hasCompatLibrary = Boolean(compatSyncConfig || compatVitePluginConfig);\n\n if (!options?.noFrameworkSetup && hasCompatLibrary) {\n logger(\n `${v} Compat i18n library detected — keeping the app structure untouched (no route restructure, no provider injection)`\n );\n }\n\n if (!options?.noFrameworkSetup && !hasCompatLibrary) {\n try {\n // Prefer the user's interactive choice, else the configured routing mode\n // (which itself defaults to `prefix-no-default`). Drives whether the\n // locale path segment is required (`prefix-all`) or optional.\n const routingMode =\n options?.routingMode ??\n getConfiguration({ baseDir: rootDir }).routing.mode;\n\n await setupFramework({\n rootDir,\n allDeps,\n packageManager,\n useTypeScript: hasTsConfig,\n routingMode,\n });\n } catch {\n logger(\n `${x} Framework-specific scaffolding failed. Your existing files were left untouched; follow the documentation to finish the setup.`,\n { level: 'warn' }\n );\n }\n }\n\n // FINAL SUCCESS MESSAGE\n logger(`${v} ${colorize('Intlayer init setup complete.', ANSIColors.GREEN)}`);\n logger([\n colorize('Next →', ANSIColors.MAGENTA),\n colorize(\n `Follow the instructions in the documentation to complete the setup:`,\n ANSIColors.GREY_LIGHT\n ),\n colorizePath(guideUrl),\n ]);\n\n // INTLAYER LSP — instruction for the AI agent running `intlayer init`.\n // The Intlayer Language Server (`@intlayer/lsp`) provides Go-to-Definition\n // from `useIntlayer` / `getIntlayer` keys to their `.content` files, for both\n // IDEs and AI agents. Setup is editor-specific (VS Code, Cursor, Windsurf,\n // Zed, Neovim, Claude Code, …), so we don't scaffold anything here — instead\n // we point to the doc and ask the agent to confirm the target platform.\n logger([\n colorize('Intlayer LSP →', ANSIColors.MAGENTA),\n colorize(\n 'The Intlayer Language Server is available via the `@intlayer/lsp` package. It lets IDEs and AI agents jump from `useIntlayer`/`getIntlayer` keys to their `.content` files.',\n ANSIColors.GREY_LIGHT\n ),\n colorize(\n 'Ask the user which platform/editor they want to enable it on (VS Code, Cursor, Windsurf, Zed, Neovim, Claude Code, …), then follow the matching setup instructions:',\n ANSIColors.GREY_LIGHT\n ),\n colorizePath(DocumentationRouter.LSP),\n ]);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/init/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, logger, v, x } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport { getAlias } from '@intlayer/config/utils';\nimport { initConfig } from '../initConfig';\nimport { setCompilerOutputInConfig, setRoutingModeInConfig } from './cms';\nimport { setupFramework } from './frameworkSetup';\nimport type { CompatSyncConfig, RoutingMode } from './utils';\nimport {\n BACKEND_INTLAYER_PACKAGES,\n detectJsonLocalePattern,\n detectLinguiCatalogPattern,\n detectMissingIntlayerPackages,\n detectNextIntlMessagesPattern,\n detectOutdatedIntlayerPackages,\n detectPackageManager,\n ensureDirectory,\n exists,\n findTsConfigFiles,\n getGithubWorkflows,\n getMetroConfigTemplate,\n hasIntlayerVitePlugin,\n hasLintTooling,\n installPackages,\n parseJSONWithComments,\n readFileFromRoot,\n replaceViteConfigPluginImportSource,\n resolveDevScript,\n resolveGithubWorkflowsContext,\n setupNextCompilerBabelConfig,\n updateAstroConfig,\n updateIntlayerConfigWithSyncPlugin,\n updateMetroConfig,\n updateNextConfig,\n updateNextConfigForNextI18next,\n updateNextConfigForNextIntl,\n updateNextConfigForNextTranslate,\n updateNuxtConfig,\n updateNuxtConfigForNuxtjsI18n,\n updateViteConfig,\n updateViteConfigForCompatPlugin,\n upgradePackages,\n writeFileToRoot,\n} from './utils';\n\n/**\n * Documentation URL Constants\n */\nconst DocumentationRouter = {\n NextJS: 'https://intlayer.org/doc/environment/nextjs.md',\n NextJS_15: 'https://intlayer.org/doc/environment/nextjs/15.md',\n NextJS_14: 'https://intlayer.org/doc/environment/nextjs/14.md',\n CRA: 'https://intlayer.org/doc/environment/create-react-app.md',\n Astro: 'https://intlayer.org/doc/environment/astro.md',\n ViteAndReact: 'https://intlayer.org/doc/environment/vite-and-react.md',\n ViteAndReact_ReactRouterV7:\n 'https://intlayer.org/doc/environment/vite-and-react/react-router-v7.md',\n ViteAndReact_ReactRouterV7_FSRoutes:\n 'https://intlayer.org/doc/environment/vite-and-react/react-router-v7-fs-routes.md',\n ViteAndVue: 'https://intlayer.org/doc/environment/vite-and-vue.md',\n ViteAndSolid: 'https://intlayer.org/doc/environment/vite-and-solid.md',\n ViteAndSvelte: 'https://intlayer.org/doc/environment/vite-and-svelte.md',\n ViteAndPreact: 'https://intlayer.org/doc/environment/vite-and-preact.md',\n TanStackRouter: 'https://intlayer.org/doc/environment/tanstack-start.md',\n NuxtAndVue: 'https://intlayer.org/doc/environment/nuxt-and-vue.md',\n Angular: 'https://intlayer.org/doc/environment/angular.md',\n SvelteKit: 'https://intlayer.org/doc/environment/sveltekit.md',\n ReactNativeAndExpo:\n 'https://intlayer.org/doc/environment/react-native-and-expo.md',\n Lynx: 'https://intlayer.org/doc/environment/lynx-and-react.md',\n Express: 'https://intlayer.org/doc/environment/express.md',\n NestJS: 'https://intlayer.org/doc/environment/nest.md',\n Fastify: 'https://intlayer.org/doc/environment/fastify.md',\n Hono: 'https://intlayer.org/doc/environment/hono.md',\n AdonisJS: 'https://intlayer.org/doc/environment/adonisjs.md',\n Elysia: 'https://intlayer.org/doc/environment/elysia.md',\n Default: 'https://intlayer.org/doc/get-started',\n\n // Intlayer Language Server (Go-to-Definition from getter keys to .content files)\n LSP: 'https://intlayer.org/doc/lsp.md',\n\n // Lint rules for hardcoded text and non-optimizable dynamic calls\n ESLint: 'https://intlayer.org/doc/eslint.md',\n\n // Check for competitors libs\n NextIntl: 'https://intlayer.org/blog/intlayer-with-next-intl.md',\n ReactI18Next: 'https://intlayer.org/blog/intlayer-with-react-i18next.md',\n ReactIntl: 'https://intlayer.org/blog/intlayer-with-react-intl.md',\n NextI18Next: 'https://intlayer.org/blog/intlayer-with-next-i18next.md',\n VueI18n: 'https://intlayer.org/blog/intlayer-with-vue-i18n.md',\n};\n\n/**\n * The Intlayer lint setup, as `intlayer init` writes it into `.oxlintrc.json`.\n *\n * Declared locally on purpose: `@intlayer/engine` is installed by every Intlayer\n * user, so it must not depend on a lint plugin to write a handful of strings\n * into a JSON file. oxlint resolves a plugin's *rules* but not its shipped\n * *configs*, so the preset cannot be referenced by name there either.\n *\n * Mirrors the `recommended` preset of `eslint-plugin-intlayer`; keep in sync\n * when a rule is enabled by default there.\n */\nconst LINT_PLUGIN_PACKAGE_NAME = 'eslint-plugin-intlayer';\n\n/** Intlayer's generated output — never source, never worth linting. */\nconst GENERATED_OUTPUT_GLOBS = ['**/.intlayer/**'];\n\n/** Rules of the `recommended` preset, prefixed by the plugin namespace. */\nconst getRecommendedRules = (): Record<string, string> => ({\n 'intlayer/no-raw-text': 'warn',\n 'intlayer/static-dictionary-key': 'error',\n 'intlayer/no-dynamic-field-access': 'error',\n});\n\n/**\n * Helper: Detects the environment and returns the doc URL\n */\nconst getDocumentationUrl = (packageJson: any): string => {\n const deps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n\n /**\n * Helper to check if a version string matches a specific major version\n * Matches: \"15\", \"^15.0.0\", \"~15.2\", \"15.0.0-beta\"\n */\n const isVersion = (versionString: string, major: number): boolean => {\n if (!versionString || typeof versionString !== 'string') return false;\n const regex = new RegExp(`^[\\\\^~]?${major}(?:\\\\.|$)`);\n return regex.test(versionString);\n };\n\n // Mobile / Cross-platform\n if (deps['@lynx-js/react'] || deps['@lynx-js/core']) {\n return DocumentationRouter.Lynx;\n }\n if (deps['react-native'] || deps.expo) {\n return DocumentationRouter.ReactNativeAndExpo;\n }\n\n // Meta-frameworks (Next, Nuxt, Astro, SvelteKit)\n if (deps.next) {\n const version = deps.next;\n\n if (isVersion(version, 14)) {\n return DocumentationRouter.NextJS_14;\n }\n\n if (isVersion(version, 15)) {\n return DocumentationRouter.NextJS_15;\n }\n\n return DocumentationRouter.NextJS;\n }\n\n if (deps.nuxt) return DocumentationRouter.NuxtAndVue;\n if (deps.astro) return DocumentationRouter.Astro;\n if (deps['@sveltejs/kit']) return DocumentationRouter.SvelteKit;\n\n // Routers (TanStack & React Router v7)\n if (deps['@tanstack/react-router']) {\n return DocumentationRouter.TanStackRouter;\n }\n\n // Check for React Router v7\n const reactRouterVersion = deps['react-router'];\n if (reactRouterVersion && typeof reactRouterVersion === 'string') {\n // Distinguish between standard v7 and v7 with FS routes\n if (deps['@react-router/fs-routes']) {\n return DocumentationRouter.ViteAndReact_ReactRouterV7_FSRoutes;\n }\n\n // Use Regex to ensure it is v7\n if (isVersion(reactRouterVersion, 7)) {\n return DocumentationRouter.ViteAndReact_ReactRouterV7;\n }\n }\n\n // Vite Ecosystem (General)\n if (deps.vite) {\n if (deps.vue) return DocumentationRouter.ViteAndVue;\n if (deps['solid-js']) return DocumentationRouter.ViteAndSolid;\n if (deps.svelte) return DocumentationRouter.ViteAndSvelte;\n if (deps.preact) return DocumentationRouter.ViteAndPreact;\n\n // Default to React if Vite is present but specific other frameworks aren't found\n return DocumentationRouter.ViteAndReact;\n }\n\n // Other Web Frameworks\n if (deps['react-scripts']) return DocumentationRouter.CRA;\n if (deps['@angular/core']) return DocumentationRouter.Angular;\n\n // Backend\n // NestJS first: it runs on top of Express (or Fastify), so both dependencies\n // are present and the more specific one has to win.\n if (deps['@nestjs/core']) return DocumentationRouter.NestJS;\n if (deps['@adonisjs/core']) return DocumentationRouter.AdonisJS;\n if (deps.elysia) return DocumentationRouter.Elysia;\n if (deps.hono) return DocumentationRouter.Hono;\n if (deps.express) return DocumentationRouter.Express;\n if (deps.fastify) return DocumentationRouter.Fastify;\n\n // Competitor Libs (Migration Guides)\n // We check these last as specific environment setup is usually higher priority,\n // but if no specific framework logic matched (or as a fallback), we guide to migration.\n if (deps['next-intl']) return DocumentationRouter.NextIntl;\n if (deps['react-i18next'] || deps.i18next)\n return DocumentationRouter.ReactI18Next;\n if (deps['react-intl']) return DocumentationRouter.ReactIntl;\n if (deps['next-i18next']) return DocumentationRouter.NextI18Next;\n if (deps['vue-i18n']) return DocumentationRouter.VueI18n;\n\n return DocumentationRouter.Default;\n};\n\n/**\n * Resolves the default dev-server URL of the detected framework, used as the\n * `editor.applicationURL` of the generated configuration file. Frameworks that\n * embed Vite (Astro, SvelteKit, …) are checked before the bare Vite fallback so\n * their own dev port wins.\n */\nexport const getDefaultApplicationURL = (\n dependencies: Record<string, string>\n): string => {\n if (dependencies.next) return 'http://localhost:3000';\n if (dependencies.astro) return 'http://localhost:4321';\n if (dependencies['@angular/core']) return 'http://localhost:4200';\n if (dependencies.nuxt) return 'http://localhost:3000';\n if (dependencies['@sveltejs/kit']) return 'http://localhost:5173';\n if (dependencies['react-scripts']) return 'http://localhost:3000';\n if (dependencies.vite) return 'http://localhost:5173';\n\n return 'http://localhost:3000';\n};\n\n/**\n * How the interactive init organizes autogenerated content (compiler output /\n * autofill):\n * - `per-component` — multilingual `.content` files co-located with components\n * (the configuration template default).\n * - `centralized` — per-locale JSON dictionaries under `/locales/{locale}/`.\n * - `json-namespaces` — plain per-locale JSON namespace files kept in sync\n * through `@intlayer/sync-json-plugin`.\n */\nexport type ContentStrategy =\n | 'per-component'\n | 'centralized'\n | 'json-namespaces';\n\n/**\n * OPTIONS\n */\nexport type InitOptions = {\n noGitignore?: boolean;\n /** Skip scaffolding the `fill` and `test` GitHub Actions workflows. */\n noGithubActions?: boolean;\n /**\n * Skip installing missing Intlayer dependencies and upgrading outdated ones.\n */\n noInstallPackages?: boolean;\n /** Skip adding the Intlayer extension to `.vscode/extensions.json`. */\n noVscodeExtension?: boolean;\n /** Skip writing the Intlayer LSP configuration to `.vscode/settings.json`. */\n noLsp?: boolean;\n /** Skip enabling the Intlayer lint rules (ESLint / oxlint). */\n noEslint?: boolean;\n /**\n * Skip framework-specific scaffolding (middleware/proxy, providers in\n * layout/page, example content). Defaults to enabled.\n */\n noFrameworkSetup?: boolean;\n /**\n * Version that outdated Intlayer packages should be upgraded to (typically the\n * running CLI version). When omitted, installed packages are left untouched and\n * only missing ones are installed.\n */\n upgradeToVersion?: string;\n /**\n * Locale routing strategy to write to `routing.mode` in the generated/existing\n * Intlayer configuration file. When omitted, the configuration default\n * (`prefix-no-default`) is kept.\n */\n routingMode?: RoutingMode;\n /**\n * Content organization strategy chosen by the interactive init prompt. Drives\n * the `compiler.output` template (`centralized`) or the injection of the\n * syncJSON plugin (`json-namespaces`). When omitted (or `per-component`), the\n * configuration template default is kept.\n */\n contentStrategy?: ContentStrategy;\n};\n\n/**\n * MAIN LOGIC\n */\nexport const initIntlayer = async (rootDir: string, options?: InitOptions) => {\n logger(colorize('Checking Intlayer configuration...', ANSIColors.CYAN));\n\n // READ PACKAGE.JSON\n const packageJsonPath = 'package.json';\n if (!(await exists(rootDir, packageJsonPath))) {\n logger(\n `${x} No ${colorizePath('package.json')} found. Please run this script from the project root.`,\n { level: 'error' }\n );\n process.exit(1);\n }\n\n const packageJsonContent = await readFileFromRoot(rootDir, packageJsonPath);\n let packageJson: Record<string, any>;\n try {\n packageJson = JSON.parse(packageJsonContent);\n } catch {\n logger(`${x} Could not parse ${colorizePath('package.json')}.`, {\n level: 'error',\n });\n process.exit(1);\n }\n\n // Determine the correct documentation URL based on dependencies\n const guideUrl = getDocumentationUrl(packageJson);\n\n const allDeps: Record<string, string> = {\n ...(packageJson.dependencies ?? {}),\n ...(packageJson.devDependencies ?? {}),\n };\n\n // INSTALL MISSING INTLAYER DEPENDENCIES\n const packageManager = detectPackageManager(rootDir);\n\n // lingui keeps its catalogs as `.po` (default) or `.json`. Detect which so the\n // matching sync plugin (`syncPO` / `syncJSON`) + dev dependency are chosen.\n const linguiPresent = Boolean(\n allDeps['@lingui/core'] ||\n allDeps['@lingui/react'] ||\n allDeps['@intlayer/lingui']\n );\n\n const linguiCatalog = linguiPresent\n ? await detectLinguiCatalogPattern(rootDir)\n : null;\n\n // `linguiCatalogFormat` resolution:\n // 1. Filesystem scan result (authoritative when catalogs already exist)\n // 2. Fallback: 'po' (lingui's default) when lingui is installed but no\n // catalogs have been generated yet.\n const linguiCatalogFormat: 'po' | 'json' | null =\n linguiCatalog?.format ?? (linguiPresent ? 'po' : null);\n\n const {\n packagesToInstall,\n devPackagesToInstall,\n compatSyncConfig,\n compatVitePluginConfig,\n } = detectMissingIntlayerPackages(allDeps, {\n linguiCatalogFormat,\n });\n\n // The `json-namespaces` content strategy uses the same syncJSON pipeline as\n // the compat libraries: plain per-locale JSON namespace files synced through\n // `@intlayer/sync-json-plugin`. A detected compat library keeps precedence —\n // its own catalog format/template is authoritative.\n const wantsJsonNamespaces =\n options?.contentStrategy === 'json-namespaces' && !compatSyncConfig;\n\n if (\n wantsJsonNamespaces &&\n !allDeps['@intlayer/sync-json-plugin'] &&\n !devPackagesToInstall.includes('@intlayer/sync-json-plugin')\n ) {\n devPackagesToInstall.push('@intlayer/sync-json-plugin');\n }\n\n if (!options?.noInstallPackages) {\n const withVersion = (packages: string[]): string[] =>\n options?.upgradeToVersion\n ? packages.map((pkg) => `${pkg}@${options.upgradeToVersion}`)\n : packages;\n\n if (packagesToInstall.length > 0) {\n logger(\n colorize('Installing missing Intlayer dependencies...', ANSIColors.CYAN)\n );\n try {\n installPackages(\n rootDir,\n withVersion(packagesToInstall),\n packageManager\n );\n logger(\n `${v} Installed: ${packagesToInstall.map((pkg) => colorize(pkg, ANSIColors.MAGENTA)).join(', ')}`\n );\n } catch {\n logger(\n `${x} Failed to install packages. Please install manually: ${packagesToInstall.join(' ')}`,\n { level: 'warn' }\n );\n }\n }\n\n if (devPackagesToInstall.length > 0) {\n logger(\n colorize(\n 'Installing missing Intlayer dev dependencies...',\n ANSIColors.CYAN\n )\n );\n try {\n installPackages(\n rootDir,\n withVersion(devPackagesToInstall),\n packageManager,\n true\n );\n logger(\n `${v} Installed: ${devPackagesToInstall.map((pkg) => colorize(pkg, ANSIColors.MAGENTA)).join(', ')}`\n );\n } catch {\n logger(\n `${x} Failed to install dev packages. Please install manually: ${devPackagesToInstall.join(' ')}`,\n { level: 'warn' }\n );\n }\n }\n\n // UPGRADE OUTDATED INTLAYER DEPENDENCIES\n // Only runs when a target version is provided (typically the running CLI\n // version). Already up-to-date packages are skipped. Prod and dev\n // dependencies are upgraded separately so their dependency type is kept.\n if (options?.upgradeToVersion) {\n const outdatedDependencies = detectOutdatedIntlayerPackages(\n rootDir,\n packageJson.dependencies ?? {},\n options.upgradeToVersion\n );\n const outdatedDevDependencies = detectOutdatedIntlayerPackages(\n rootDir,\n packageJson.devDependencies ?? {},\n options.upgradeToVersion\n );\n\n const allOutdated = [...outdatedDependencies, ...outdatedDevDependencies];\n\n if (allOutdated.length > 0) {\n logger(\n colorize(\n `Upgrading outdated Intlayer dependencies to ${options.upgradeToVersion}...`,\n ANSIColors.CYAN\n )\n );\n try {\n upgradePackages(\n rootDir,\n outdatedDependencies,\n packageManager,\n options.upgradeToVersion\n );\n upgradePackages(\n rootDir,\n outdatedDevDependencies,\n packageManager,\n options.upgradeToVersion,\n true\n );\n logger(\n `${v} Upgraded: ${allOutdated.map((pkg) => colorize(pkg, ANSIColors.MAGENTA)).join(', ')}`\n );\n } catch {\n logger(\n `${x} Failed to upgrade packages. Please upgrade manually: ${allOutdated.join(' ')}`,\n { level: 'warn' }\n );\n }\n } else {\n logger(`${v} Intlayer dependencies are up to date`);\n }\n }\n }\n\n // CHECK .GITIGNORE\n const gitignorePath = '.gitignore';\n if (!options?.noGitignore && (await exists(rootDir, gitignorePath))) {\n const gitignoreContent = await readFileFromRoot(rootDir, gitignorePath);\n\n if (!gitignoreContent.includes('intlayer')) {\n const newContent = `${gitignoreContent}\\n# Intlayer\\n.intlayer\\n`;\n await writeFileToRoot(rootDir, gitignorePath, newContent);\n logger(\n `${v} Added ${colorizePath('.intlayer')} to ${colorizePath(gitignorePath)}`\n );\n } else {\n logger(`${v} ${colorizePath(gitignorePath)} already includes .intlayer`);\n }\n }\n\n // SCAFFOLD GITHUB ACTIONS WORKFLOWS (fill + test)\n // Generate two workflows whose commands match the detected package manager:\n // - intlayer-fill.yml: auto-fills missing translations on pull requests\n // - intlayer-test.yml: fails the PR when required locales are missing\n // Every command runs with `--ci`, which iterates over the Intlayer\n // project(s) of the repository. GitHub only triggers workflows stored at the\n // repository root, so in a monorepo the files are written to the git root\n // (see resolveGithubWorkflowsContext).\n if (!options?.noGithubActions) {\n const workflowsContext = await resolveGithubWorkflowsContext(\n rootDir,\n packageManager\n );\n const workflows = getGithubWorkflows(\n workflowsContext.packageManager,\n workflowsContext.options\n );\n\n for (const workflow of workflows) {\n if (await exists(workflowsContext.workflowsRootDir, workflow.filePath)) {\n logger(`${v} ${colorizePath(workflow.filePath)} already exists`);\n continue;\n }\n\n try {\n await ensureDirectory(\n workflowsContext.workflowsRootDir,\n join('.github', 'workflows')\n );\n await writeFileToRoot(\n workflowsContext.workflowsRootDir,\n workflow.filePath,\n workflow.content\n );\n logger(\n `${v} Added GitHub Actions workflow ${colorizePath(workflow.filePath)}`\n );\n } catch {\n logger(\n `${x} Could not create ${colorizePath(workflow.filePath)}. You may need to add it manually.`,\n { level: 'warn' }\n );\n }\n }\n }\n\n // CHECK VS CODE EXTENSION RECOMMENDATIONS\n const vscodeDir = '.vscode';\n const extensionsJsonPath = join(vscodeDir, 'extensions.json');\n const extensionId = 'intlayer.intlayer-vs-code-extension';\n\n if (!options?.noVscodeExtension) {\n try {\n let extensionsConfig: { recommendations: string[] } = {\n recommendations: [],\n };\n\n if (await exists(rootDir, extensionsJsonPath)) {\n const content = await readFileFromRoot(rootDir, extensionsJsonPath);\n extensionsConfig = parseJSONWithComments(content);\n } else {\n await ensureDirectory(rootDir, vscodeDir);\n }\n\n if (!extensionsConfig.recommendations) {\n extensionsConfig.recommendations = [];\n }\n\n if (!extensionsConfig.recommendations.includes(extensionId)) {\n extensionsConfig.recommendations.push(extensionId);\n await writeFileToRoot(\n rootDir,\n extensionsJsonPath,\n JSON.stringify(extensionsConfig, null, 2)\n );\n logger(\n `${v} Added ${colorize(extensionId, ANSIColors.MAGENTA)} to ${colorizePath(extensionsJsonPath)}`\n );\n } else {\n logger(\n `${v} ${colorizePath(extensionsJsonPath)} already includes ${colorize(extensionId, ANSIColors.MAGENTA)}`\n );\n }\n } catch {\n logger(\n `${x} Could not update ${colorizePath(extensionsJsonPath)}. You may need to add ${colorize(extensionId, ANSIColors.MAGENTA)} manually.`,\n { level: 'warn' }\n );\n }\n }\n\n // CHECK VS CODE LSP SETTINGS\n const settingsJsonPath = join(vscodeDir, 'settings.json');\n\n if (!options?.noLsp) {\n try {\n let settingsConfig: Record<string, unknown> = {};\n\n if (await exists(rootDir, settingsJsonPath)) {\n const content = await readFileFromRoot(rootDir, settingsJsonPath);\n settingsConfig = parseJSONWithComments(content);\n } else {\n await ensureDirectory(rootDir, vscodeDir);\n }\n\n let settingsUpdated = false;\n\n if (!settingsConfig['intlayer.languageServer.command']) {\n settingsConfig['intlayer.languageServer.command'] = 'npx';\n settingsUpdated = true;\n }\n\n if (!settingsConfig['intlayer.languageServer.args']) {\n settingsConfig['intlayer.languageServer.args'] = ['@intlayer/lsp'];\n settingsUpdated = true;\n }\n\n if (settingsUpdated) {\n await writeFileToRoot(\n rootDir,\n settingsJsonPath,\n JSON.stringify(settingsConfig, null, 2)\n );\n logger(\n `${v} Updated ${colorizePath(settingsJsonPath)} with LSP configuration`\n );\n } else {\n logger(\n `${v} ${colorizePath(settingsJsonPath)} already includes LSP configuration`\n );\n }\n } catch {\n logger(\n `${x} Could not update ${colorizePath(settingsJsonPath)}. You may need to add the LSP settings manually.`,\n { level: 'warn' }\n );\n }\n }\n\n // CHECK LINT RULES (eslint-plugin-intlayer)\n //\n // Gated on the project already having a linter installed: with no ESLint or\n // oxlint there is nothing to plug the rules into, and the guidance below\n // would just be noise. The same check gates the install in\n // `detectMissingIntlayerPackages`, so the two never disagree.\n //\n // Only the oxlint config is patched here: `.oxlintrc.json` is plain JSON and\n // safe to edit, whereas an ESLint flat config is arbitrary JavaScript that\n // cannot be rewritten reliably — that one is left to the user, with the\n // snippet printed below.\n if (!options?.noEslint && hasLintTooling(allDeps)) {\n const oxlintConfigPath = '.oxlintrc.json';\n const hasOxlintConfig = await exists(rootDir, oxlintConfigPath);\n\n if (hasOxlintConfig) {\n try {\n const content = await readFileFromRoot(rootDir, oxlintConfigPath);\n const oxlintConfig: {\n jsPlugins?: string[];\n rules?: Record<string, unknown>;\n ignorePatterns?: string[];\n } = parseJSONWithComments(content);\n\n oxlintConfig.jsPlugins ??= [];\n oxlintConfig.rules ??= {};\n oxlintConfig.ignorePatterns ??= [];\n\n let oxlintUpdated = false;\n\n // Generated dictionaries are not source; linting them is wasted work.\n for (const glob of GENERATED_OUTPUT_GLOBS) {\n if (!oxlintConfig.ignorePatterns.includes(glob)) {\n oxlintConfig.ignorePatterns.push(glob);\n oxlintUpdated = true;\n }\n }\n\n if (!oxlintConfig.jsPlugins.includes(LINT_PLUGIN_PACKAGE_NAME)) {\n oxlintConfig.jsPlugins.push(LINT_PLUGIN_PACKAGE_NAME);\n oxlintUpdated = true;\n }\n\n for (const [ruleName, severity] of Object.entries(\n getRecommendedRules()\n )) {\n if (!oxlintConfig.rules[ruleName]) {\n oxlintConfig.rules[ruleName] = severity;\n oxlintUpdated = true;\n }\n }\n\n if (oxlintUpdated) {\n await writeFileToRoot(\n rootDir,\n oxlintConfigPath,\n JSON.stringify(oxlintConfig, null, 2)\n );\n logger(\n `${v} Added the Intlayer lint rules to ${colorizePath(oxlintConfigPath)}`\n );\n } else {\n logger(\n `${v} ${colorizePath(oxlintConfigPath)} already includes the Intlayer lint rules`\n );\n }\n } catch {\n logger(\n `${x} Could not update ${colorizePath(oxlintConfigPath)}. You may need to add ${colorize(LINT_PLUGIN_PACKAGE_NAME, ANSIColors.MAGENTA)} manually.`,\n { level: 'warn' }\n );\n }\n } else {\n // ESLint flat config — print the snippet instead of rewriting the file.\n logger([\n colorize('Intlayer lint rules →', ANSIColors.MAGENTA),\n colorize(\n `Add ${LINT_PLUGIN_PACKAGE_NAME} to your ESLint flat config to catch hardcoded text and dynamic calls the compiler cannot optimize:`,\n ANSIColors.GREY_LIGHT\n ),\n colorize(\n ` import intlayer from '${LINT_PLUGIN_PACKAGE_NAME}';\\n export default [...intlayer.configs.recommended];`,\n ANSIColors.GREY_LIGHT\n ),\n colorizePath(DocumentationRouter.ESLint),\n ]);\n }\n }\n\n // CHECK TSCONFIGS\n const tsConfigFiles = await findTsConfigFiles(rootDir);\n let hasTsConfig = false;\n\n for (const fileName of tsConfigFiles) {\n if (await exists(rootDir, fileName)) {\n hasTsConfig = true;\n try {\n const fileContent = await readFileFromRoot(rootDir, fileName);\n const config = parseJSONWithComments(fileContent);\n const typeDefinition = './.intlayer/**/*.ts';\n\n let updated = false;\n\n if (!config.include) {\n // Skip if no include array (solution-style)\n } else if (\n Array.isArray(config.include) &&\n !(config.include as string[]).some((pattern: string) =>\n pattern.includes('.intlayer')\n )\n ) {\n config.include.push(typeDefinition);\n updated = true;\n } else if (config.include.includes(typeDefinition)) {\n logger(\n `${v} ${colorizePath(fileName)} already includes intlayer types`\n );\n }\n\n if (updated) {\n await writeFileToRoot(\n rootDir,\n fileName,\n JSON.stringify(config, null, 2)\n );\n logger(\n `${v} Updated ${colorizePath(fileName)} to include intlayer types`\n );\n }\n } catch {\n logger(\n `${x} Could not parse or update ${colorizePath(fileName)}. You may need to add ${colorizePath('.intlayer/types/**/*.ts')} manually.`,\n { level: 'warn' }\n );\n }\n }\n }\n\n // INITIALIZE CONFIG FILE\n const format = hasTsConfig ? 'intlayer.config.ts' : 'intlayer.config.mjs';\n\n // Detect the locale JSON file pattern already in the project so we can\n // insert the matching locales into the config and produce the most\n // accurate source template for compat libraries.\n const detectedPattern = await detectJsonLocalePattern(rootDir);\n\n await initConfig(format, rootDir, {\n locales: detectedPattern?.locales,\n applicationURL: getDefaultApplicationURL(allDeps),\n });\n\n // APPLY CONTENT STRATEGY — CENTRALIZED\n // Point `compiler.output` at per-locale JSON dictionaries under `/locales/`\n // instead of the per-component template default.\n if (options?.contentStrategy === 'centralized') {\n await setCompilerOutputInConfig(\n rootDir,\n '/locales/{{locale}}/{{key}}.content.json'\n );\n }\n\n // INJECT SYNC-JSON PLUGIN FOR COMPAT LIBRARIES / JSON-NAMESPACES STRATEGY\n const syncConfigToInject: CompatSyncConfig | undefined =\n compatSyncConfig ??\n (wantsJsonNamespaces\n ? {\n format: 'i18next',\n sourceTemplate:\n detectedPattern?.template ?? './locales/${locale}/${key}.json',\n }\n : undefined);\n\n if (syncConfigToInject) {\n // For next-intl / use-intl, the messages path is authoritatively declared\n // in `i18n/request.ts` (e.g. `import(`../messages/${locale}.json`)`), so we\n // read it directly. It usually resolves to a single file per locale (no\n // `${key}` segment), whose first-level keys are namespaces — so the compat\n // config carries `splitKeys: true` to emit one dictionary per namespace.\n // Falls back to file-system globbing otherwise.\n const nextIntlMessagesPattern =\n allDeps['next-intl'] ||\n allDeps['@intlayer/next-intl'] ||\n allDeps['use-intl'] ||\n allDeps['@intlayer/use-intl']\n ? await detectNextIntlMessagesPattern(rootDir)\n : null;\n\n // lingui authoritatively resolves to `…/{locale}/messages.{po,json}`, where\n // the `messages` filename becomes the dictionary key. Prefer it so a `.po`\n // project gets `syncPO` (the generic JSON glob only sees `.json`).\n const sourceTemplate =\n linguiCatalog?.template ??\n nextIntlMessagesPattern?.template ??\n detectedPattern?.template;\n\n const resolvedSyncConfig = {\n ...syncConfigToInject,\n ...(sourceTemplate ? { sourceTemplate } : {}),\n };\n\n const syncPluginPackage =\n resolvedSyncConfig.plugin === 'po'\n ? '@intlayer/sync-po-plugin'\n : '@intlayer/sync-json-plugin';\n const syncPluginName =\n resolvedSyncConfig.plugin === 'po' ? 'syncPO' : 'syncJSON';\n\n // `splitKeys` only makes sense for a single file holding several namespaces.\n // If the resolved template addresses one namespace per file (`${key}`\n // segment), each file is already a single namespace — drop the flag so\n // syncJSON keeps one dictionary per file.\n if (\n resolvedSyncConfig.splitKeys &&\n resolvedSyncConfig.sourceTemplate.includes('${key}')\n ) {\n resolvedSyncConfig.splitKeys = false;\n }\n\n const intlayerConfigCandidates = [\n 'intlayer.config.ts',\n 'intlayer.config.mjs',\n 'intlayer.config.js',\n 'intlayer.config.cjs',\n ];\n\n for (const configFile of intlayerConfigCandidates) {\n if (await exists(rootDir, configFile)) {\n const configContent = await readFileFromRoot(rootDir, configFile);\n\n if (!configContent.includes(syncPluginPackage)) {\n const extension = configFile.split('.').pop()!;\n const updatedConfigContent = updateIntlayerConfigWithSyncPlugin(\n configContent,\n extension,\n resolvedSyncConfig\n );\n await writeFileToRoot(rootDir, configFile, updatedConfigContent);\n logger(\n `${v} Updated ${colorizePath(configFile)} with ${syncPluginName} compat plugin`\n );\n } else {\n logger(\n `${v} ${colorizePath(configFile)} already includes ${syncPluginName} plugin`\n );\n }\n break;\n }\n }\n }\n\n // APPLY ROUTING MODE\n // When a routing strategy was chosen (interactive init), write it to\n // `routing.mode` in the configuration file. Idempotent and comment-preserving.\n if (options?.routingMode) {\n await setRoutingModeInConfig(rootDir, options.routingMode);\n }\n\n let hasAliasConfiguration = false;\n\n // CHECK VITE CONFIG\n const viteConfigs = ['vite.config.ts', 'vite.config.js', 'vite.config.mjs'];\n\n for (const file of viteConfigs) {\n if (await exists(rootDir, file)) {\n hasAliasConfiguration = true;\n const content = await readFileFromRoot(rootDir, file);\n const extension = file.split('.').pop()!;\n\n if (compatVitePluginConfig) {\n const { replacesVitePlugin } = compatVitePluginConfig;\n\n if (content.includes(compatVitePluginConfig.pluginPackageSource)) {\n logger(\n `${v} ${colorizePath(file)} already includes ${compatVitePluginConfig.pluginPackageSource}`\n );\n } else if (\n replacesVitePlugin &&\n content.includes(replacesVitePlugin.fromPackageSource)\n ) {\n // Drop-in replacement: rewrite the original i18n vite plugin's import\n // source, keeping its binding and call site (e.g. lingui).\n const updatedContent = replaceViteConfigPluginImportSource(\n content,\n replacesVitePlugin.importName,\n replacesVitePlugin.fromPackageSource,\n compatVitePluginConfig.pluginPackageSource\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to import ${replacesVitePlugin.importName} from ${compatVitePluginConfig.pluginPackageSource}`\n );\n } else {\n const updatedContent = updateViteConfigForCompatPlugin(\n content,\n extension,\n compatVitePluginConfig\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include ${compatVitePluginConfig.pluginFunctionName} compat plugin`\n );\n }\n } else if (hasIntlayerVitePlugin(content)) {\n // A compat adapter plugin (e.g. `reactI18nextVitePlugin`) already wraps\n // `intlayer()` internally, so skip adding a redundant standalone plugin.\n logger(\n `${v} ${colorizePath(file)} already includes an Intlayer plugin`\n );\n } else {\n const updatedContent = updateViteConfig(content, extension);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(`${v} Updated ${colorizePath(file)} to include Intlayer plugin`);\n }\n break;\n }\n }\n\n // CHECK NEXT CONFIG\n const nextConfigs = ['next.config.js', 'next.config.mjs', 'next.config.ts'];\n let isNextJsProject = false;\n\n for (const file of nextConfigs) {\n if (await exists(rootDir, file)) {\n isNextJsProject = true;\n hasAliasConfiguration = true;\n const content = await readFileFromRoot(rootDir, file);\n const extension = file.split('.').pop()!;\n\n if (allDeps['next-i18next']) {\n if (!content.includes('@intlayer/next-i18next')) {\n const updatedContent = updateNextConfigForNextI18next(\n content,\n extension\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include Intlayer next-i18next compat plugin`\n );\n } else {\n logger(\n `${v} ${colorizePath(file)} already includes @intlayer/next-i18next`\n );\n }\n } else if (allDeps['next-intl']) {\n if (!content.includes('@intlayer/next-intl/plugin')) {\n const updatedContent = updateNextConfigForNextIntl(\n content,\n extension\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include Intlayer next-intl compat plugin`\n );\n } else {\n logger(\n `${v} ${colorizePath(file)} already includes @intlayer/next-intl/plugin`\n );\n }\n } else if (allDeps['next-translate']) {\n if (!content.includes('@intlayer/next-translate')) {\n const updatedContent = updateNextConfigForNextTranslate(\n content,\n extension\n );\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include Intlayer next-translate compat plugin`\n );\n } else {\n logger(\n `${v} ${colorizePath(file)} already includes @intlayer/next-translate`\n );\n }\n } else if (!content.includes('next-intlayer')) {\n const updatedContent = updateNextConfig(content, extension);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(`${v} Updated ${colorizePath(file)} to include Intlayer plugin`);\n }\n break;\n }\n }\n\n // CHECK NEXT.JS COMPILER (BABEL)\n // When a Next.js project already uses Babel, install @intlayer/babel and print\n // the compiler + build-optimization plugins (extract, purge, minify, optimize)\n // to add. SWC projects need no setup: withIntlayer injects @intlayer/swc.\n if (isNextJsProject) {\n await setupNextCompilerBabelConfig({\n rootDir,\n packageManager,\n allDeps,\n skipInstall: Boolean(options?.noInstallPackages),\n });\n }\n\n // CHECK OTHER FRAMEWORKS CONFIG\n const astroConfigs = [\n 'astro.config.mjs',\n 'astro.config.js',\n 'astro.config.ts',\n 'astro.config.cjs',\n ];\n\n for (const file of astroConfigs) {\n if (await exists(rootDir, file)) {\n hasAliasConfiguration = true;\n\n if (file.startsWith('astro.config.')) {\n const content = await readFileFromRoot(rootDir, file);\n\n if (!content.includes('astro-intlayer')) {\n const extension = file.split('.').pop()!;\n const updatedContent = updateAstroConfig(content, extension);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include Intlayer integration`\n );\n }\n }\n break;\n }\n }\n\n const nuxtConfigs = ['nuxt.config.js', 'nuxt.config.ts'];\n for (const file of nuxtConfigs) {\n if (await exists(rootDir, file)) {\n hasAliasConfiguration = true;\n\n const content = await readFileFromRoot(rootDir, file);\n\n if (allDeps['@nuxtjs/i18n']) {\n if (!content.includes('@intlayer/nuxtjs-i18n')) {\n const updatedContent = updateNuxtConfigForNuxtjsI18n(content);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(\n `${v} Updated ${colorizePath(file)} to include @intlayer/nuxtjs-i18n module`\n );\n } else {\n logger(\n `${v} ${colorizePath(file)} already includes @intlayer/nuxtjs-i18n`\n );\n }\n } else if (!content.includes('nuxt-intlayer')) {\n const updatedContent = updateNuxtConfig(content);\n await writeFileToRoot(rootDir, file, updatedContent);\n logger(`${v} Updated ${colorizePath(file)} to include Intlayer module`);\n }\n break;\n }\n }\n\n // CHECK METRO CONFIG (React Native / Expo)\n // Metro is the React Native bundler. The Intlayer Metro plugin\n // (`react-native-intlayer/metro`) handles dictionary aliasing and building.\n // When no config exists we scaffold one; when one exists we safely wrap its\n // exported config with `configMetroIntlayerSync`, leaving custom async\n // (IIFE) configs untouched so existing setups are never broken.\n const isReactNativeProject = Boolean(allDeps['react-native'] || allDeps.expo);\n\n if (isReactNativeProject) {\n // Metro resolves Intlayer aliases itself, so skip the alias fallback below.\n hasAliasConfiguration = true;\n\n const metroConfigs = [\n 'metro.config.js',\n 'metro.config.cjs',\n 'metro.config.mjs',\n 'metro.config.ts',\n ];\n\n let metroConfigFile: string | undefined;\n\n for (const file of metroConfigs) {\n if (await exists(rootDir, file)) {\n metroConfigFile = file;\n break;\n }\n }\n\n if (metroConfigFile) {\n const content = await readFileFromRoot(rootDir, metroConfigFile);\n\n if (content.includes('react-native-intlayer')) {\n logger(\n `${v} ${colorizePath(metroConfigFile)} already includes the Intlayer Metro plugin`\n );\n } else {\n const extension = metroConfigFile.split('.').pop()!;\n const updatedContent = updateMetroConfig(content, extension);\n\n if (updatedContent !== content) {\n await writeFileToRoot(rootDir, metroConfigFile, updatedContent);\n logger(\n `${v} Updated ${colorizePath(metroConfigFile)} to include the Intlayer Metro plugin`\n );\n } else {\n logger(\n `${x} Could not automatically update ${colorizePath(metroConfigFile)}. Wrap your exported config with ${colorize('configMetroIntlayer', ANSIColors.MAGENTA)}: ${colorizePath(DocumentationRouter.ReactNativeAndExpo)}`,\n { level: 'warn' }\n );\n }\n }\n } else {\n const newMetroConfigFile = 'metro.config.js';\n await writeFileToRoot(\n rootDir,\n newMetroConfigFile,\n getMetroConfigTemplate(Boolean(allDeps.expo))\n );\n logger(\n `${v} Created ${colorizePath(newMetroConfigFile)} with the Intlayer Metro plugin`\n );\n }\n }\n\n // UPDATE PACKAGE.JSON DEV SCRIPT\n // Only frameworks with no bundler plugin to host the content watcher get\n // their dev server wrapped; a Next.js app is always left alone. See\n // `resolveDevScript`.\n const newDevScript = resolveDevScript({\n devScript: packageJson.scripts?.dev,\n allDeps,\n isNextJsProject,\n });\n\n if (newDevScript) {\n packageJson.scripts.dev = newDevScript;\n\n await writeFileToRoot(\n rootDir,\n packageJsonPath,\n JSON.stringify(packageJson, null, 2)\n );\n\n logger(\n `${v} Updated ${colorizePath('package.json')} dev script to run intlayer watch`\n );\n }\n\n // CHECK WEBPACK CONFIG\n const webpackConfigs = [\n 'webpack.config.js',\n 'webpack.config.ts',\n 'webpack.config.mjs',\n 'webpack.config.cjs',\n ];\n\n for (const file of webpackConfigs) {\n if (await exists(rootDir, file)) {\n hasAliasConfiguration = true;\n logger(\n `${v} Found ${colorizePath(\n file\n )}. Make sure to configure aliases manually or use the Intlayer Webpack plugin.`\n );\n break;\n }\n }\n\n // The server frameworks themselves, alongside their Intlayer packages: a\n // project may be mid-setup and carry only one of the two.\n const backendConfigPackages = [\n 'express',\n 'fastify',\n '@adonisjs/core',\n 'hono',\n 'elysia',\n ...BACKEND_INTLAYER_PACKAGES,\n ];\n\n if (backendConfigPackages.some((pkg) => allDeps[pkg])) {\n hasAliasConfiguration = true;\n }\n\n if (!hasAliasConfiguration) {\n const configuration = getConfiguration({ baseDir: rootDir });\n // `tsconfig`/`jsconfig` `paths` and `package.json` `imports` both expect\n // explicitly relative specifiers, so bare relative paths get a `./` prefix.\n const aliases = getAlias({\n configuration,\n formatter: (value) =>\n value.startsWith('./') || value.startsWith('../')\n ? value\n : `./${value}`,\n });\n\n if (hasTsConfig && tsConfigFiles.length > 0) {\n const tsConfigPath =\n tsConfigFiles.find((file) => file === 'tsconfig.json') ||\n tsConfigFiles[0];\n\n if (tsConfigPath) {\n const tsConfigContent = await readFileFromRoot(rootDir, tsConfigPath);\n const config = parseJSONWithComments(tsConfigContent);\n\n config.compilerOptions ??= {};\n config.compilerOptions.paths ??= {};\n\n let updated = false;\n\n Object.entries(aliases).forEach(([alias, path]) => {\n if (!config.compilerOptions.paths[alias]) {\n config.compilerOptions.paths[alias] = [path];\n updated = true;\n }\n });\n\n if (updated) {\n await writeFileToRoot(\n rootDir,\n tsConfigPath,\n JSON.stringify(config, null, 2)\n );\n\n logger(\n `${v} Updated ${colorizePath(\n tsConfigPath\n )} to include Intlayer aliases`\n );\n }\n }\n } else {\n const jsConfigPath = 'jsconfig.json';\n\n if (await exists(rootDir, jsConfigPath)) {\n const jsConfigContent = await readFileFromRoot(rootDir, jsConfigPath);\n const config = parseJSONWithComments(jsConfigContent);\n\n config.compilerOptions ??= {};\n config.compilerOptions.paths ??= {};\n\n let updated = false;\n\n Object.entries(aliases).forEach(([alias, path]) => {\n if (!config.compilerOptions.paths[alias]) {\n config.compilerOptions.paths[alias] = [path];\n updated = true;\n }\n });\n\n if (updated) {\n await writeFileToRoot(\n rootDir,\n jsConfigPath,\n JSON.stringify(config, null, 2)\n );\n logger(\n `${v} Updated ${colorizePath(\n jsConfigPath\n )} to include Intlayer aliases`\n );\n }\n } else {\n packageJson.imports ??= {};\n\n let updated = false;\n\n Object.entries(aliases).forEach(([alias, path]) => {\n const importAlias = alias.replace('@', '#');\n\n if (!packageJson.imports[importAlias]) {\n packageJson.imports[importAlias] = path;\n updated = true;\n }\n });\n\n if (updated) {\n await writeFileToRoot(\n rootDir,\n packageJsonPath,\n JSON.stringify(packageJson, null, 2)\n );\n logger(\n `${v} Updated ${colorizePath(\n packageJsonPath\n )} to include Intlayer imports`\n );\n }\n }\n }\n }\n\n // FRAMEWORK-SPECIFIC SCAFFOLDING\n // Sets up middleware/proxy and wraps the layout/page with the Intlayer\n // providers for the detected framework (Next.js App Router today). Idempotent\n // and non-destructive: it never overwrites user code it cannot safely\n // transform, skipping with guidance instead.\n //\n // Skipped entirely when a compat i18n library drives the app (i18next,\n // next-intl, vue-i18n, …): the app already owns its routing, providers and\n // layouts, and Intlayer only syncs its catalogs — restructuring routes or\n // injecting providers would break the existing setup.\n const hasCompatLibrary = Boolean(compatSyncConfig || compatVitePluginConfig);\n\n if (!options?.noFrameworkSetup && hasCompatLibrary) {\n logger(\n `${v} Compat i18n library detected — keeping the app structure untouched (no route restructure, no provider injection)`\n );\n }\n\n if (!options?.noFrameworkSetup && !hasCompatLibrary) {\n try {\n // Prefer the user's interactive choice, else the configured routing mode\n // (which itself defaults to `prefix-no-default`). Drives whether the\n // locale path segment is required (`prefix-all`) or optional.\n const routingMode =\n options?.routingMode ??\n getConfiguration({ baseDir: rootDir }).routing.mode;\n\n await setupFramework({\n rootDir,\n allDeps,\n packageManager,\n useTypeScript: hasTsConfig,\n routingMode,\n });\n } catch {\n logger(\n `${x} Framework-specific scaffolding failed. Your existing files were left untouched; follow the documentation to finish the setup.`,\n { level: 'warn' }\n );\n }\n }\n\n // FINAL SUCCESS MESSAGE\n logger(`${v} ${colorize('Intlayer init setup complete.', ANSIColors.GREEN)}`);\n logger([\n colorize('Next →', ANSIColors.MAGENTA),\n colorize(\n `Follow the instructions in the documentation to complete the setup:`,\n ANSIColors.GREY_LIGHT\n ),\n colorizePath(guideUrl),\n ]);\n\n // INTLAYER LSP — instruction for the AI agent running `intlayer init`.\n // The Intlayer Language Server (`@intlayer/lsp`) provides Go-to-Definition\n // from `useIntlayer` / `getIntlayer` keys to their `.content` files, for both\n // IDEs and AI agents. Setup is editor-specific (VS Code, Cursor, Windsurf,\n // Zed, Neovim, Claude Code, …), so we don't scaffold anything here — instead\n // we point to the doc and ask the agent to confirm the target platform.\n logger([\n colorize('Intlayer LSP →', ANSIColors.MAGENTA),\n colorize(\n 'The Intlayer Language Server is available via the `@intlayer/lsp` package. It lets IDEs and AI agents jump from `useIntlayer`/`getIntlayer` keys to their `.content` files.',\n ANSIColors.GREY_LIGHT\n ),\n colorize(\n 'Ask the user which platform/editor they want to enable it on (VS Code, Cursor, Windsurf, Zed, Neovim, Claude Code, …), then follow the matching setup instructions:',\n ANSIColors.GREY_LIGHT\n ),\n colorizePath(DocumentationRouter.LSP),\n ]);\n};\n"],"mappings":"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"}
|
|
@@ -17,9 +17,8 @@ const BACKEND_INTLAYER_PACKAGES = [
|
|
|
17
17
|
* project's script must be left alone.
|
|
18
18
|
*
|
|
19
19
|
* A Next.js app is always left alone: `withIntlayer` starts the content watcher
|
|
20
|
-
* itself
|
|
21
|
-
*
|
|
22
|
-
* add a second watcher rebuilding the same dictionaries. This is checked on the
|
|
20
|
+
* itself on both bundlers, so wrapping the dev server in `intlayer watch --with`
|
|
21
|
+
* would only add a second watcher rebuilding the same dictionaries. This is checked on the
|
|
23
22
|
* `next` dependency as well as on the config file, since `next.config.*` is
|
|
24
23
|
* optional, and it takes precedence over the backend list, which a Next.js app
|
|
25
24
|
* with a custom server (or a monorepo root) can match too.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devScript.mjs","names":[],"sources":["../../../../src/init/utils/devScript.ts"],"sourcesContent":["/**\n * Frameworks with no Intlayer bundler plugin to host the content watcher.\n *\n * Their dev server is a plain Node process, so the only way to rebuild\n * `.intlayer` on a content change is to run the watcher next to it.\n */\nexport const BACKEND_INTLAYER_PACKAGES = [\n 'express-intlayer',\n 'fastify-intlayer',\n 'adonis-intlayer',\n 'hono-intlayer',\n 'elysia-intlayer',\n];\n\nexport type ResolveDevScriptParams = {\n /** The project's current `dev` script, if it has one. */\n devScript?: string;\n /** Combined prod + dev dependencies of the project. */\n allDeps: Record<string, string>;\n /** Whether a `next.config.*` file was found in the project. */\n isNextJsProject: boolean;\n};\n\n/**\n * Resolves the `dev` script `intlayer init` should write, or `null` when the\n * project's script must be left alone.\n *\n * A Next.js app is always left alone: `withIntlayer` starts the content watcher\n * itself
|
|
1
|
+
{"version":3,"file":"devScript.mjs","names":[],"sources":["../../../../src/init/utils/devScript.ts"],"sourcesContent":["/**\n * Frameworks with no Intlayer bundler plugin to host the content watcher.\n *\n * Their dev server is a plain Node process, so the only way to rebuild\n * `.intlayer` on a content change is to run the watcher next to it.\n */\nexport const BACKEND_INTLAYER_PACKAGES = [\n 'express-intlayer',\n 'fastify-intlayer',\n 'adonis-intlayer',\n 'hono-intlayer',\n 'elysia-intlayer',\n];\n\nexport type ResolveDevScriptParams = {\n /** The project's current `dev` script, if it has one. */\n devScript?: string;\n /** Combined prod + dev dependencies of the project. */\n allDeps: Record<string, string>;\n /** Whether a `next.config.*` file was found in the project. */\n isNextJsProject: boolean;\n};\n\n/**\n * Resolves the `dev` script `intlayer init` should write, or `null` when the\n * project's script must be left alone.\n *\n * A Next.js app is always left alone: `withIntlayer` starts the content watcher\n * itself on both bundlers, so wrapping the dev server in `intlayer watch --with`\n * would only add a second watcher rebuilding the same dictionaries. This is checked on the\n * `next` dependency as well as on the config file, since `next.config.*` is\n * optional, and it takes precedence over the backend list, which a Next.js app\n * with a custom server (or a monorepo root) can match too.\n *\n * @example a backend project gets its dev server wrapped\n * ```ts\n * resolveDevScript({\n * devScript: 'node ./src/index.ts',\n * allDeps: { 'express-intlayer': '9.0.0' },\n * isNextJsProject: false,\n * }); // → \"intlayer watch --with 'node ./src/index.ts'\"\n * ```\n *\n * @example a Next.js project keeps its own script\n * ```ts\n * resolveDevScript({\n * devScript: 'next dev',\n * allDeps: { next: '16.0.0' },\n * isNextJsProject: true,\n * }); // → null\n * ```\n */\nexport const resolveDevScript = ({\n devScript,\n allDeps,\n isNextJsProject,\n}: ResolveDevScriptParams): string | null => {\n if (!devScript) return null;\n\n const isNextJsApp = isNextJsProject || Boolean(allDeps.next);\n\n if (isNextJsApp) return null;\n\n const hasBackendIntlayerPackage = BACKEND_INTLAYER_PACKAGES.some(\n (packageName) => allDeps[packageName]\n );\n\n if (!hasBackendIntlayerPackage) return null;\n\n // Re-running init must not nest one wrapper inside another.\n if (devScript.includes('intlayer watch')) return null;\n\n return `intlayer watch --with '${devScript}'`;\n};\n"],"mappings":";;;;;;;AAMA,MAAa,4BAA4B;CACvC;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,oBAAoB,EAC/B,WACA,SACA,sBAC2C;CAC3C,IAAI,CAAC,WAAW,OAAO;CAIvB,IAFoB,mBAAmB,QAAQ,QAAQ,IAAI,GAE1C,OAAO;CAMxB,IAAI,CAJ8B,0BAA0B,MACzD,gBAAgB,QAAQ,YAGE,GAAG,OAAO;CAGvC,IAAI,UAAU,SAAS,gBAAgB,GAAG,OAAO;CAEjD,OAAO,0BAA0B,UAAU;AAC7C"}
|
|
@@ -27,13 +27,12 @@ const hasWorkspaceManifest = (repositoryRootDir) => {
|
|
|
27
27
|
*
|
|
28
28
|
* - Project at the repository root: workflows are written in place. When the
|
|
29
29
|
* repository is a monorepo (workspace manifest, or several Intlayer projects
|
|
30
|
-
* discovered by `listProjects`), the
|
|
31
|
-
* every project is covered.
|
|
30
|
+
* discovered by `listProjects`), the per-project credentials hint is added.
|
|
32
31
|
* - Project nested in a repository whose root has a `package.json`
|
|
33
32
|
* (workspace-managed monorepo): workflows are written at the git root —
|
|
34
33
|
* GitHub ignores `.github/workflows` in subdirectories — dependencies are
|
|
35
|
-
* installed at the root with the root's package manager, and the
|
|
36
|
-
*
|
|
34
|
+
* installed at the root with the root's package manager, and the per-project
|
|
35
|
+
* credentials hint is added.
|
|
37
36
|
* - Project nested in a repository without a root `package.json`: workflows
|
|
38
37
|
* are written at the git root but run inside the project directory
|
|
39
38
|
* (`working-directory`), keeping the project's own package manager.
|
|
@@ -58,12 +57,12 @@ const resolveGithubWorkflowsContext = async (rootDir, projectPackageManager) =>
|
|
|
58
57
|
if (!isNestedProject) return {
|
|
59
58
|
workflowsRootDir: projectRootDir,
|
|
60
59
|
packageManager: projectPackageManager,
|
|
61
|
-
options: {
|
|
60
|
+
options: { isMonorepo: isWorkspaceRepository || repositoryProjectCount > 1 }
|
|
62
61
|
};
|
|
63
62
|
if (hasRootPackageJson) return {
|
|
64
63
|
workflowsRootDir: repositoryRootDir,
|
|
65
64
|
packageManager: detectPackageManager(repositoryRootDir),
|
|
66
|
-
options: {
|
|
65
|
+
options: { isMonorepo: true }
|
|
67
66
|
};
|
|
68
67
|
return {
|
|
69
68
|
workflowsRootDir: repositoryRootDir,
|
|
@@ -131,12 +130,12 @@ const getWorkingDirectoryBlock = (workingDirectory) => workingDirectory ? `
|
|
|
131
130
|
run:
|
|
132
131
|
working-directory: ${workingDirectory}` : "";
|
|
133
132
|
/**
|
|
134
|
-
* Renders the env comment + optional wiring for per-project credentials in
|
|
135
|
-
* monorepo
|
|
133
|
+
* Renders the env comment + optional wiring for per-project credentials in a
|
|
134
|
+
* monorepo. With `--ci`, the CLI matches each entry of the
|
|
136
135
|
* `INTLAYER_PROJECT_CREDENTIALS` JSON map to a discovered project path and
|
|
137
136
|
* injects its access keys before running the command in that project.
|
|
138
137
|
*/
|
|
139
|
-
const getMonorepoCredentialsBlock = (
|
|
138
|
+
const getMonorepoCredentialsBlock = (isMonorepo) => isMonorepo ? `
|
|
140
139
|
#
|
|
141
140
|
# Monorepo — per-project CMS credentials, as a JSON map of project path
|
|
142
141
|
# (relative to the repository root) to access keys, e.g.
|
|
@@ -147,13 +146,16 @@ const getMonorepoCredentialsBlock = (useCiCommand) => useCiCommand ? `
|
|
|
147
146
|
* missing translations for the changed dictionaries (`--git-diff`) using AI,
|
|
148
147
|
* then commits the result back to the PR branch.
|
|
149
148
|
*
|
|
149
|
+
* Every intlayer command carries `--ci`: the CLI discovers the Intlayer
|
|
150
|
+
* project(s) of the repository and runs the command inside each of them, so
|
|
151
|
+
* the same workflow works for a single project and for a monorepo.
|
|
152
|
+
*
|
|
150
153
|
* AI access is required for `fill`. The workflow exposes both options:
|
|
151
154
|
* - a provider API key (`AI_API_KEY` secret), forwarded via CLI flags, or
|
|
152
155
|
* - Intlayer CMS access keys (`INTLAYER_CLIENT_ID` / `INTLAYER_CLIENT_SECRET`).
|
|
153
156
|
*/
|
|
154
157
|
const generateFillWorkflow = (packageManager, options) => {
|
|
155
158
|
const { setupSteps, installCommand, execCommand } = getPackageManagerCIConfig(packageManager, options.workingDirectory);
|
|
156
|
-
const intlayerCommand = options.useCiCommand ? `${execCommand} ci` : execCommand;
|
|
157
159
|
return `name: Intlayer Fill
|
|
158
160
|
# Auto-fill missing translations on every pull request.
|
|
159
161
|
on:
|
|
@@ -184,7 +186,7 @@ jobs:
|
|
|
184
186
|
# Option 2 — Use Intlayer CMS access keys instead of your own AI key.
|
|
185
187
|
# Wire them in your intlayer.config and uncomment the lines below:
|
|
186
188
|
# INTLAYER_CLIENT_ID: \${{ secrets.INTLAYER_CLIENT_ID }}
|
|
187
|
-
# INTLAYER_CLIENT_SECRET: \${{ secrets.INTLAYER_CLIENT_SECRET }}${getMonorepoCredentialsBlock(options.
|
|
189
|
+
# INTLAYER_CLIENT_SECRET: \${{ secrets.INTLAYER_CLIENT_SECRET }}${getMonorepoCredentialsBlock(options.isMonorepo)}
|
|
188
190
|
steps:
|
|
189
191
|
- name: ⬇️ Checkout repository
|
|
190
192
|
uses: actions/checkout@v4
|
|
@@ -195,12 +197,12 @@ ${setupSteps}
|
|
|
195
197
|
- name: 📦 Install dependencies
|
|
196
198
|
run: ${installCommand}
|
|
197
199
|
- name: ⚙️ Build dictionaries
|
|
198
|
-
run: ${
|
|
200
|
+
run: ${execCommand} build --ci
|
|
199
201
|
- name: 🤖 Fill missing translations
|
|
200
202
|
# Skip when no AI credentials are configured, so the workflow stays green
|
|
201
203
|
# until an \`AI_API_KEY\` (or Intlayer CMS access keys) secret is added.
|
|
202
204
|
if: \${{ env.AI_API_KEY != '' || env.INTLAYER_CLIENT_ID != '' }}
|
|
203
|
-
run: ${
|
|
205
|
+
run: ${execCommand} fill --ci --git-diff --mode complete --provider $AI_PROVIDER --model $AI_MODEL --api-key $AI_API_KEY
|
|
204
206
|
- name: 📤 Commit and push changes
|
|
205
207
|
run: |
|
|
206
208
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
|
@@ -220,7 +222,6 @@ ${setupSteps}
|
|
|
220
222
|
*/
|
|
221
223
|
const generateTestWorkflow = (packageManager, options) => {
|
|
222
224
|
const { setupSteps, installCommand, execCommand } = getPackageManagerCIConfig(packageManager, options.workingDirectory);
|
|
223
|
-
const intlayerCommand = options.useCiCommand ? `${execCommand} ci` : execCommand;
|
|
224
225
|
return `name: Intlayer Test
|
|
225
226
|
# Fail the pull request when required locales are missing translations.
|
|
226
227
|
on:
|
|
@@ -242,9 +243,9 @@ ${setupSteps}
|
|
|
242
243
|
- name: 📦 Install dependencies
|
|
243
244
|
run: ${installCommand}
|
|
244
245
|
- name: ⚙️ Build dictionaries
|
|
245
|
-
run: ${
|
|
246
|
+
run: ${execCommand} build --ci
|
|
246
247
|
- name: 🧪 Test for missing translations
|
|
247
|
-
run: ${
|
|
248
|
+
run: ${execCommand} test --ci
|
|
248
249
|
`;
|
|
249
250
|
};
|
|
250
251
|
/** Workflow file path constants, relative to the project root. */
|