@intlayer/engine 9.5.0 → 9.5.1
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/utils/devScript.cjs +2 -3
- package/dist/cjs/init/utils/devScript.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/utils/devScript.mjs +2 -3
- package/dist/esm/init/utils/devScript.mjs.map +1 -1
- package/dist/esm/utils/contentWatcherLock.mjs +4 -5
- package/dist/esm/utils/contentWatcherLock.mjs.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/utils/contentWatcherLock.d.ts +2 -13
- package/dist/types/utils/contentWatcherLock.d.ts.map +1 -1
- package/package.json +9 -9
|
@@ -18,9 +18,8 @@ const BACKEND_INTLAYER_PACKAGES = [
|
|
|
18
18
|
* project's script must be left alone.
|
|
19
19
|
*
|
|
20
20
|
* A Next.js app is always left alone: `withIntlayer` starts the content watcher
|
|
21
|
-
* itself
|
|
22
|
-
*
|
|
23
|
-
* add a second watcher rebuilding the same dictionaries. This is checked on the
|
|
21
|
+
* itself on both bundlers, so wrapping the dev server in `intlayer watch --with`
|
|
22
|
+
* would only add a second watcher rebuilding the same dictionaries. This is checked on the
|
|
24
23
|
* `next` dependency as well as on the config file, since `next.config.*` is
|
|
25
24
|
* optional, and it takes precedence over the backend list, which a Next.js app
|
|
26
25
|
* with a custom server (or a monorepo root) can match too.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devScript.cjs","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.cjs","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"}
|
|
@@ -72,8 +72,7 @@ const getContentWatcherOwner = async (configuration) => {
|
|
|
72
72
|
if (owner.pid && getIsProcessAlive(owner.pid)) return {
|
|
73
73
|
pid: owner.pid,
|
|
74
74
|
source: owner.source ?? "bundler",
|
|
75
|
-
label: owner.label ?? "Intlayer"
|
|
76
|
-
since: owner.since
|
|
75
|
+
label: owner.label ?? "Intlayer"
|
|
77
76
|
};
|
|
78
77
|
try {
|
|
79
78
|
await (0, node_fs_promises.rm)(lockFilePath, { force: true });
|
|
@@ -148,16 +147,16 @@ let hasReportedRedundantWatcher = false;
|
|
|
148
147
|
* per command, and callers may re-enter this on a retry loop.
|
|
149
148
|
*
|
|
150
149
|
* @param configuration - The resolved Intlayer configuration.
|
|
151
|
-
* @param report - Who is watching
|
|
150
|
+
* @param report - Who is watching in parallel with whom.
|
|
152
151
|
*/
|
|
153
|
-
const reportRedundantContentWatcher = (configuration, { cliLabel, bundlerLabel
|
|
152
|
+
const reportRedundantContentWatcher = (configuration, { cliLabel, bundlerLabel }) => {
|
|
154
153
|
if (hasReportedRedundantWatcher) return;
|
|
155
154
|
hasReportedRedundantWatcher = true;
|
|
156
155
|
(0, _intlayer_config_logger.getAppLogger)(configuration)([
|
|
157
156
|
(0, _intlayer_config_logger.colorize)(cliLabel, _intlayer_config_colors.BLUE, _intlayer_config_colors.BEIGE),
|
|
158
157
|
"is watching your content declarations in parallel with",
|
|
159
158
|
`${(0, _intlayer_config_logger.colorize)(bundlerLabel, _intlayer_config_colors.BLUE, _intlayer_config_colors.BEIGE)},`,
|
|
160
|
-
|
|
159
|
+
"which watches them on its own.",
|
|
161
160
|
"The parallel watch command is no longer needed — running your dev server on its own is enough."
|
|
162
161
|
], { level: "warn" });
|
|
163
162
|
};
|
|
@@ -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"}
|
|
@@ -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"}
|
|
@@ -69,8 +69,7 @@ const getContentWatcherOwner = async (configuration) => {
|
|
|
69
69
|
if (owner.pid && getIsProcessAlive(owner.pid)) return {
|
|
70
70
|
pid: owner.pid,
|
|
71
71
|
source: owner.source ?? "bundler",
|
|
72
|
-
label: owner.label ?? "Intlayer"
|
|
73
|
-
since: owner.since
|
|
72
|
+
label: owner.label ?? "Intlayer"
|
|
74
73
|
};
|
|
75
74
|
try {
|
|
76
75
|
await rm(lockFilePath, { force: true });
|
|
@@ -145,16 +144,16 @@ let hasReportedRedundantWatcher = false;
|
|
|
145
144
|
* per command, and callers may re-enter this on a retry loop.
|
|
146
145
|
*
|
|
147
146
|
* @param configuration - The resolved Intlayer configuration.
|
|
148
|
-
* @param report - Who is watching
|
|
147
|
+
* @param report - Who is watching in parallel with whom.
|
|
149
148
|
*/
|
|
150
|
-
const reportRedundantContentWatcher = (configuration, { cliLabel, bundlerLabel
|
|
149
|
+
const reportRedundantContentWatcher = (configuration, { cliLabel, bundlerLabel }) => {
|
|
151
150
|
if (hasReportedRedundantWatcher) return;
|
|
152
151
|
hasReportedRedundantWatcher = true;
|
|
153
152
|
getAppLogger(configuration)([
|
|
154
153
|
colorize(cliLabel, ANSIColors.BLUE, ANSIColors.BEIGE),
|
|
155
154
|
"is watching your content declarations in parallel with",
|
|
156
155
|
`${colorize(bundlerLabel, ANSIColors.BLUE, ANSIColors.BEIGE)},`,
|
|
157
|
-
|
|
156
|
+
"which watches them on its own.",
|
|
158
157
|
"The parallel watch command is no longer needed — running your dev server on its own is enough."
|
|
159
158
|
], { level: "warn" });
|
|
160
159
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contentWatcherLock.mjs","names":[],"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,OAAO,mBAAmB,EAAE,OAAO,KAAK,CAAC;CAC3C,QAAQ,CAAC;AACX,CAAC;;;;;;;;;;;;AAaD,MAAa,iCACX,kBACW,KAAK,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,MAAM,SAAS,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,MAAM,GAAG,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,MAAM,MAAM,QAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;EACtD,MAAM,UAAU,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,aAAa,aAAa,CAAC,CACzB;EACE,SAAS,UAAU,WAAW,MAAM,WAAW,KAAK;EACpD;EACA,GAAG,SAAS,cAAc,WAAW,MAAM,WAAW,KAAK,EAAE;EAC7D,QACI,uCAAuC,SAAS,GAAG,aAAa,GAAG,SAAS,WAAW,YAAY,WAAW,KAAK,EAAE,KACrH;EACJ;CACF,GACA,EAAE,OAAO,OAAO,CAClB;AACF"}
|
|
1
|
+
{"version":3,"file":"contentWatcherLock.mjs","names":[],"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,OAAO,mBAAmB,EAAE,OAAO,KAAK,CAAC;CAC3C,QAAQ,CAAC;AACX,CAAC;;;;;;;;;;;;AAaD,MAAa,iCACX,kBACW,KAAK,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,MAAM,SAAS,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,MAAM,GAAG,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,MAAM,MAAM,QAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;EACtD,MAAM,UAAU,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,aAAa,aAAa,CAAC,CACzB;EACE,SAAS,UAAU,WAAW,MAAM,WAAW,KAAK;EACpD;EACA,GAAG,SAAS,cAAc,WAAW,MAAM,WAAW,KAAK,EAAE;EAC7D;EACA;CACF,GACA,EAAE,OAAO,OAAO,CAClB;AACF"}
|
|
@@ -19,9 +19,8 @@ export type ResolveDevScriptParams = {
|
|
|
19
19
|
* project's script must be left alone.
|
|
20
20
|
*
|
|
21
21
|
* A Next.js app is always left alone: `withIntlayer` starts the content watcher
|
|
22
|
-
* itself
|
|
23
|
-
*
|
|
24
|
-
* add a second watcher rebuilding the same dictionaries. This is checked on the
|
|
22
|
+
* itself on both bundlers, so wrapping the dev server in `intlayer watch --with`
|
|
23
|
+
* would only add a second watcher rebuilding the same dictionaries. This is checked on the
|
|
25
24
|
* `next` dependency as well as on the config file, since `next.config.*` is
|
|
26
25
|
* optional, and it takes precedence over the backend list, which a Next.js app
|
|
27
26
|
* with a custom server (or a monorepo root) can match too.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devScript.d.ts","names":[],"sources":["../../../../src/init/utils/devScript.ts"],"mappings":";;;;;;;qBAMa;YAQD;;EAEV;;EAEA,SAAS;;EAET
|
|
1
|
+
{"version":3,"file":"devScript.d.ts","names":[],"sources":["../../../../src/init/utils/devScript.ts"],"mappings":";;;;;;;qBAMa;YAQD;;EAEV;;EAEA,SAAS;;EAET;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAgCW,qBAAgB,WAAA,SAAA,mBAI1B"}
|
|
@@ -16,12 +16,6 @@ export type ContentWatcherOwner = {
|
|
|
16
16
|
* `next-intlayer`.
|
|
17
17
|
*/
|
|
18
18
|
label: string;
|
|
19
|
-
/**
|
|
20
|
-
* Release from which a `bundler` owner watches the content declarations on
|
|
21
|
-
* its own, so a CLI watcher finding it can name the version that made the
|
|
22
|
-
* parallel command redundant without keeping a table of its own.
|
|
23
|
-
*/
|
|
24
|
-
since?: string;
|
|
25
19
|
};
|
|
26
20
|
/**
|
|
27
21
|
* Set on every process spawned by `intlayer watch --with` (and
|
|
@@ -92,11 +86,6 @@ type RedundantWatcherReport = {
|
|
|
92
86
|
cliLabel: string;
|
|
93
87
|
/** Name of the bundler integration watching, e.g. `next-intlayer`. */
|
|
94
88
|
bundlerLabel: string;
|
|
95
|
-
/**
|
|
96
|
-
* Release from which the bundler integration watches on its own. Omitted when
|
|
97
|
-
* the integration did not report one.
|
|
98
|
-
*/
|
|
99
|
-
since?: string;
|
|
100
89
|
};
|
|
101
90
|
/**
|
|
102
91
|
* Reports a CLI watcher running alongside a bundler integration that watches
|
|
@@ -106,8 +95,8 @@ type RedundantWatcherReport = {
|
|
|
106
95
|
* per command, and callers may re-enter this on a retry loop.
|
|
107
96
|
*
|
|
108
97
|
* @param configuration - The resolved Intlayer configuration.
|
|
109
|
-
* @param report - Who is watching
|
|
98
|
+
* @param report - Who is watching in parallel with whom.
|
|
110
99
|
*/
|
|
111
|
-
export declare const reportRedundantContentWatcher: (configuration: IntlayerConfig, { cliLabel, bundlerLabel
|
|
100
|
+
export declare const reportRedundantContentWatcher: (configuration: IntlayerConfig, { cliLabel, bundlerLabel }: RedundantWatcherReport) => void;
|
|
112
101
|
//#endregion
|
|
113
102
|
//# sourceMappingURL=contentWatcherLock.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contentWatcherLock.d.ts","names":[],"sources":["../../../src/utils/contentWatcherLock.ts"],"mappings":";;;;;;;;YAaY;YAEA;;EAEV;EACA,QAAQ;;;;;EAKR
|
|
1
|
+
{"version":3,"file":"contentWatcherLock.d.ts","names":[],"sources":["../../../src/utils/contentWatcherLock.ts"],"mappings":";;;;;;;;YAaY;YAEA;;EAEV;EACA,QAAQ;;;;;EAKR;;;;;;;;;;qBAWW;;;;;;;;;;;;qBAkCA,gCAA6B,eACzB;;;;;;;;;qBA2BJ,yBAAsB,eAClB,mBACd,QAAQ;;;;;;;;;;;;;qBAsCE,4BAAyB,eACrB,gBAAc,OACtB,KAAK,gCACX;;;;;qBAkCU;;;;;;;;;;;;;;;;qBAkBA,yBAAsB,eAClB,gBAAc,kBAE5B,QAAQ;KAoBN;;EAEH;;EAEA;;;;;;;;;;;;qBAaW,gCAA6B,eACzB,kBAAc,UAAA,gBACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/engine",
|
|
3
|
-
"version": "9.5.
|
|
3
|
+
"version": "9.5.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Scans and builds Intlayer declaration files into dictionaries based on Intlayer configuration.",
|
|
6
6
|
"keywords": [
|
|
@@ -141,13 +141,13 @@
|
|
|
141
141
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
142
142
|
},
|
|
143
143
|
"dependencies": {
|
|
144
|
-
"@intlayer/api": "9.5.
|
|
145
|
-
"@intlayer/config": "9.5.
|
|
146
|
-
"@intlayer/core": "9.5.
|
|
147
|
-
"@intlayer/dictionaries-entry": "9.5.
|
|
148
|
-
"@intlayer/remote-dictionaries-entry": "9.5.
|
|
149
|
-
"@intlayer/types": "9.5.
|
|
150
|
-
"@intlayer/unmerged-dictionaries-entry": "9.5.
|
|
144
|
+
"@intlayer/api": "9.5.1",
|
|
145
|
+
"@intlayer/config": "9.5.1",
|
|
146
|
+
"@intlayer/core": "9.5.1",
|
|
147
|
+
"@intlayer/dictionaries-entry": "9.5.1",
|
|
148
|
+
"@intlayer/remote-dictionaries-entry": "9.5.1",
|
|
149
|
+
"@intlayer/types": "9.5.1",
|
|
150
|
+
"@intlayer/unmerged-dictionaries-entry": "9.5.1",
|
|
151
151
|
"@parcel/watcher": "2.6.0",
|
|
152
152
|
"defu": "6.1.7",
|
|
153
153
|
"fast-glob": "3.3.3",
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
"zod": "4.5.4"
|
|
168
168
|
},
|
|
169
169
|
"peerDependencies": {
|
|
170
|
-
"@intlayer/babel": "9.5.
|
|
170
|
+
"@intlayer/babel": "9.5.1"
|
|
171
171
|
},
|
|
172
172
|
"peerDependenciesMeta": {
|
|
173
173
|
"@intlayer/babel": {
|