@intlayer/cli 9.5.9-canary.0 → 9.5.10
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/_virtual/_utils_asset.cjs +1 -1
- package/dist/cjs/auth/login.cjs +4 -4
- package/dist/cjs/build.cjs +1 -1
- package/dist/cjs/ci.cjs +1 -1
- package/dist/cjs/cli.cjs +9 -4
- package/dist/cjs/cli.cjs.map +1 -1
- package/dist/cjs/config.cjs +1 -1
- package/dist/cjs/editor.cjs +1 -1
- package/dist/cjs/extract.cjs +5 -5
- package/dist/cjs/fill/fill.cjs +2 -2
- package/dist/cjs/fill/formatAutoFilledFilePath.cjs +1 -1
- package/dist/cjs/fill/listTranslationsTasks.cjs +1 -1
- package/dist/cjs/fill/translateDictionary.cjs +1 -1
- package/dist/cjs/fill/writeFill.cjs +1 -1
- package/dist/cjs/getTargetDictionary.cjs +1 -1
- package/dist/cjs/index.cjs +21 -7
- package/dist/cjs/init.cjs +111 -39
- package/dist/cjs/init.cjs.map +1 -1
- package/dist/cjs/initChromeExtension.cjs +36 -0
- package/dist/cjs/initChromeExtension.cjs.map +1 -0
- package/dist/cjs/initInfra.cjs +1 -1
- package/dist/cjs/initInfra.cjs.map +1 -1
- package/dist/cjs/initSkills.cjs +1 -1
- package/dist/cjs/listContentDeclaration.cjs +2 -2
- package/dist/cjs/liveSync.cjs +4 -4
- package/dist/cjs/pull.cjs +4 -4
- package/dist/cjs/push/push.cjs +5 -5
- package/dist/cjs/pushConfig.cjs +1 -1
- package/dist/cjs/reviewDoc/reviewDoc.cjs +3 -3
- package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs +3 -3
- package/dist/cjs/reviewDoc/reviewDocLog.cjs +1 -1
- package/dist/cjs/searchDoc.cjs +2 -1
- package/dist/cjs/searchDoc.cjs.map +1 -1
- package/dist/cjs/test/listMissingTranslations.cjs +1 -1
- package/dist/cjs/test/test.cjs +1 -1
- package/dist/cjs/translateDoc/translateDoc.cjs +2 -2
- package/dist/cjs/translateDoc/translateFile.cjs +2 -2
- package/dist/cjs/utils/getOutputFilePath.cjs +1 -1
- package/dist/cjs/utils/getParentPackageJSON.cjs +1 -1
- package/dist/cjs/utils/selectCmsEnvironment.cjs +1 -1
- package/dist/cjs/utils/selectCmsEnvironment.cjs.map +1 -1
- package/dist/cjs/watch.cjs +1 -1
- package/dist/esm/_virtual/_utils_asset.mjs +1 -1
- package/dist/esm/auth/login.mjs +3 -3
- package/dist/esm/build.mjs +1 -1
- package/dist/esm/ci.mjs +1 -1
- package/dist/esm/cli.mjs +9 -4
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/config.mjs +1 -1
- package/dist/esm/editor.mjs +1 -1
- package/dist/esm/extract.mjs +6 -6
- package/dist/esm/extract.mjs.map +1 -1
- package/dist/esm/fill/fill.mjs +4 -4
- package/dist/esm/fill/fill.mjs.map +1 -1
- package/dist/esm/fill/formatAutoFilledFilePath.mjs +1 -1
- package/dist/esm/fill/listTranslationsTasks.mjs +1 -1
- package/dist/esm/fill/translateDictionary.mjs +1 -1
- package/dist/esm/fill/writeFill.mjs +1 -1
- package/dist/esm/getTargetDictionary.mjs +1 -1
- package/dist/esm/index.mjs +10 -9
- package/dist/esm/init.mjs +104 -41
- package/dist/esm/init.mjs.map +1 -1
- package/dist/esm/initChromeExtension.mjs +32 -0
- package/dist/esm/initChromeExtension.mjs.map +1 -0
- package/dist/esm/initInfra.mjs +1 -1
- package/dist/esm/initInfra.mjs.map +1 -1
- package/dist/esm/initSkills.mjs +1 -1
- package/dist/esm/listContentDeclaration.mjs +2 -2
- package/dist/esm/liveSync.mjs +3 -3
- package/dist/esm/pull.mjs +4 -4
- package/dist/esm/push/push.mjs +7 -7
- package/dist/esm/push/push.mjs.map +1 -1
- package/dist/esm/pushConfig.mjs +1 -1
- package/dist/esm/reviewDoc/reviewDoc.mjs +3 -3
- package/dist/esm/reviewDoc/reviewDocBlockAware.mjs +3 -3
- package/dist/esm/reviewDoc/reviewDocLog.mjs +1 -1
- package/dist/esm/searchDoc.mjs +2 -1
- package/dist/esm/searchDoc.mjs.map +1 -1
- package/dist/esm/test/listMissingTranslations.mjs +1 -1
- package/dist/esm/test/test.mjs +1 -1
- package/dist/esm/translateDoc/translateDoc.mjs +2 -2
- package/dist/esm/translateDoc/translateFile.mjs +2 -2
- package/dist/esm/utils/getOutputFilePath.mjs +1 -1
- package/dist/esm/utils/getParentPackageJSON.mjs +1 -1
- package/dist/esm/utils/selectCmsEnvironment.mjs +1 -1
- package/dist/esm/utils/selectCmsEnvironment.mjs.map +1 -1
- package/dist/esm/watch.mjs +1 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/init.d.ts +43 -1
- package/dist/types/init.d.ts.map +1 -1
- package/dist/types/initChromeExtension.d.ts +11 -0
- package/dist/types/initChromeExtension.d.ts.map +1 -0
- package/dist/types/searchDoc.d.ts.map +1 -1
- package/package.json +11 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fill.mjs","names":[],"sources":["../../../src/fill/fill.ts"],"sourcesContent":["import { basename, join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/api';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeKey,\n colorizePath,\n getAppLogger,\n x,\n} from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport {\n loadContentDeclarations,\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/engine/build';\nimport {\n type ListGitFilesOptions,\n logConfigDetails,\n} from '@intlayer/engine/cli';\nimport {\n formatPath,\n getGlobalLimiter,\n getTaskLimiter,\n} from '@intlayer/engine/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { Fill } from '@intlayer/types/dictionary';\nimport {\n ensureArray,\n type GetTargetDictionaryOptions,\n getTargetUnmergedDictionaries,\n} from '../getTargetDictionary';\nimport { setupAI } from '../utils/setupAI';\nimport {\n listTranslationsTasks,\n type TranslationTask,\n} from './listTranslationsTasks';\nimport { translateDictionary } from './translateDictionary';\nimport { writeFill } from './writeFill';\n\nconst NB_CONCURRENT_TRANSLATIONS = 7;\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locale;\n outputLocales?: Locale | Locale[];\n mode?: 'complete' | 'review';\n gitOptions?: ListGitFilesOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n nbConcurrentTranslations?: number;\n nbConcurrentTasks?: number; // NEW: number of tasks that may run at once\n build?: boolean;\n skipMetadata?: boolean;\n} & GetTargetDictionaryOptions;\n\n/**\n * Fill translations based on the provided options.\n */\nexport const fill = async (options?: FillOptions): Promise<void> => {\n const configuration = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n const appLogger = getAppLogger(configuration);\n\n if (options?.build === true) {\n await prepareIntlayer(configuration, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(configuration);\n }\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options?.mode ?? 'complete';\n const baseLocale = options?.sourceLocale ?? defaultLocale;\n\n const outputLocales = options?.outputLocales\n ? ensureArray(options.outputLocales)\n : locales;\n\n const aiResult = await setupAI(configuration, options?.aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig, isCustomAI } = aiResult;\n\n if (isCustomAI && aiClient && aiConfig) {\n const { hasAIAccess, error } = await aiClient.checkAISDKAccess(aiConfig);\n if (!hasAIAccess) {\n appLogger(`${x} ${error}`);\n return;\n }\n }\n\n const targetUnmergedDictionaries =\n await getTargetUnmergedDictionaries(options);\n\n // Load the original source content declaration files to recover function-type\n // `fill` values that are lost when dictionaries are JSON-serialised into\n // unmerged_dictionaries.cjs. Dictionary-level fill takes priority over the\n // config-level fill, but we can only know that by reading the source files.\n const uniqueSourcePaths = [\n ...new Set(\n targetUnmergedDictionaries\n .filter((unmergedDictionary) =>\n ['local', 'hybrid'].includes(unmergedDictionary.location!)\n )\n .map((unmergedDictionary) => unmergedDictionary.filePath)\n .filter(Boolean) as string[]\n ),\n ];\n const sourceDictionaries = await loadContentDeclarations(\n uniqueSourcePaths.map((sourcePath) =>\n join(configuration.system.baseDir, sourcePath)\n ),\n configuration,\n undefined,\n {\n logError: false,\n }\n );\n // Map relative filePath → original fill value from the source file\n const originalFillByPath = new Map<string, Fill | undefined>();\n\n for (const dictionary of sourceDictionaries) {\n if (dictionary.filePath) {\n originalFillByPath.set(\n dictionary.filePath,\n dictionary.fill as Fill | undefined\n );\n }\n }\n\n const affectedDictionaryKeys = new Set<string>();\n\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n const keysToProcess = Array.from(affectedDictionaryKeys);\n\n appLogger([\n 'Affected dictionary keys for processing:',\n keysToProcess.length > 0\n ? keysToProcess.map((key) => colorizeKey(key)).join(', ')\n : colorize('No keys found', ANSIColors.YELLOW),\n ]);\n\n if (keysToProcess.length === 0) return;\n\n /**\n * List the translations tasks\n *\n * Create a list of per-locale dictionaries to translate\n *\n * In 'complete' mode, filter only the missing locales to translate\n */\n const translationTasks: TranslationTask[] = listTranslationsTasks(\n targetUnmergedDictionaries.map((dictionary) => dictionary.localId!),\n outputLocales,\n mode,\n baseLocale,\n configuration\n );\n\n // AI calls in flight at once (translateJSON + metadata audit)\n const nbConcurrentTranslations =\n options?.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS;\n const globalLimiter = getGlobalLimiter(nbConcurrentTranslations);\n\n // NEW: number of *tasks* that may run at once (start/prepare/log/write)\n const nbConcurrentTasks = Math.max(\n 1,\n Math.min(\n options?.nbConcurrentTasks ?? nbConcurrentTranslations,\n translationTasks.length\n )\n );\n\n const taskLimiter = getTaskLimiter(nbConcurrentTasks);\n\n const runners = translationTasks.map((task) =>\n taskLimiter(async () => {\n const relativePath = relative(\n configuration?.system?.baseDir ?? process.cwd(),\n task?.dictionaryFilePath ?? ''\n );\n\n // log AFTER acquiring a task slot\n appLogger(\n `${task.dictionaryPreset} Processing ${colorizePath(basename(relativePath))}`,\n { level: 'info' }\n );\n\n const translationTaskResult = await translateDictionary(\n task,\n configuration,\n {\n mode,\n aiOptions: options?.aiOptions,\n fillMetadata: !options?.skipMetadata,\n onHandle: globalLimiter,\n aiClient,\n aiConfig,\n }\n );\n\n if (!translationTaskResult?.dictionaryOutput) return;\n\n const { dictionaryOutput, sourceLocale } = translationTaskResult;\n\n // Determine if we should write to separate files\n // - If dictionary has explicit fill setting (string, function, or object), use it\n // - If dictionary is per-locale AND has no explicit fill=false, use global fill config\n // - If dictionary is multilingual (no locale property), always write to same file\n //\n // NOTE: function-type fill values are lost during JSON serialisation of\n // unmerged_dictionaries.cjs. We recover them by checking the original\n // source file that was loaded above (originalFillByPath). Dictionary-level\n // fill always takes priority over config-level fill.\n const originalFill = originalFillByPath.get(\n dictionaryOutput.filePath ?? ''\n );\n\n // originalFill is undefined when the source file had no fill property; use\n // the (possibly JSON-preserved) dictionaryOutput.fill as a fallback so that\n // string/boolean fill values set directly on the dict still work.\n const dictFill: Fill | undefined =\n originalFill !== undefined ? originalFill : dictionaryOutput.fill;\n\n const hasDictionaryLevelFill =\n typeof dictFill === 'string' ||\n typeof dictFill === 'function' ||\n (typeof dictFill === 'object' && dictFill !== null);\n\n const isPerLocale = typeof dictionaryOutput.locale === 'string';\n\n const effectiveFill = hasDictionaryLevelFill\n ? dictFill\n : isPerLocale\n ? (configuration.dictionary?.fill ?? true)\n : false; // Multilingual dictionaries (no locale property) always write to same file unless explicitly set on the dictionary\n\n const isFillOtherFile =\n typeof effectiveFill === 'string' ||\n typeof effectiveFill === 'function' ||\n (typeof effectiveFill === 'object' && effectiveFill !== null);\n\n if (isFillOtherFile) {\n await writeFill(\n {\n ...dictionaryOutput,\n // Ensure fill is set on the dictionary for writeFill to use\n fill: effectiveFill,\n },\n outputLocales,\n [sourceLocale],\n configuration\n );\n } else {\n await writeContentDeclaration(dictionaryOutput, configuration);\n\n if (dictionaryOutput.filePath) {\n appLogger(\n `${task.dictionaryPreset} Content declaration written to ${formatPath(basename(dictionaryOutput.filePath))}`,\n { level: 'info' }\n );\n }\n }\n })\n );\n\n await Promise.all(runners);\n await (globalLimiter as any).onIdle();\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fill.mjs","names":["prepareIntlayer"],"sources":["../../../src/fill/fill.ts"],"sourcesContent":["import { basename, join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/api';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeKey,\n colorizePath,\n getAppLogger,\n x,\n} from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport {\n loadContentDeclarations,\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/engine/build';\nimport {\n type ListGitFilesOptions,\n logConfigDetails,\n} from '@intlayer/engine/cli';\nimport {\n formatPath,\n getGlobalLimiter,\n getTaskLimiter,\n} from '@intlayer/engine/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { Fill } from '@intlayer/types/dictionary';\nimport {\n ensureArray,\n type GetTargetDictionaryOptions,\n getTargetUnmergedDictionaries,\n} from '../getTargetDictionary';\nimport { setupAI } from '../utils/setupAI';\nimport {\n listTranslationsTasks,\n type TranslationTask,\n} from './listTranslationsTasks';\nimport { translateDictionary } from './translateDictionary';\nimport { writeFill } from './writeFill';\n\nconst NB_CONCURRENT_TRANSLATIONS = 7;\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locale;\n outputLocales?: Locale | Locale[];\n mode?: 'complete' | 'review';\n gitOptions?: ListGitFilesOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n nbConcurrentTranslations?: number;\n nbConcurrentTasks?: number; // NEW: number of tasks that may run at once\n build?: boolean;\n skipMetadata?: boolean;\n} & GetTargetDictionaryOptions;\n\n/**\n * Fill translations based on the provided options.\n */\nexport const fill = async (options?: FillOptions): Promise<void> => {\n const configuration = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n const appLogger = getAppLogger(configuration);\n\n if (options?.build === true) {\n await prepareIntlayer(configuration, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(configuration);\n }\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options?.mode ?? 'complete';\n const baseLocale = options?.sourceLocale ?? defaultLocale;\n\n const outputLocales = options?.outputLocales\n ? ensureArray(options.outputLocales)\n : locales;\n\n const aiResult = await setupAI(configuration, options?.aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig, isCustomAI } = aiResult;\n\n if (isCustomAI && aiClient && aiConfig) {\n const { hasAIAccess, error } = await aiClient.checkAISDKAccess(aiConfig);\n if (!hasAIAccess) {\n appLogger(`${x} ${error}`);\n return;\n }\n }\n\n const targetUnmergedDictionaries =\n await getTargetUnmergedDictionaries(options);\n\n // Load the original source content declaration files to recover function-type\n // `fill` values that are lost when dictionaries are JSON-serialised into\n // unmerged_dictionaries.cjs. Dictionary-level fill takes priority over the\n // config-level fill, but we can only know that by reading the source files.\n const uniqueSourcePaths = [\n ...new Set(\n targetUnmergedDictionaries\n .filter((unmergedDictionary) =>\n ['local', 'hybrid'].includes(unmergedDictionary.location!)\n )\n .map((unmergedDictionary) => unmergedDictionary.filePath)\n .filter(Boolean) as string[]\n ),\n ];\n const sourceDictionaries = await loadContentDeclarations(\n uniqueSourcePaths.map((sourcePath) =>\n join(configuration.system.baseDir, sourcePath)\n ),\n configuration,\n undefined,\n {\n logError: false,\n }\n );\n // Map relative filePath → original fill value from the source file\n const originalFillByPath = new Map<string, Fill | undefined>();\n\n for (const dictionary of sourceDictionaries) {\n if (dictionary.filePath) {\n originalFillByPath.set(\n dictionary.filePath,\n dictionary.fill as Fill | undefined\n );\n }\n }\n\n const affectedDictionaryKeys = new Set<string>();\n\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n const keysToProcess = Array.from(affectedDictionaryKeys);\n\n appLogger([\n 'Affected dictionary keys for processing:',\n keysToProcess.length > 0\n ? keysToProcess.map((key) => colorizeKey(key)).join(', ')\n : colorize('No keys found', ANSIColors.YELLOW),\n ]);\n\n if (keysToProcess.length === 0) return;\n\n /**\n * List the translations tasks\n *\n * Create a list of per-locale dictionaries to translate\n *\n * In 'complete' mode, filter only the missing locales to translate\n */\n const translationTasks: TranslationTask[] = listTranslationsTasks(\n targetUnmergedDictionaries.map((dictionary) => dictionary.localId!),\n outputLocales,\n mode,\n baseLocale,\n configuration\n );\n\n // AI calls in flight at once (translateJSON + metadata audit)\n const nbConcurrentTranslations =\n options?.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS;\n const globalLimiter = getGlobalLimiter(nbConcurrentTranslations);\n\n // NEW: number of *tasks* that may run at once (start/prepare/log/write)\n const nbConcurrentTasks = Math.max(\n 1,\n Math.min(\n options?.nbConcurrentTasks ?? nbConcurrentTranslations,\n translationTasks.length\n )\n );\n\n const taskLimiter = getTaskLimiter(nbConcurrentTasks);\n\n const runners = translationTasks.map((task) =>\n taskLimiter(async () => {\n const relativePath = relative(\n configuration?.system?.baseDir ?? process.cwd(),\n task?.dictionaryFilePath ?? ''\n );\n\n // log AFTER acquiring a task slot\n appLogger(\n `${task.dictionaryPreset} Processing ${colorizePath(basename(relativePath))}`,\n { level: 'info' }\n );\n\n const translationTaskResult = await translateDictionary(\n task,\n configuration,\n {\n mode,\n aiOptions: options?.aiOptions,\n fillMetadata: !options?.skipMetadata,\n onHandle: globalLimiter,\n aiClient,\n aiConfig,\n }\n );\n\n if (!translationTaskResult?.dictionaryOutput) return;\n\n const { dictionaryOutput, sourceLocale } = translationTaskResult;\n\n // Determine if we should write to separate files\n // - If dictionary has explicit fill setting (string, function, or object), use it\n // - If dictionary is per-locale AND has no explicit fill=false, use global fill config\n // - If dictionary is multilingual (no locale property), always write to same file\n //\n // NOTE: function-type fill values are lost during JSON serialisation of\n // unmerged_dictionaries.cjs. We recover them by checking the original\n // source file that was loaded above (originalFillByPath). Dictionary-level\n // fill always takes priority over config-level fill.\n const originalFill = originalFillByPath.get(\n dictionaryOutput.filePath ?? ''\n );\n\n // originalFill is undefined when the source file had no fill property; use\n // the (possibly JSON-preserved) dictionaryOutput.fill as a fallback so that\n // string/boolean fill values set directly on the dict still work.\n const dictFill: Fill | undefined =\n originalFill !== undefined ? originalFill : dictionaryOutput.fill;\n\n const hasDictionaryLevelFill =\n typeof dictFill === 'string' ||\n typeof dictFill === 'function' ||\n (typeof dictFill === 'object' && dictFill !== null);\n\n const isPerLocale = typeof dictionaryOutput.locale === 'string';\n\n const effectiveFill = hasDictionaryLevelFill\n ? dictFill\n : isPerLocale\n ? (configuration.dictionary?.fill ?? true)\n : false; // Multilingual dictionaries (no locale property) always write to same file unless explicitly set on the dictionary\n\n const isFillOtherFile =\n typeof effectiveFill === 'string' ||\n typeof effectiveFill === 'function' ||\n (typeof effectiveFill === 'object' && effectiveFill !== null);\n\n if (isFillOtherFile) {\n await writeFill(\n {\n ...dictionaryOutput,\n // Ensure fill is set on the dictionary for writeFill to use\n fill: effectiveFill,\n },\n outputLocales,\n [sourceLocale],\n configuration\n );\n } else {\n await writeContentDeclaration(dictionaryOutput, configuration);\n\n if (dictionaryOutput.filePath) {\n appLogger(\n `${task.dictionaryPreset} Content declaration written to ${formatPath(basename(dictionaryOutput.filePath))}`,\n { level: 'info' }\n );\n }\n }\n })\n );\n\n await Promise.all(runners);\n await (globalLimiter as any).onIdle();\n};\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { basename, dirname, isAbsolute, normalize, resolve } from "node:path";
|
|
2
2
|
import { GREY_DARK } from "@intlayer/config/colors";
|
|
3
3
|
import { colorize, colorizePath } from "@intlayer/config/logger";
|
|
4
|
-
import { parseFilePathPattern } from "@intlayer/config/utils";
|
|
5
4
|
import { getFormatFromExtension } from "@intlayer/engine/utils";
|
|
5
|
+
import { parseFilePathPattern } from "@intlayer/config/utils";
|
|
6
6
|
|
|
7
7
|
//#region src/fill/formatAutoFilledFilePath.ts
|
|
8
8
|
const formatAutoFilledFilePath = async (autoFillField, dictionaryKey, dictionaryFilePath, baseDir, configuration, locale) => {
|
|
@@ -2,8 +2,8 @@ import { listMissingTranslationsWithConfig } from "../test/listMissingTranslatio
|
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
import * as ANSIColors from "@intlayer/config/colors";
|
|
4
4
|
import { colon, colorize, colorizeKey, colorizePath, getAppLogger } from "@intlayer/config/logger";
|
|
5
|
-
import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
|
|
6
5
|
import { formatLocale } from "@intlayer/engine/utils";
|
|
6
|
+
import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
|
|
7
7
|
import { getFilterTranslationsOnlyDictionary } from "@intlayer/core/plugins";
|
|
8
8
|
import { getDictionaries } from "@intlayer/dictionaries-entry";
|
|
9
9
|
|
|
@@ -4,9 +4,9 @@ import { extractTranslatableContent, reinsertTranslatedContent } from "./extract
|
|
|
4
4
|
import { basename } from "node:path";
|
|
5
5
|
import * as ANSIColors from "@intlayer/config/colors";
|
|
6
6
|
import { colon, colorize, colorizeNumber, colorizePath, getAppLogger } from "@intlayer/config/logger";
|
|
7
|
+
import { chunkJSON, excludeObjectFormat, formatLocale, mergeChunks, reconstructFromSingleChunk, verifyIdenticObjectFormat } from "@intlayer/engine/utils";
|
|
7
8
|
import { retryManager } from "@intlayer/config/utils";
|
|
8
9
|
import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
|
|
9
|
-
import { chunkJSON, excludeObjectFormat, formatLocale, mergeChunks, reconstructFromSingleChunk, verifyIdenticObjectFormat } from "@intlayer/engine/utils";
|
|
10
10
|
import { getFilterMissingTranslationsDictionary, getMultilingualDictionary, getPerLocaleDictionary, insertContentInDictionary } from "@intlayer/core/plugins";
|
|
11
11
|
|
|
12
12
|
//#region src/fill/translateDictionary.ts
|
|
@@ -2,8 +2,8 @@ import { formatFillData } from "./formatFillData.mjs";
|
|
|
2
2
|
import { getAvailableLocalesInDictionary } from "./getAvailableLocalesInDictionary.mjs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { colorizeKey, getAppLogger } from "@intlayer/config/logger";
|
|
5
|
-
import { writeContentDeclaration } from "@intlayer/engine/build";
|
|
6
5
|
import { formatLocale, formatPath } from "@intlayer/engine/utils";
|
|
6
|
+
import { writeContentDeclaration } from "@intlayer/engine/build";
|
|
7
7
|
import { getDictionaries } from "@intlayer/dictionaries-entry";
|
|
8
8
|
|
|
9
9
|
//#region src/fill/writeFill.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join, relative } from "node:path";
|
|
2
|
+
import { listGitFiles } from "@intlayer/engine/cli";
|
|
2
3
|
import { getConfiguration } from "@intlayer/config/node";
|
|
3
4
|
import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
|
|
4
|
-
import { listGitFiles } from "@intlayer/engine/cli";
|
|
5
5
|
|
|
6
6
|
//#region src/getTargetDictionary.ts
|
|
7
7
|
const ensureArray = (value) => [value].flat();
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { extract } from "./extract.mjs";
|
|
3
|
-
import { startEditor } from "./editor.mjs";
|
|
4
|
-
import { liveSync } from "./liveSync.mjs";
|
|
1
|
+
import { BROWSER_EXTENSION_OPTIONS, CHROME_EXTENSION_URL, MOZILLA_EXTENSION_URL, initChromeExtension } from "./initChromeExtension.mjs";
|
|
5
2
|
import { INFRA_MODES, INSTALL_SCRIPT_URL, INSTALL_SCRIPT_URL_WINDOWS, initInfra, parseInfraMode } from "./initInfra.mjs";
|
|
6
3
|
import { PLATFORM_OPTIONS, getDetectedPlatform, initSkills, promptPlatform } from "./initSkills.mjs";
|
|
7
|
-
import { findProjectRoot, init } from "./init.mjs";
|
|
4
|
+
import { ESLINT_CONFIG_FILES, INIT_STEP_GROUPS, LOCALE_ROUTING_CHOICES, OPT_IN_INIT_STEPS, findProjectRoot, getInitialInitSteps, getProjectDependencies, getRoutingInitOptions, init, isEslintInstalled, parseLocaleRoutingChoice } from "./init.mjs";
|
|
8
5
|
import { build } from "./build.mjs";
|
|
9
|
-
import {
|
|
6
|
+
import { listProjectsCommand } from "./listProjects.mjs";
|
|
7
|
+
import { startEditor } from "./editor.mjs";
|
|
8
|
+
import { scan } from "./scan.mjs";
|
|
10
9
|
import { dirname, setAPI } from "./cli.mjs";
|
|
10
|
+
import { extract } from "./extract.mjs";
|
|
11
|
+
import { bundle } from "./bundle.mjs";
|
|
11
12
|
import { listMissingTranslations, listMissingTranslationsWithConfig } from "./test/listMissingTranslations.mjs";
|
|
12
13
|
import { testMissingTranslations } from "./test/test.mjs";
|
|
13
14
|
import { fill } from "./fill/fill.mjs";
|
|
14
15
|
import { listContentDeclaration, listContentDeclarationRows } from "./listContentDeclaration.mjs";
|
|
15
|
-
import {
|
|
16
|
+
import { liveSync } from "./liveSync.mjs";
|
|
17
|
+
import { pull } from "./pull.mjs";
|
|
16
18
|
import { push } from "./push/push.mjs";
|
|
17
19
|
import { pushConfig } from "./pushConfig.mjs";
|
|
18
20
|
import { reviewDoc } from "./reviewDoc/reviewDoc.mjs";
|
|
19
|
-
import { scan } from "./scan.mjs";
|
|
20
21
|
import { searchDoc } from "./searchDoc.mjs";
|
|
21
22
|
import { translateDoc } from "./translateDoc/translateDoc.mjs";
|
|
22
23
|
|
|
23
|
-
export { INFRA_MODES, INSTALL_SCRIPT_URL, INSTALL_SCRIPT_URL_WINDOWS, PLATFORM_OPTIONS, build, bundle, dirname, extract, fill, findProjectRoot, getDetectedPlatform, init, initInfra, initSkills, listContentDeclaration, listContentDeclarationRows, listMissingTranslations, listMissingTranslationsWithConfig, listProjectsCommand, liveSync, parseInfraMode, promptPlatform, pull, push, pushConfig, reviewDoc, scan, searchDoc, setAPI, startEditor, testMissingTranslations, translateDoc };
|
|
24
|
+
export { BROWSER_EXTENSION_OPTIONS, CHROME_EXTENSION_URL, ESLINT_CONFIG_FILES, INFRA_MODES, INIT_STEP_GROUPS, INSTALL_SCRIPT_URL, INSTALL_SCRIPT_URL_WINDOWS, LOCALE_ROUTING_CHOICES, MOZILLA_EXTENSION_URL, OPT_IN_INIT_STEPS, PLATFORM_OPTIONS, build, bundle, dirname, extract, fill, findProjectRoot, getDetectedPlatform, getInitialInitSteps, getProjectDependencies, getRoutingInitOptions, init, initChromeExtension, initInfra, initSkills, isEslintInstalled, listContentDeclaration, listContentDeclarationRows, listMissingTranslations, listMissingTranslationsWithConfig, listProjectsCommand, liveSync, parseInfraMode, parseLocaleRoutingChoice, promptPlatform, pull, push, pushConfig, reviewDoc, scan, searchDoc, setAPI, startEditor, testMissingTranslations, translateDoc };
|
package/dist/esm/init.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { login } from "./auth/login.mjs";
|
|
2
2
|
import { loadPrompts } from "./loadPrompts.mjs";
|
|
3
|
+
import { initChromeExtension } from "./initChromeExtension.mjs";
|
|
3
4
|
import { initInfra } from "./initInfra.mjs";
|
|
4
5
|
import { initSkills } from "./initSkills.mjs";
|
|
5
6
|
import { initMCP } from "./initMCP.mjs";
|
|
6
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
7
7
|
import { join, resolve } from "node:path";
|
|
8
|
-
import { detectCompatI18nLibraries, initIntlayer, setupCmsCredentials } from "@intlayer/engine/cli";
|
|
8
|
+
import { detectCompatI18nLibraries, hasLintTooling, hasUrlRoutingFramework, initIntlayer, logInitSuccessMessage, setupCmsCredentials } from "@intlayer/engine/cli";
|
|
9
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
10
|
|
|
10
11
|
//#region src/init.ts
|
|
11
12
|
const findProjectRoot = (startDir) => {
|
|
@@ -18,10 +19,10 @@ const findProjectRoot = (startDir) => {
|
|
|
18
19
|
};
|
|
19
20
|
/**
|
|
20
21
|
* Steps shown but not pre-selected. The infrastructure installer touches the
|
|
21
|
-
* machine (downloads an app, pulls Docker images)
|
|
22
|
-
*
|
|
22
|
+
* machine (downloads an app, pulls Docker images) and browser extensions open external URLs
|
|
23
|
+
* rather than modifying the project, so they must be an explicit choice.
|
|
23
24
|
*/
|
|
24
|
-
const OPT_IN_INIT_STEPS = ["infra"];
|
|
25
|
+
const OPT_IN_INIT_STEPS = ["infra", "chromeExtension"];
|
|
25
26
|
/** Grouped checkbox entries of the interactive init flow, in display order. */
|
|
26
27
|
const INIT_STEP_GROUPS = {
|
|
27
28
|
Codebase: [
|
|
@@ -41,15 +42,23 @@ const INIT_STEP_GROUPS = {
|
|
|
41
42
|
hint: "scaffold the fill and test workflows that run on every pull request (covers every project in a monorepo)"
|
|
42
43
|
}
|
|
43
44
|
],
|
|
44
|
-
DevTools: [
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
DevTools: [
|
|
46
|
+
{
|
|
47
|
+
value: "vscodeExtension",
|
|
48
|
+
label: "VS Code extension",
|
|
49
|
+
hint: "recommend the Intlayer extension"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
value: "eslint",
|
|
53
|
+
label: "Lint rules (ESLint / oxlint)",
|
|
54
|
+
hint: "flag hardcoded text and dynamic calls the compiler cannot optimize"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
value: "chromeExtension",
|
|
58
|
+
label: "Chrome extension",
|
|
59
|
+
hint: "for audit, debug and analysis purpose"
|
|
60
|
+
}
|
|
61
|
+
],
|
|
53
62
|
"Coding assistant": [
|
|
54
63
|
{
|
|
55
64
|
value: "skills",
|
|
@@ -78,7 +87,7 @@ const INIT_STEP_GROUPS = {
|
|
|
78
87
|
}]
|
|
79
88
|
};
|
|
80
89
|
/** Locale routing strategies offered by the interactive init flow. */
|
|
81
|
-
const
|
|
90
|
+
const ROUTING_OPTIONS = [
|
|
82
91
|
{
|
|
83
92
|
value: "prefix-no-default",
|
|
84
93
|
label: "Prefix all except the default locale",
|
|
@@ -98,8 +107,68 @@ const ROUTING_MODE_OPTIONS = [
|
|
|
98
107
|
value: "search-params",
|
|
99
108
|
label: "Use a search parameter",
|
|
100
109
|
hint: "/about?locale=fr"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
value: "none",
|
|
113
|
+
label: "No locale routing",
|
|
114
|
+
hint: "no proxy, no locale route segment — handle the locale yourself"
|
|
101
115
|
}
|
|
102
116
|
];
|
|
117
|
+
/** Locale routing choices accepted by `--routing`. */
|
|
118
|
+
const LOCALE_ROUTING_CHOICES = ROUTING_OPTIONS.map((option) => option.value);
|
|
119
|
+
/** Validates a `--routing` value, throwing on an unknown choice. */
|
|
120
|
+
const parseLocaleRoutingChoice = (value) => {
|
|
121
|
+
const choice = LOCALE_ROUTING_CHOICES.find((routing) => routing === value);
|
|
122
|
+
if (!choice) throw new Error(`Invalid --routing value "${value}". Expected one of: ${LOCALE_ROUTING_CHOICES.join(", ")}.`);
|
|
123
|
+
return choice;
|
|
124
|
+
};
|
|
125
|
+
/** Maps a routing choice to the init options it sets. */
|
|
126
|
+
const getRoutingInitOptions = (choice) => choice === "none" ? {
|
|
127
|
+
routingMode: "no-prefix",
|
|
128
|
+
enableProxy: false
|
|
129
|
+
} : {
|
|
130
|
+
routingMode: choice,
|
|
131
|
+
enableProxy: true
|
|
132
|
+
};
|
|
133
|
+
/** Known ESLint and oxlint configuration file names. */
|
|
134
|
+
const ESLINT_CONFIG_FILES = [
|
|
135
|
+
"eslint.config.js",
|
|
136
|
+
"eslint.config.mjs",
|
|
137
|
+
"eslint.config.cjs",
|
|
138
|
+
"eslint.config.ts",
|
|
139
|
+
"eslint.config.mts",
|
|
140
|
+
"eslint.config.cts",
|
|
141
|
+
".eslintrc.js",
|
|
142
|
+
".eslintrc.cjs",
|
|
143
|
+
".eslintrc.yaml",
|
|
144
|
+
".eslintrc.yml",
|
|
145
|
+
".eslintrc.json",
|
|
146
|
+
".eslintrc",
|
|
147
|
+
".oxlintrc.json"
|
|
148
|
+
];
|
|
149
|
+
/**
|
|
150
|
+
* Checks whether ESLint or a compatible linter (oxlint) is installed or
|
|
151
|
+
* configured in the project.
|
|
152
|
+
*/
|
|
153
|
+
const isEslintInstalled = (dependencies, root) => {
|
|
154
|
+
if (hasLintTooling(dependencies)) return true;
|
|
155
|
+
if (dependencies.eslint || dependencies.oxlint) return true;
|
|
156
|
+
if (root) return ESLINT_CONFIG_FILES.some((file) => existsSync(join(root, file)));
|
|
157
|
+
return false;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Computes the initial setup steps selected by default in the interactive prompt.
|
|
161
|
+
* Steps like infrastructure and browser extension require opt-in, while
|
|
162
|
+
* eslint is preselected only if installed on the project.
|
|
163
|
+
*/
|
|
164
|
+
const getInitialInitSteps = (dependencies, root) => {
|
|
165
|
+
const isEslintPresent = isEslintInstalled(dependencies, root);
|
|
166
|
+
return Object.values(INIT_STEP_GROUPS).flat().map((option) => option.value).filter((step) => {
|
|
167
|
+
if (OPT_IN_INIT_STEPS.includes(step)) return false;
|
|
168
|
+
if (step === "eslint" && !isEslintPresent) return false;
|
|
169
|
+
return true;
|
|
170
|
+
});
|
|
171
|
+
};
|
|
103
172
|
/** Reads the merged dependencies of the project at `root`. */
|
|
104
173
|
const getProjectDependencies = (root) => {
|
|
105
174
|
try {
|
|
@@ -115,17 +184,6 @@ const getProjectDependencies = (root) => {
|
|
|
115
184
|
}
|
|
116
185
|
};
|
|
117
186
|
/**
|
|
118
|
-
* Returns true when the project uses a URL-based router for which a locale
|
|
119
|
-
* routing strategy is meaningful: Next.js, React Router, or TanStack Router.
|
|
120
|
-
* Apps without URL routing (e.g. React Native / Expo) are excluded, since
|
|
121
|
-
* `routing.mode` has no effect there.
|
|
122
|
-
*/
|
|
123
|
-
const hasUrlRouting = (root) => {
|
|
124
|
-
const deps = getProjectDependencies(root);
|
|
125
|
-
if (deps["react-native"] || deps.expo) return false;
|
|
126
|
-
return Boolean(deps.next || deps["react-router"] || deps["react-router-dom"] || deps["@tanstack/react-router"] || deps["@tanstack/react-start"]);
|
|
127
|
-
};
|
|
128
|
-
/**
|
|
129
187
|
* Runs `init` in interactive mode: prompts the user with a checkbox of setup
|
|
130
188
|
* steps, then forwards the selection to {@link initIntlayer} (packages, GitHub
|
|
131
189
|
* Actions, VS Code extension, LSP) and runs the dedicated skills/MCP installers
|
|
@@ -135,10 +193,11 @@ const hasUrlRouting = (root) => {
|
|
|
135
193
|
const runInteractiveInit = async (root, baseOptions) => {
|
|
136
194
|
const p = await loadPrompts();
|
|
137
195
|
p.intro("Initialize Intlayer");
|
|
196
|
+
const projectDependencies = getProjectDependencies(root);
|
|
138
197
|
const selected = await p.groupMultiselect({
|
|
139
198
|
message: "Select what you want to set up:",
|
|
140
199
|
options: INIT_STEP_GROUPS,
|
|
141
|
-
initialValues:
|
|
200
|
+
initialValues: getInitialInitSteps(projectDependencies, root),
|
|
142
201
|
required: false
|
|
143
202
|
});
|
|
144
203
|
if (p.isCancel(selected)) {
|
|
@@ -146,36 +205,39 @@ const runInteractiveInit = async (root, baseOptions) => {
|
|
|
146
205
|
return;
|
|
147
206
|
}
|
|
148
207
|
const steps = selected;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
208
|
+
const detectedCompatLibraries = detectCompatI18nLibraries(projectDependencies);
|
|
209
|
+
const hasCompatLib = detectedCompatLibraries.length > 0;
|
|
210
|
+
if (hasCompatLib) p.log.info(`Detected existing i18n ${detectedCompatLibraries.length > 1 ? "libraries" : "library"}: ${detectedCompatLibraries.join(", ")} — the matching Intlayer compat adapter and catalog sync will be set up.`);
|
|
211
|
+
let routingOptions = {};
|
|
212
|
+
if (steps.includes("projectSetup") && baseOptions?.routingMode === void 0 && !hasCompatLib && hasUrlRoutingFramework(projectDependencies)) {
|
|
213
|
+
const selectedRouting = await p.select({
|
|
214
|
+
message: "How should the locale appear in your URLs?",
|
|
215
|
+
options: ROUTING_OPTIONS,
|
|
154
216
|
initialValue: "prefix-no-default"
|
|
155
217
|
});
|
|
156
|
-
if (p.isCancel(
|
|
218
|
+
if (p.isCancel(selectedRouting)) {
|
|
157
219
|
p.cancel("Operation cancelled.");
|
|
158
220
|
return;
|
|
159
221
|
}
|
|
160
|
-
|
|
222
|
+
routingOptions = getRoutingInitOptions(selectedRouting);
|
|
161
223
|
}
|
|
162
|
-
const detectedCompatLibraries = detectCompatI18nLibraries(getProjectDependencies(root));
|
|
163
|
-
if (detectedCompatLibraries.length > 0) p.log.info(`Detected existing i18n ${detectedCompatLibraries.length > 1 ? "libraries" : "library"}: ${detectedCompatLibraries.join(", ")} — the matching Intlayer compat adapter and catalog sync will be set up.`);
|
|
164
224
|
const options = {
|
|
165
225
|
...baseOptions,
|
|
166
|
-
|
|
226
|
+
...routingOptions,
|
|
167
227
|
noInstallPackages: !steps.includes("packages"),
|
|
168
|
-
noGitignore: baseOptions?.noGitignore,
|
|
228
|
+
noGitignore: baseOptions?.noGitignore || !steps.includes("projectSetup"),
|
|
169
229
|
noGithubActions: baseOptions?.noGithubActions || !steps.includes("githubActions"),
|
|
170
230
|
noFrameworkSetup: baseOptions?.noFrameworkSetup || !steps.includes("projectSetup"),
|
|
171
231
|
noVscodeExtension: !steps.includes("vscodeExtension"),
|
|
172
232
|
noLsp: !steps.includes("lsp"),
|
|
173
|
-
noEslint: !steps.includes("eslint")
|
|
233
|
+
noEslint: !steps.includes("eslint"),
|
|
234
|
+
skipFinalMessage: true
|
|
174
235
|
};
|
|
175
|
-
await initIntlayer(root, options);
|
|
236
|
+
const { guideUrl } = await initIntlayer(root, options);
|
|
176
237
|
const skillsPlatform = steps.includes("skills") ? await initSkills(root) : void 0;
|
|
177
238
|
if (steps.includes("mcp")) await initMCP(root, skillsPlatform);
|
|
178
239
|
if (steps.includes("infra")) await initInfra();
|
|
240
|
+
if (steps.includes("chromeExtension")) await initChromeExtension();
|
|
179
241
|
if (steps.includes("cms")) {
|
|
180
242
|
p.log.info("Opening your browser to log in to the Intlayer CMS...");
|
|
181
243
|
await login({
|
|
@@ -184,6 +246,7 @@ const runInteractiveInit = async (root, baseOptions) => {
|
|
|
184
246
|
});
|
|
185
247
|
}
|
|
186
248
|
p.outro("Intlayer initialization complete");
|
|
249
|
+
if (guideUrl) logInitSuccessMessage(guideUrl);
|
|
187
250
|
};
|
|
188
251
|
const init = async (projectRoot, options, interactive) => {
|
|
189
252
|
const root = projectRoot ? findProjectRoot(resolve(projectRoot)) : findProjectRoot(process.cwd());
|
|
@@ -195,5 +258,5 @@ const init = async (projectRoot, options, interactive) => {
|
|
|
195
258
|
};
|
|
196
259
|
|
|
197
260
|
//#endregion
|
|
198
|
-
export { findProjectRoot, init };
|
|
261
|
+
export { ESLINT_CONFIG_FILES, INIT_STEP_GROUPS, LOCALE_ROUTING_CHOICES, OPT_IN_INIT_STEPS, findProjectRoot, getInitialInitSteps, getProjectDependencies, getRoutingInitOptions, init, isEslintInstalled, parseLocaleRoutingChoice };
|
|
199
262
|
//# sourceMappingURL=init.mjs.map
|
package/dist/esm/init.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.mjs","names":[],"sources":["../../src/init.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport {\n detectCompatI18nLibraries,\n type InitOptions,\n initIntlayer,\n type RoutingMode,\n setupCmsCredentials,\n} from '@intlayer/engine/cli';\nimport { login } from './auth/login';\nimport { initInfra } from './initInfra';\nimport { initMCP } from './initMCP';\nimport { initSkills } from './initSkills';\nimport { loadPrompts } from './loadPrompts';\n\nexport const findProjectRoot = (startDir: string) => {\n let currentDir = startDir;\n\n while (currentDir !== resolve(currentDir, '..')) {\n if (existsSync(join(currentDir, 'package.json'))) {\n return currentDir;\n }\n currentDir = resolve(currentDir, '..');\n }\n\n // If no package.json is found, return the start directory.\n // The initIntlayer function will handle the missing package.json error.\n return startDir;\n};\n\n/** Individually selectable setup steps exposed by the interactive init flow. */\ntype InitStep =\n | 'packages'\n | 'githubActions'\n | 'projectSetup'\n | 'vscodeExtension'\n | 'lsp'\n | 'eslint'\n | 'skills'\n | 'mcp'\n | 'infra'\n | 'cms';\n\n/** A checkbox entry of the interactive init flow. */\ntype InitStepOption = {\n value: InitStep;\n label: string;\n hint: string;\n};\n\n/**\n * Steps shown but not pre-selected. The infrastructure installer touches the\n * machine (downloads an app, pulls Docker images) rather than the project, so\n * it must be an explicit choice.\n */\nconst OPT_IN_INIT_STEPS: InitStep[] = ['infra'];\n\n/** Grouped checkbox entries of the interactive init flow, in display order. */\nconst INIT_STEP_GROUPS: Record<string, InitStepOption[]> = {\n Codebase: [\n {\n value: 'packages',\n label: 'Install & upgrade packages',\n hint: 'install missing Intlayer dependencies and upgrade outdated ones',\n },\n {\n value: 'projectSetup',\n label: 'Project setup',\n hint: 'intlayer config, tsconfig, bundler plugin, middleware/proxy and providers in layout/page',\n },\n {\n value: 'githubActions',\n label: 'CI/CD (GitHub Actions)',\n hint: 'scaffold the fill and test workflows that run on every pull request (covers every project in a monorepo)',\n },\n ],\n DevTools: [\n {\n value: 'vscodeExtension',\n label: 'VS Code extension',\n hint: 'recommend the Intlayer extension',\n },\n {\n value: 'eslint',\n label: 'Lint rules (ESLint / oxlint)',\n hint: 'flag hardcoded text and dynamic calls the compiler cannot optimize',\n },\n ],\n 'Coding assistant': [\n {\n value: 'skills',\n label: 'AI skills',\n hint: 'install the Intlayer documentation as agent skills',\n },\n {\n value: 'mcp',\n label: 'MCP server',\n hint: 'configure the Intlayer MCP server',\n },\n {\n value: 'lsp',\n label: 'Editor LSP',\n hint: 'go-to-definition from keys to .content files',\n },\n ],\n CMS: [\n {\n value: 'cms',\n label: 'CMS',\n hint: 'log in through your browser, then store the credentials in your .env',\n },\n {\n value: 'infra',\n label: 'Infrastructure (desktop app / self-hosting)',\n hint: 'install the desktop app, or self-host with Docker (all-in-one or Compose)',\n },\n ],\n};\n\n/** Locale routing strategies offered by the interactive init flow. */\nconst ROUTING_MODE_OPTIONS: Array<{\n value: RoutingMode;\n label: string;\n hint: string;\n}> = [\n {\n value: 'prefix-no-default',\n label: 'Prefix all except the default locale',\n hint: '/about, /fr/about (default)',\n },\n {\n value: 'prefix-all',\n label: 'Prefix all locales',\n hint: '/en/about, /fr/about',\n },\n {\n value: 'no-prefix',\n label: 'No locale in the URL',\n hint: '/about',\n },\n {\n value: 'search-params',\n label: 'Use a search parameter',\n hint: '/about?locale=fr',\n },\n];\n\n/** Reads the merged dependencies of the project at `root`. */\nconst getProjectDependencies = (root: string): Record<string, string> => {\n try {\n const packageJsonPath = join(root, 'package.json');\n if (!existsSync(packageJsonPath)) return {};\n const { dependencies = {}, devDependencies = {} } = JSON.parse(\n readFileSync(packageJsonPath, 'utf-8')\n );\n return { ...dependencies, ...devDependencies };\n } catch {\n return {};\n }\n};\n\n/**\n * Returns true when the project uses a URL-based router for which a locale\n * routing strategy is meaningful: Next.js, React Router, or TanStack Router.\n * Apps without URL routing (e.g. React Native / Expo) are excluded, since\n * `routing.mode` has no effect there.\n */\nconst hasUrlRouting = (root: string): boolean => {\n const deps = getProjectDependencies(root);\n\n // React Native / Expo apps have no URL routing — never ask for a strategy.\n if (deps['react-native'] || deps.expo) return false;\n\n return Boolean(\n deps.next ||\n deps['react-router'] ||\n deps['react-router-dom'] ||\n deps['@tanstack/react-router'] ||\n deps['@tanstack/react-start']\n );\n};\n\n/**\n * Runs `init` in interactive mode: prompts the user with a checkbox of setup\n * steps, then forwards the selection to {@link initIntlayer} (packages, GitHub\n * Actions, VS Code extension, LSP) and runs the dedicated skills/MCP installers\n * for the steps that own their own prompts. The `.gitignore` entry is not\n * offered as a checkbox — it is always added (unless `--no-gitignore` is set).\n */\nconst runInteractiveInit = async (\n root: string,\n baseOptions?: InitOptions\n): Promise<void> => {\n const p = await loadPrompts();\n\n p.intro('Initialize Intlayer');\n\n const selected = await p.groupMultiselect<InitStep>({\n message: 'Select what you want to set up:',\n options: INIT_STEP_GROUPS,\n initialValues: Object.values(INIT_STEP_GROUPS)\n .flat()\n .map((option) => option.value)\n .filter((step) => !OPT_IN_INIT_STEPS.includes(step)),\n required: false,\n });\n\n if (p.isCancel(selected)) {\n p.cancel('Operation cancelled.');\n return;\n }\n\n const steps = selected as InitStep[];\n\n // Locale routing strategy → written to `routing.mode` in the config file.\n // Only relevant for URL-based routers (Next.js, React Router, TanStack);\n // skipped for apps without URL routing such as React Native / Expo.\n let routingMode: RoutingMode | undefined;\n\n if (steps.includes('projectSetup') && hasUrlRouting(root)) {\n const selectedRoutingMode = await p.select<RoutingMode>({\n message: 'Which locale routing strategy do you want?',\n options: ROUTING_MODE_OPTIONS,\n initialValue: 'prefix-no-default',\n });\n\n if (p.isCancel(selectedRoutingMode)) {\n p.cancel('Operation cancelled.');\n return;\n }\n\n routingMode = selectedRoutingMode;\n }\n\n // Compat i18n library detection — never asked, always derived from the\n // installed packages. `detectMissingIntlayerPackages` reads the very same\n // dependency map to schedule the compat adapter, the sync plugin and the\n // catalog template, so a project without any of those packages is simply set\n // up on Intlayer alone.\n const detectedCompatLibraries = detectCompatI18nLibraries(\n getProjectDependencies(root)\n );\n const hasCompatLib = detectedCompatLibraries.length > 0;\n\n if (hasCompatLib) {\n p.log.info(\n `Detected existing i18n ${detectedCompatLibraries.length > 1 ? 'libraries' : 'library'}: ${detectedCompatLibraries.join(', ')} — the matching Intlayer compat adapter and catalog sync will be set up.`\n );\n }\n\n const options: InitOptions = {\n ...baseOptions,\n routingMode,\n noInstallPackages: !steps.includes('packages'),\n // The `.gitignore` entry is never offered as a checkbox: in interactive\n // mode we always add `.intlayer` to `.gitignore`, only honoring an explicit\n // `--no-gitignore` flag from the command line.\n noGitignore: baseOptions?.noGitignore,\n // Respect explicit `--no-*` flags from the command line even when the\n // corresponding step is selected in the checkbox.\n noGithubActions:\n baseOptions?.noGithubActions || !steps.includes('githubActions'),\n noFrameworkSetup:\n baseOptions?.noFrameworkSetup || !steps.includes('projectSetup'),\n noVscodeExtension: !steps.includes('vscodeExtension'),\n noLsp: !steps.includes('lsp'),\n noEslint: !steps.includes('eslint'),\n };\n\n await initIntlayer(root, options);\n\n // Skills ask for the platform after the skill selection; MCP reuses it.\n const skillsPlatform = steps.includes('skills')\n ? await initSkills(root)\n : undefined;\n\n if (steps.includes('mcp')) {\n await initMCP(root, skillsPlatform);\n }\n\n // Delegated to the hosted install script, which owns its own menu.\n if (steps.includes('infra')) {\n await initInfra();\n }\n\n // CMS / visual editor runs last: the browser login persists the access-key\n // credentials to `.env` and enables the editor in the config file. Kept last\n // so the browser flow does not interrupt setup.\n if (steps.includes('cms')) {\n p.log.info('Opening your browser to log in to the Intlayer CMS...');\n // `exitAfter: false` keeps the process alive so the flow can finish; the\n // credentials are persisted to `.env` and the editor enabled in the config.\n await login({\n exitAfter: false,\n onCredentials: (credentials) => setupCmsCredentials(root, credentials),\n });\n }\n\n p.outro('Intlayer initialization complete');\n};\n\nexport const init = async (\n projectRoot?: string,\n options?: InitOptions,\n interactive?: boolean\n) => {\n const root = projectRoot\n ? findProjectRoot(resolve(projectRoot))\n : findProjectRoot(process.cwd());\n\n if (interactive) {\n await runInteractiveInit(root, options);\n return;\n }\n\n await initIntlayer(root, options);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"init.mjs","names":[],"sources":["../../src/init.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport {\n detectCompatI18nLibraries,\n hasLintTooling,\n hasUrlRoutingFramework,\n type InitOptions,\n initIntlayer,\n logInitSuccessMessage,\n type RoutingMode,\n setupCmsCredentials,\n} from '@intlayer/engine/cli';\nimport { login } from './auth/login';\nimport { initChromeExtension } from './initChromeExtension';\nimport { initInfra } from './initInfra';\nimport { initMCP } from './initMCP';\nimport { initSkills } from './initSkills';\nimport { loadPrompts } from './loadPrompts';\n\nexport const findProjectRoot = (startDir: string) => {\n let currentDir = startDir;\n\n while (currentDir !== resolve(currentDir, '..')) {\n if (existsSync(join(currentDir, 'package.json'))) {\n return currentDir;\n }\n currentDir = resolve(currentDir, '..');\n }\n\n // If no package.json is found, return the start directory.\n // The initIntlayer function will handle the missing package.json error.\n return startDir;\n};\n\n/** Individually selectable setup steps exposed by the interactive init flow. */\nexport type InitStep =\n | 'packages'\n | 'githubActions'\n | 'projectSetup'\n | 'vscodeExtension'\n | 'lsp'\n | 'eslint'\n | 'skills'\n | 'mcp'\n | 'infra'\n | 'cms'\n | 'chromeExtension';\n\n/** A checkbox entry of the interactive init flow. */\nexport type InitStepOption = {\n value: InitStep;\n label: string;\n hint: string;\n};\n\n/**\n * Steps shown but not pre-selected. The infrastructure installer touches the\n * machine (downloads an app, pulls Docker images) and browser extensions open external URLs\n * rather than modifying the project, so they must be an explicit choice.\n */\nexport const OPT_IN_INIT_STEPS: InitStep[] = ['infra', 'chromeExtension'];\n\n/** Grouped checkbox entries of the interactive init flow, in display order. */\nexport const INIT_STEP_GROUPS: Record<string, InitStepOption[]> = {\n Codebase: [\n {\n value: 'packages',\n label: 'Install & upgrade packages',\n hint: 'install missing Intlayer dependencies and upgrade outdated ones',\n },\n {\n value: 'projectSetup',\n label: 'Project setup',\n hint: 'intlayer config, tsconfig, bundler plugin, middleware/proxy and providers in layout/page',\n },\n {\n value: 'githubActions',\n label: 'CI/CD (GitHub Actions)',\n hint: 'scaffold the fill and test workflows that run on every pull request (covers every project in a monorepo)',\n },\n ],\n DevTools: [\n {\n value: 'vscodeExtension',\n label: 'VS Code extension',\n hint: 'recommend the Intlayer extension',\n },\n {\n value: 'eslint',\n label: 'Lint rules (ESLint / oxlint)',\n hint: 'flag hardcoded text and dynamic calls the compiler cannot optimize',\n },\n {\n value: 'chromeExtension',\n label: 'Chrome extension',\n hint: 'for audit, debug and analysis purpose',\n },\n ],\n 'Coding assistant': [\n {\n value: 'skills',\n label: 'AI skills',\n hint: 'install the Intlayer documentation as agent skills',\n },\n {\n value: 'mcp',\n label: 'MCP server',\n hint: 'configure the Intlayer MCP server',\n },\n {\n value: 'lsp',\n label: 'Editor LSP',\n hint: 'go-to-definition from keys to .content files',\n },\n ],\n CMS: [\n {\n value: 'cms',\n label: 'CMS',\n hint: 'log in through your browser, then store the credentials in your .env',\n },\n {\n value: 'infra',\n label: 'Infrastructure (desktop app / self-hosting)',\n hint: 'install the desktop app, or self-host with Docker (all-in-one or Compose)',\n },\n ],\n};\n\n/**\n * Locale routing choice: a `routing.mode` served through the locale proxy, or\n * `none` to leave routing to the app (`no-prefix` with the proxy disabled).\n */\nexport type LocaleRoutingChoice = RoutingMode | 'none';\n\n/** Locale routing strategies offered by the interactive init flow. */\nconst ROUTING_OPTIONS: Array<{\n value: LocaleRoutingChoice;\n label: string;\n hint: string;\n}> = [\n {\n value: 'prefix-no-default',\n label: 'Prefix all except the default locale',\n hint: '/about, /fr/about (default)',\n },\n {\n value: 'prefix-all',\n label: 'Prefix all locales',\n hint: '/en/about, /fr/about',\n },\n {\n value: 'no-prefix',\n label: 'No locale in the URL',\n hint: '/about',\n },\n {\n value: 'search-params',\n label: 'Use a search parameter',\n hint: '/about?locale=fr',\n },\n {\n value: 'none',\n label: 'No locale routing',\n hint: 'no proxy, no locale route segment — handle the locale yourself',\n },\n];\n\n/** Locale routing choices accepted by `--routing`. */\nexport const LOCALE_ROUTING_CHOICES = ROUTING_OPTIONS.map(\n (option) => option.value\n);\n\n/** Validates a `--routing` value, throwing on an unknown choice. */\nexport const parseLocaleRoutingChoice = (\n value: string\n): LocaleRoutingChoice => {\n const choice = LOCALE_ROUTING_CHOICES.find((routing) => routing === value);\n\n if (!choice) {\n throw new Error(\n `Invalid --routing value \"${value}\". Expected one of: ${LOCALE_ROUTING_CHOICES.join(', ')}.`\n );\n }\n\n return choice;\n};\n\n/** Maps a routing choice to the init options it sets. */\nexport const getRoutingInitOptions = (\n choice: LocaleRoutingChoice\n): Pick<InitOptions, 'routingMode' | 'enableProxy'> =>\n choice === 'none'\n ? { routingMode: 'no-prefix', enableProxy: false }\n : { routingMode: choice, enableProxy: true };\n\n/** Known ESLint and oxlint configuration file names. */\nexport const ESLINT_CONFIG_FILES = [\n 'eslint.config.js',\n 'eslint.config.mjs',\n 'eslint.config.cjs',\n 'eslint.config.ts',\n 'eslint.config.mts',\n 'eslint.config.cts',\n '.eslintrc.js',\n '.eslintrc.cjs',\n '.eslintrc.yaml',\n '.eslintrc.yml',\n '.eslintrc.json',\n '.eslintrc',\n '.oxlintrc.json',\n];\n\n/**\n * Checks whether ESLint or a compatible linter (oxlint) is installed or\n * configured in the project.\n */\nexport const isEslintInstalled = (\n dependencies: Record<string, string>,\n root?: string\n): boolean => {\n if (hasLintTooling(dependencies)) return true;\n if (dependencies.eslint || dependencies.oxlint) return true;\n\n if (root) {\n return ESLINT_CONFIG_FILES.some((file) => existsSync(join(root, file)));\n }\n\n return false;\n};\n\n/**\n * Computes the initial setup steps selected by default in the interactive prompt.\n * Steps like infrastructure and browser extension require opt-in, while\n * eslint is preselected only if installed on the project.\n */\nexport const getInitialInitSteps = (\n dependencies: Record<string, string>,\n root?: string\n): InitStep[] => {\n const isEslintPresent = isEslintInstalled(dependencies, root);\n\n return Object.values(INIT_STEP_GROUPS)\n .flat()\n .map((option) => option.value)\n .filter((step) => {\n if (OPT_IN_INIT_STEPS.includes(step)) return false;\n if (step === 'eslint' && !isEslintPresent) return false;\n return true;\n });\n};\n\n/** Reads the merged dependencies of the project at `root`. */\nexport const getProjectDependencies = (\n root: string\n): Record<string, string> => {\n try {\n const packageJsonPath = join(root, 'package.json');\n if (!existsSync(packageJsonPath)) return {};\n const { dependencies = {}, devDependencies = {} } = JSON.parse(\n readFileSync(packageJsonPath, 'utf-8')\n );\n return { ...dependencies, ...devDependencies };\n } catch {\n return {};\n }\n};\n\n/**\n * Runs `init` in interactive mode: prompts the user with a checkbox of setup\n * steps, then forwards the selection to {@link initIntlayer} (packages, GitHub\n * Actions, VS Code extension, LSP) and runs the dedicated skills/MCP installers\n * for the steps that own their own prompts. The `.gitignore` entry is not\n * offered as a checkbox — it is always added (unless `--no-gitignore` is set).\n */\nconst runInteractiveInit = async (\n root: string,\n baseOptions?: InitOptions\n): Promise<void> => {\n const p = await loadPrompts();\n\n p.intro('Initialize Intlayer');\n\n const projectDependencies = getProjectDependencies(root);\n\n const selected = await p.groupMultiselect<InitStep>({\n message: 'Select what you want to set up:',\n options: INIT_STEP_GROUPS,\n initialValues: getInitialInitSteps(projectDependencies, root),\n required: false,\n });\n\n if (p.isCancel(selected)) {\n p.cancel('Operation cancelled.');\n return;\n }\n\n const steps = selected as InitStep[];\n\n // Compat i18n library detection — never asked, always derived from the\n // installed packages. `detectMissingIntlayerPackages` reads the very same\n // dependency map to schedule the compat adapter, the sync plugin and the\n // catalog template, so a project without any of those packages is simply set\n // up on Intlayer alone.\n const detectedCompatLibraries =\n detectCompatI18nLibraries(projectDependencies);\n const hasCompatLib = detectedCompatLibraries.length > 0;\n\n if (hasCompatLib) {\n p.log.info(\n `Detected existing i18n ${detectedCompatLibraries.length > 1 ? 'libraries' : 'library'}: ${detectedCompatLibraries.join(', ')} — the matching Intlayer compat adapter and catalog sync will be set up.`\n );\n }\n\n // Locale routing → `routing.mode` + `routing.enableProxy` in the config, and\n // the route segment the framework scaffolding creates. Only asked for web\n // frontends; projects without URL routing (backend, React Native / Expo) keep\n // only `routing.storage` in a freshly created config. Skipped for compat\n // i18n libraries, which keep their own routing.\n let routingOptions: Pick<InitOptions, 'routingMode' | 'enableProxy'> = {};\n\n if (\n steps.includes('projectSetup') &&\n baseOptions?.routingMode === undefined &&\n !hasCompatLib &&\n hasUrlRoutingFramework(projectDependencies)\n ) {\n const selectedRouting = await p.select<LocaleRoutingChoice>({\n message: 'How should the locale appear in your URLs?',\n options: ROUTING_OPTIONS,\n initialValue: 'prefix-no-default',\n });\n\n if (p.isCancel(selectedRouting)) {\n p.cancel('Operation cancelled.');\n return;\n }\n\n routingOptions = getRoutingInitOptions(selectedRouting);\n }\n\n const options: InitOptions = {\n ...baseOptions,\n ...routingOptions,\n noInstallPackages: !steps.includes('packages'),\n // The `.gitignore` entry is tied to project setup.\n noGitignore: baseOptions?.noGitignore || !steps.includes('projectSetup'),\n // Respect explicit `--no-*` flags from the command line even when the\n // corresponding step is selected in the checkbox.\n noGithubActions:\n baseOptions?.noGithubActions || !steps.includes('githubActions'),\n noFrameworkSetup:\n baseOptions?.noFrameworkSetup || !steps.includes('projectSetup'),\n noVscodeExtension: !steps.includes('vscodeExtension'),\n noLsp: !steps.includes('lsp'),\n noEslint: !steps.includes('eslint'),\n skipFinalMessage: true,\n };\n\n const { guideUrl } = await initIntlayer(root, options);\n\n // Skills ask for the platform after the skill selection; MCP reuses it.\n const skillsPlatform = steps.includes('skills')\n ? await initSkills(root)\n : undefined;\n\n if (steps.includes('mcp')) {\n await initMCP(root, skillsPlatform);\n }\n\n // Delegated to the hosted install script, which owns its own menu.\n if (steps.includes('infra')) {\n await initInfra();\n }\n\n if (steps.includes('chromeExtension')) {\n await initChromeExtension();\n }\n\n // CMS / visual editor runs last: the browser login persists the access-key\n // credentials to `.env` and enables the editor in the config file. Kept last\n // so the browser flow does not interrupt setup.\n if (steps.includes('cms')) {\n p.log.info('Opening your browser to log in to the Intlayer CMS...');\n // `exitAfter: false` keeps the process alive so the flow can finish; the\n // credentials are persisted to `.env` and the editor enabled in the config.\n await login({\n exitAfter: false,\n onCredentials: (credentials) => setupCmsCredentials(root, credentials),\n });\n }\n\n p.outro('Intlayer initialization complete');\n\n if (guideUrl) {\n logInitSuccessMessage(guideUrl);\n }\n};\n\nexport const init = async (\n projectRoot?: string,\n options?: InitOptions,\n interactive?: boolean\n) => {\n const root = projectRoot\n ? findProjectRoot(resolve(projectRoot))\n : findProjectRoot(process.cwd());\n\n if (interactive) {\n await runInteractiveInit(root, options);\n return;\n }\n\n await initIntlayer(root, options);\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { openBrowser } from "./utils/openBrowser.mjs";
|
|
2
|
+
import { loadPrompts } from "./loadPrompts.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/initChromeExtension.ts
|
|
5
|
+
const CHROME_EXTENSION_URL = "https://chromewebstore.google.com/detail/intlayer_i18n_scanner/pmlehcgmjmfimmjnembihbakhnheiabc";
|
|
6
|
+
const MOZILLA_EXTENSION_URL = "https://addons.mozilla.org/en-US/firefox/addon/intlayer-i18n-scanner/";
|
|
7
|
+
const BROWSER_EXTENSION_OPTIONS = [{
|
|
8
|
+
value: CHROME_EXTENSION_URL,
|
|
9
|
+
label: "Chrome",
|
|
10
|
+
hint: CHROME_EXTENSION_URL
|
|
11
|
+
}, {
|
|
12
|
+
value: MOZILLA_EXTENSION_URL,
|
|
13
|
+
label: "Mozilla Firefox",
|
|
14
|
+
hint: MOZILLA_EXTENSION_URL
|
|
15
|
+
}];
|
|
16
|
+
const initChromeExtension = async () => {
|
|
17
|
+
const p = await loadPrompts();
|
|
18
|
+
const selectedUrls = await p.multiselect({
|
|
19
|
+
message: "Select browser extension(s) to install:",
|
|
20
|
+
options: BROWSER_EXTENSION_OPTIONS,
|
|
21
|
+
required: false
|
|
22
|
+
});
|
|
23
|
+
if (p.isCancel(selectedUrls)) return;
|
|
24
|
+
if (Array.isArray(selectedUrls) && selectedUrls.length > 0) for (const url of selectedUrls) {
|
|
25
|
+
p.log.info(`Opening ${url} in your browser...`);
|
|
26
|
+
openBrowser(url);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { BROWSER_EXTENSION_OPTIONS, CHROME_EXTENSION_URL, MOZILLA_EXTENSION_URL, initChromeExtension };
|
|
32
|
+
//# sourceMappingURL=initChromeExtension.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initChromeExtension.mjs","names":[],"sources":["../../src/initChromeExtension.ts"],"sourcesContent":["import { loadPrompts } from './loadPrompts';\nimport { openBrowser } from './utils/openBrowser';\n\nexport const CHROME_EXTENSION_URL =\n 'https://chromewebstore.google.com/detail/intlayer_i18n_scanner/pmlehcgmjmfimmjnembihbakhnheiabc';\n\nexport const MOZILLA_EXTENSION_URL =\n 'https://addons.mozilla.org/en-US/firefox/addon/intlayer-i18n-scanner/';\n\nexport const BROWSER_EXTENSION_OPTIONS = [\n {\n value: CHROME_EXTENSION_URL,\n label: 'Chrome',\n hint: CHROME_EXTENSION_URL,\n },\n {\n value: MOZILLA_EXTENSION_URL,\n label: 'Mozilla Firefox',\n hint: MOZILLA_EXTENSION_URL,\n },\n];\n\nexport const initChromeExtension = async () => {\n const p = await loadPrompts();\n\n const selectedUrls = await p.multiselect({\n message: 'Select browser extension(s) to install:',\n options: BROWSER_EXTENSION_OPTIONS,\n required: false,\n });\n\n if (p.isCancel(selectedUrls)) {\n return;\n }\n\n if (Array.isArray(selectedUrls) && selectedUrls.length > 0) {\n for (const url of selectedUrls) {\n p.log.info(`Opening ${url} in your browser...`);\n openBrowser(url);\n }\n }\n};\n"],"mappings":";;;;AAGA,MAAa,uBACX;AAEF,MAAa,wBACX;AAEF,MAAa,4BAA4B,CACvC;CACE,OAAO;CACP,OAAO;CACP,MAAM;AACR,GACA;CACE,OAAO;CACP,OAAO;CACP,MAAM;AACR,CACF;AAEA,MAAa,sBAAsB,YAAY;CAC7C,MAAM,IAAI,MAAM,YAAY;CAE5B,MAAM,eAAe,MAAM,EAAE,YAAY;EACvC,SAAS;EACT,SAAS;EACT,UAAU;CACZ,CAAC;CAED,IAAI,EAAE,SAAS,YAAY,GACzB;CAGF,IAAI,MAAM,QAAQ,YAAY,KAAK,aAAa,SAAS,GACvD,KAAK,MAAM,OAAO,cAAc;EAC9B,EAAE,IAAI,KAAK,WAAW,IAAI,oBAAoB;EAC9C,YAAY,GAAG;CACjB;AAEJ"}
|
package/dist/esm/initInfra.mjs
CHANGED
|
@@ -74,7 +74,7 @@ const initInfra = async (options = {}) => {
|
|
|
74
74
|
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
|
|
75
75
|
script = await response.text();
|
|
76
76
|
} catch (error) {
|
|
77
|
-
spinner.stop("Could not download the installer"
|
|
77
|
+
spinner.stop("Could not download the installer");
|
|
78
78
|
p.log.error(`${error.message}\nRun it directly: ${process.platform === "win32" ? `irm ${scriptUrl} | iex` : `curl -fsSL ${scriptUrl} | sh`}`);
|
|
79
79
|
process.exitCode = 1;
|
|
80
80
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initInfra.mjs","names":[],"sources":["../../src/initInfra.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport { mkdtemp, rm, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { loadPrompts } from './loadPrompts';\n\n/** Setup modes understood by the hosted installer. */\nexport const INFRA_MODES = ['desktop', 'docker', 'compose'] as const;\n\nexport type InfraMode = (typeof INFRA_MODES)[number];\n\n/**\n * The hosted installers are the single source of truth for the infrastructure\n * setup flow (desktop app, all-in-one container, Compose stack). The CLI runs\n * them rather than re-implementing the same steps in JavaScript.\n */\nexport const INSTALL_SCRIPT_URL = 'https://intlayer.org/install.sh';\nexport const INSTALL_SCRIPT_URL_WINDOWS = 'https://intlayer.org/install.ps1';\n\nexport type InitInfraOptions = {\n /** Skip the interactive menu and run this mode directly. */\n mode?: InfraMode;\n /** Installer URL override (forks, staging). Defaults to the platform's script. */\n scriptUrl?: string;\n};\n\n/** How to fetch and execute the installer on the current platform. */\ntype InstallerRunner = {\n url: string;\n fileName: string;\n /** Builds the command and arguments that execute the downloaded script. */\n command: (scriptPath: string, mode?: InfraMode) => [string, string[]];\n};\n\nconst POSIX_RUNNER: InstallerRunner = {\n url: INSTALL_SCRIPT_URL,\n fileName: 'install.sh',\n command: (scriptPath, mode) => [\n 'sh',\n [scriptPath, ...(mode ? ['--mode', mode] : [])],\n ],\n};\n\nconst WINDOWS_RUNNER: InstallerRunner = {\n url: INSTALL_SCRIPT_URL_WINDOWS,\n fileName: 'install.ps1',\n command: (scriptPath, mode) => [\n 'powershell',\n [\n '-NoProfile',\n '-ExecutionPolicy',\n 'Bypass',\n '-File',\n scriptPath,\n ...(mode ? ['-Mode', mode] : []),\n ],\n ],\n};\n\nconst getInstallerRunner = (): InstallerRunner =>\n process.platform === 'win32' ? WINDOWS_RUNNER : POSIX_RUNNER;\n\n/**\n * Validates a user-provided mode against {@link INFRA_MODES}.\n *\n * @throws When the value is not a known mode.\n */\nexport const parseInfraMode = (value: string): InfraMode => {\n if ((INFRA_MODES as readonly string[]).includes(value)) {\n return value as InfraMode;\n }\n\n throw new Error(\n `Unknown infra mode \"${value}\". Expected one of: ${INFRA_MODES.join(', ')}`\n );\n};\n\n/**\n * Runs the installer with the terminal attached, so its own prompts and\n * progress bars reach the user unchanged.\n *\n * @returns The script's exit code.\n */\nconst runInstaller = (command: string, args: string[]): Promise<number> =>\n new Promise((resolve, reject) => {\n const child = spawn(command, args, { stdio: 'inherit' });\n\n child.on('error', reject);\n child.on('close', (code) => resolve(code ?? 1));\n });\n\n/**\n * Downloads the hosted installer and runs it: desktop app, all-in-one Docker\n * container or Docker Compose stack, chosen interactively unless `mode` is set.\n *\n * Exposed as `intlayer init infra` and as a step of `intlayer init --interactive`.\n */\nexport const initInfra = async (\n options: InitInfraOptions = {}\n): Promise<void> => {\n const p = await loadPrompts();\n const runner = getInstallerRunner();\n const scriptUrl = options.scriptUrl ?? runner.url;\n\n const spinner = p.spinner();\n spinner.start(`Fetching ${scriptUrl}`);\n\n let script: string;\n\n try {\n const response = await fetch(scriptUrl);\n\n if (!response.ok) {\n throw new Error(`${response.status} ${response.statusText}`);\n }\n\n script = await response.text();\n } catch (error) {\n spinner.stop('Could not download the installer'
|
|
1
|
+
{"version":3,"file":"initInfra.mjs","names":[],"sources":["../../src/initInfra.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport { mkdtemp, rm, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { loadPrompts } from './loadPrompts';\n\n/** Setup modes understood by the hosted installer. */\nexport const INFRA_MODES = ['desktop', 'docker', 'compose'] as const;\n\nexport type InfraMode = (typeof INFRA_MODES)[number];\n\n/**\n * The hosted installers are the single source of truth for the infrastructure\n * setup flow (desktop app, all-in-one container, Compose stack). The CLI runs\n * them rather than re-implementing the same steps in JavaScript.\n */\nexport const INSTALL_SCRIPT_URL = 'https://intlayer.org/install.sh';\nexport const INSTALL_SCRIPT_URL_WINDOWS = 'https://intlayer.org/install.ps1';\n\nexport type InitInfraOptions = {\n /** Skip the interactive menu and run this mode directly. */\n mode?: InfraMode;\n /** Installer URL override (forks, staging). Defaults to the platform's script. */\n scriptUrl?: string;\n};\n\n/** How to fetch and execute the installer on the current platform. */\ntype InstallerRunner = {\n url: string;\n fileName: string;\n /** Builds the command and arguments that execute the downloaded script. */\n command: (scriptPath: string, mode?: InfraMode) => [string, string[]];\n};\n\nconst POSIX_RUNNER: InstallerRunner = {\n url: INSTALL_SCRIPT_URL,\n fileName: 'install.sh',\n command: (scriptPath, mode) => [\n 'sh',\n [scriptPath, ...(mode ? ['--mode', mode] : [])],\n ],\n};\n\nconst WINDOWS_RUNNER: InstallerRunner = {\n url: INSTALL_SCRIPT_URL_WINDOWS,\n fileName: 'install.ps1',\n command: (scriptPath, mode) => [\n 'powershell',\n [\n '-NoProfile',\n '-ExecutionPolicy',\n 'Bypass',\n '-File',\n scriptPath,\n ...(mode ? ['-Mode', mode] : []),\n ],\n ],\n};\n\nconst getInstallerRunner = (): InstallerRunner =>\n process.platform === 'win32' ? WINDOWS_RUNNER : POSIX_RUNNER;\n\n/**\n * Validates a user-provided mode against {@link INFRA_MODES}.\n *\n * @throws When the value is not a known mode.\n */\nexport const parseInfraMode = (value: string): InfraMode => {\n if ((INFRA_MODES as readonly string[]).includes(value)) {\n return value as InfraMode;\n }\n\n throw new Error(\n `Unknown infra mode \"${value}\". Expected one of: ${INFRA_MODES.join(', ')}`\n );\n};\n\n/**\n * Runs the installer with the terminal attached, so its own prompts and\n * progress bars reach the user unchanged.\n *\n * @returns The script's exit code.\n */\nconst runInstaller = (command: string, args: string[]): Promise<number> =>\n new Promise((resolve, reject) => {\n const child = spawn(command, args, { stdio: 'inherit' });\n\n child.on('error', reject);\n child.on('close', (code) => resolve(code ?? 1));\n });\n\n/**\n * Downloads the hosted installer and runs it: desktop app, all-in-one Docker\n * container or Docker Compose stack, chosen interactively unless `mode` is set.\n *\n * Exposed as `intlayer init infra` and as a step of `intlayer init --interactive`.\n */\nexport const initInfra = async (\n options: InitInfraOptions = {}\n): Promise<void> => {\n const p = await loadPrompts();\n const runner = getInstallerRunner();\n const scriptUrl = options.scriptUrl ?? runner.url;\n\n const spinner = p.spinner();\n spinner.start(`Fetching ${scriptUrl}`);\n\n let script: string;\n\n try {\n const response = await fetch(scriptUrl);\n\n if (!response.ok) {\n throw new Error(`${response.status} ${response.statusText}`);\n }\n\n script = await response.text();\n } catch (error) {\n spinner.stop('Could not download the installer');\n p.log.error(\n `${(error as Error).message}\\nRun it directly: ${\n process.platform === 'win32'\n ? `irm ${scriptUrl} | iex`\n : `curl -fsSL ${scriptUrl} | sh`\n }`\n );\n process.exitCode = 1;\n return;\n }\n\n spinner.stop('Installer downloaded');\n\n const directory = await mkdtemp(join(tmpdir(), 'intlayer-install-'));\n const scriptPath = join(directory, runner.fileName);\n\n try {\n await writeFile(scriptPath, script, { mode: 0o700 });\n\n const [command, args] = runner.command(scriptPath, options.mode);\n const exitCode = await runInstaller(command, args);\n\n if (exitCode !== 0) {\n process.exitCode = exitCode;\n }\n } finally {\n await rm(directory, { recursive: true, force: true });\n }\n};\n"],"mappings":";;;;;;;;AAOA,MAAa,cAAc;CAAC;CAAW;CAAU;AAAS;;;;;;AAS1D,MAAa,qBAAqB;AAClC,MAAa,6BAA6B;AAiB1C,MAAM,eAAgC;CACpC,KAAK;CACL,UAAU;CACV,UAAU,YAAY,SAAS,CAC7B,MACA,CAAC,YAAY,GAAI,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,CAAE,CAChD;AACF;AAEA,MAAM,iBAAkC;CACtC,KAAK;CACL,UAAU;CACV,UAAU,YAAY,SAAS,CAC7B,cACA;EACE;EACA;EACA;EACA;EACA;EACA,GAAI,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;CAChC,CACF;AACF;AAEA,MAAM,2BACJ,QAAQ,aAAa,UAAU,iBAAiB;;;;;;AAOlD,MAAa,kBAAkB,UAA6B;CAC1D,IAAK,YAAkC,SAAS,KAAK,GACnD,OAAO;CAGT,MAAM,IAAI,MACR,uBAAuB,MAAM,sBAAsB,YAAY,KAAK,IAAI,GAC1E;AACF;;;;;;;AAQA,MAAM,gBAAgB,SAAiB,SACrC,IAAI,SAAS,SAAS,WAAW;CAC/B,MAAM,QAAQ,MAAM,SAAS,MAAM,EAAE,OAAO,UAAU,CAAC;CAEvD,MAAM,GAAG,SAAS,MAAM;CACxB,MAAM,GAAG,UAAU,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAChD,CAAC;;;;;;;AAQH,MAAa,YAAY,OACvB,UAA4B,CAAC,MACX;CAClB,MAAM,IAAI,MAAM,YAAY;CAC5B,MAAM,SAAS,mBAAmB;CAClC,MAAM,YAAY,QAAQ,aAAa,OAAO;CAE9C,MAAM,UAAU,EAAE,QAAQ;CAC1B,QAAQ,MAAM,YAAY,WAAW;CAErC,IAAI;CAEJ,IAAI;EACF,MAAM,WAAW,MAAM,MAAM,SAAS;EAEtC,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,GAAG,SAAS,OAAO,GAAG,SAAS,YAAY;EAG7D,SAAS,MAAM,SAAS,KAAK;CAC/B,SAAS,OAAO;EACd,QAAQ,KAAK,kCAAkC;EAC/C,EAAE,IAAI,MACJ,GAAI,MAAgB,QAAQ,qBAC1B,QAAQ,aAAa,UACjB,OAAO,UAAU,UACjB,cAAc,UAAU,QAEhC;EACA,QAAQ,WAAW;EACnB;CACF;CAEA,QAAQ,KAAK,sBAAsB;CAEnC,MAAM,YAAY,MAAM,QAAQ,KAAK,OAAO,GAAG,mBAAmB,CAAC;CACnE,MAAM,aAAa,KAAK,WAAW,OAAO,QAAQ;CAElD,IAAI;EACF,MAAM,UAAU,YAAY,QAAQ,EAAE,MAAM,IAAM,CAAC;EAEnD,MAAM,CAAC,SAAS,QAAQ,OAAO,QAAQ,YAAY,QAAQ,IAAI;EAC/D,MAAM,WAAW,MAAM,aAAa,SAAS,IAAI;EAEjD,IAAI,aAAa,GACf,QAAQ,WAAW;CAEvB,UAAU;EACR,MAAM,GAAG,WAAW;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACtD;AACF"}
|
package/dist/esm/initSkills.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { loadPrompts } from "./loadPrompts.mjs";
|
|
2
2
|
import { findProjectRoot } from "./init.mjs";
|
|
3
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
4
3
|
import { join, resolve } from "node:path";
|
|
5
4
|
import { PLATFORMS, PLATFORMS_METADATA, SKILLS, SKILLS_METADATA, getInitialSkills, installSkills } from "@intlayer/engine/cli";
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
6
|
import enquirer from "enquirer";
|
|
7
7
|
|
|
8
8
|
//#region src/initSkills.ts
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { relative } from "node:path";
|
|
2
|
+
import { prepareIntlayer } from "@intlayer/engine/cli";
|
|
2
3
|
import { colon, colorizeKey, colorizeNumber, getAppLogger } from "@intlayer/config/logger";
|
|
3
4
|
import { getConfiguration } from "@intlayer/config/node";
|
|
4
|
-
import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
|
|
5
|
-
import { prepareIntlayer } from "@intlayer/engine/cli";
|
|
6
5
|
import { formatPath } from "@intlayer/engine/utils";
|
|
6
|
+
import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
|
|
7
7
|
|
|
8
8
|
//#region src/listContentDeclaration.ts
|
|
9
9
|
const listContentDeclarationRows = async (options) => {
|
package/dist/esm/liveSync.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { IntlayerEventListener } from "./IntlayerEventListener.mjs";
|
|
2
|
+
import { createServer } from "node:http";
|
|
2
3
|
import * as ANSIColors from "@intlayer/config/colors";
|
|
3
4
|
import { colorize, getAppLogger } from "@intlayer/config/logger";
|
|
4
5
|
import { getConfiguration } from "@intlayer/config/node";
|
|
6
|
+
import { runParallel } from "@intlayer/engine/utils";
|
|
5
7
|
import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
|
|
6
8
|
import { buildDictionary } from "@intlayer/engine/build";
|
|
7
|
-
import { runParallel } from "@intlayer/engine/utils";
|
|
8
|
-
import { createServer } from "node:http";
|
|
9
|
-
import packageJson from "@intlayer/config/package.json" with { type: "json" };
|
|
10
9
|
import { getLocalizedContent } from "@intlayer/core/plugins";
|
|
11
10
|
import { getDictionaries } from "@intlayer/dictionaries-entry";
|
|
11
|
+
import packageJson from "@intlayer/config/package.json" with { type: "json" };
|
|
12
12
|
|
|
13
13
|
//#region src/liveSync.ts
|
|
14
14
|
const writeDictionary = async (dictionary, configuration) => {
|
package/dist/esm/pull.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { PullLogger } from "./push/pullLog.mjs";
|
|
2
1
|
import { checkCMSAuth, getAuthenticatedAPI } from "./utils/checkAccess.mjs";
|
|
2
|
+
import { PullLogger } from "./push/pullLog.mjs";
|
|
3
3
|
import { selectCmsEnvironment } from "./utils/selectCmsEnvironment.mjs";
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
5
4
|
import { join } from "node:path";
|
|
5
|
+
import { logConfigDetails } from "@intlayer/engine/cli";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
6
7
|
import * as ANSIColors from "@intlayer/config/colors";
|
|
7
8
|
import { getAppLogger } from "@intlayer/config/logger";
|
|
8
9
|
import { getConfiguration } from "@intlayer/config/node";
|
|
10
|
+
import { parallelize } from "@intlayer/engine/utils";
|
|
9
11
|
import { getProjectRequire } from "@intlayer/config/utils";
|
|
10
12
|
import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
|
|
11
13
|
import { writeContentDeclaration } from "@intlayer/engine/build";
|
|
12
|
-
import { logConfigDetails } from "@intlayer/engine/cli";
|
|
13
|
-
import { parallelize } from "@intlayer/engine/utils";
|
|
14
14
|
|
|
15
15
|
//#region src/pull.ts
|
|
16
16
|
/**
|