UNPKG

@intlayer/config

Version:

Retrieve Intlayer configurations and manage environment variables for both server-side and client-side environments.

589 lines (587 loc) 22.7 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const require_runtime = require('../_virtual/_rolldown/runtime.cjs'); const require_defaultValues_build = require('../defaultValues/build.cjs'); const require_defaultValues_compiler = require('../defaultValues/compiler.cjs'); const require_defaultValues_content = require('../defaultValues/content.cjs'); const require_defaultValues_dictionary = require('../defaultValues/dictionary.cjs'); const require_defaultValues_system = require('../defaultValues/system.cjs'); const require_utils_ESMxCJSHelpers = require('../utils/ESMxCJSHelpers.cjs'); const require_configFile_buildBrowserConfiguration = require('./buildBrowserConfiguration.cjs'); const require_configFile_configurationSchema = require('./configurationSchema.cjs'); let node_fs = require("node:fs"); let node_path = require("node:path"); //#region src/configFile/buildConfigurationFields.ts let storedConfiguration; /** * Build the `system` section of the Intlayer configuration. * * Resolves all directory paths (dictionaries, types, cache, …) relative to * the project base directory, using Node.js `require.resolve` where available * and falling back to `path.join` otherwise. * * @param baseDir - Project root directory. Defaults to `process.cwd()`. * @param customConfiguration - Partial user-supplied system config. * @returns A fully-resolved {@link SystemConfig}. */ const buildSystemFields = (baseDir, customConfiguration) => { const projectBaseDir = baseDir ?? process.cwd(); const optionalJoinBaseDir = (pathInput) => { let absolutePath; try { const requireFunction = require_utils_ESMxCJSHelpers.getProjectRequire(projectBaseDir); try { absolutePath = requireFunction.resolve(pathInput, { paths: [projectBaseDir] }); } catch (err) { if (!pathInput.startsWith(".") && !(0, node_path.isAbsolute)(pathInput)) absolutePath = requireFunction.resolve(`${pathInput}/package.json`, { paths: [projectBaseDir] }); else throw err; } } catch { absolutePath = (0, node_path.isAbsolute)(pathInput) ? pathInput : (0, node_path.join)(projectBaseDir, pathInput); } try { if ((0, node_fs.statSync)(absolutePath).isFile()) return (0, node_path.dirname)(absolutePath); } catch { if (/\.[a-z0-9]+$/i.test(absolutePath)) return (0, node_path.dirname)(absolutePath); } return absolutePath; }; const dictionariesDir = optionalJoinBaseDir(customConfiguration?.dictionariesDir ?? ".intlayer/dictionary"); return { baseDir: projectBaseDir, moduleAugmentationDir: optionalJoinBaseDir(customConfiguration?.moduleAugmentationDir ?? ".intlayer/types"), unmergedDictionariesDir: optionalJoinBaseDir(customConfiguration?.unmergedDictionariesDir ?? ".intlayer/unmerged_dictionary"), remoteDictionariesDir: optionalJoinBaseDir(customConfiguration?.remoteDictionariesDir ?? ".intlayer/remote_dictionary"), dictionariesDir, dynamicDictionariesDir: optionalJoinBaseDir(customConfiguration?.dynamicDictionariesDir ?? ".intlayer/dynamic_dictionary"), fetchDictionariesDir: optionalJoinBaseDir(customConfiguration?.fetchDictionariesDir ?? ".intlayer/fetch_dictionary"), typesDir: optionalJoinBaseDir(customConfiguration?.typesDir ?? ".intlayer/types"), mainDir: optionalJoinBaseDir(customConfiguration?.mainDir ?? ".intlayer/main"), configDir: optionalJoinBaseDir(customConfiguration?.configDir ?? ".intlayer/config"), cacheDir: optionalJoinBaseDir(customConfiguration?.cacheDir ?? ".intlayer/cache"), tempDir: optionalJoinBaseDir(customConfiguration?.tempDir ?? ".intlayer/tmp") }; }; /** * Build the `content` section of the Intlayer configuration. * * Resolves content and code directories relative to the project base using * `require.resolve`, falling back to `path.join`. * * @param systemConfig - Already-built system configuration (provides `baseDir`). * @param customConfiguration - Partial user-supplied content config. * @returns A fully-resolved {@link ContentConfig}. */ const buildContentFields = (systemConfig, customConfiguration) => { const fileExtensions = customConfiguration?.fileExtensions ?? require_defaultValues_content.FILE_EXTENSIONS; const optionalJoinBaseDir = (pathInput) => { let absolutePath; try { const requireFunction = require_utils_ESMxCJSHelpers.getProjectRequire(systemConfig.baseDir); try { absolutePath = requireFunction.resolve(pathInput, { paths: [systemConfig.baseDir] }); } catch (err) { if (!pathInput.startsWith(".") && !(0, node_path.isAbsolute)(pathInput)) absolutePath = requireFunction.resolve(`${pathInput}/package.json`, { paths: [systemConfig.baseDir] }); else throw err; } } catch { try { try { absolutePath = require.resolve(pathInput, { paths: [systemConfig.baseDir] }); } catch (err) { if (!pathInput.startsWith(".") && !(0, node_path.isAbsolute)(pathInput)) absolutePath = require.resolve(`${pathInput}/package.json`, { paths: [systemConfig.baseDir] }); else throw err; } } catch { absolutePath = (0, node_path.isAbsolute)(pathInput) ? pathInput : (0, node_path.join)(systemConfig.baseDir, pathInput); } } try { if ((0, node_fs.statSync)(absolutePath).isFile()) return (0, node_path.dirname)(absolutePath); } catch { if (/\.[a-z0-9]+$/i.test(absolutePath)) return (0, node_path.dirname)(absolutePath); } return absolutePath; }; return { fileExtensions, contentDir: (customConfiguration?.contentDir ?? require_defaultValues_content.CONTENT_DIR).map(optionalJoinBaseDir), codeDir: (customConfiguration?.codeDir ?? require_defaultValues_content.CODE_DIR).map(optionalJoinBaseDir), excludedPath: customConfiguration?.excludedPath ?? require_defaultValues_content.EXCLUDED_PATHS, watch: customConfiguration?.watch ?? true, formatCommand: customConfiguration?.formatCommand }; }; /** * Build the `ai` section of the Intlayer configuration. * * @param customConfiguration - Partial user-supplied AI config. * @returns A fully-defaulted {@link AiConfig}. */ const buildAiFields = (customConfiguration) => ({ /** * AI configuration */ provider: customConfiguration?.provider, /** * API key */ apiKey: customConfiguration?.apiKey, /** * API model */ model: customConfiguration?.model, /** * Temperature */ temperature: customConfiguration?.temperature, /** * Application context * * Default: undefined * * The application context. * * Example: `'My application context'` * * Note: Can be used to provide additional context about the application to the AI model. You can add more rules (e.g. "You should not transform urls"). */ applicationContext: customConfiguration?.applicationContext, /** * Base URL for the AI API * * Default: undefined * * The base URL for the AI API. * * Example: `'http://localhost:5000'` * * Note: Can be used to point to a local, or custom AI API endpoint. */ baseURL: customConfiguration?.baseURL, /** * Data serialization * * Options: * - "json": The industry standard. Highly reliable and structured, but consumes more tokens. * - "toon": An optimized format designed to reduce token consumption (cost-effective). However, it may slightly increase the risk of output inconsistency compared to standard JSON * * Default: `"json"` */ dataSerialization: customConfiguration?.dataSerialization }); /** * Build the `build` section of the Intlayer configuration. * * @param customConfiguration - Partial user-supplied build config. * @returns A fully-defaulted {@link BuildConfig}. */ const buildBuildFields = (customConfiguration) => ({ /** * Indicates the mode of the build * * Default: 'auto' * * If 'auto', the build will be enabled automatically when the application is built. * If 'manual', the build will be set only when the build command is executed. * * Can be used to disable dictionaries build, for instance when execution on Node.js environment should be avoided. */ mode: customConfiguration?.mode ?? "auto", /** * Indicates if the build should be optimized * * Default: process.env.NODE_ENV === 'production' * * If true, the build will be optimized. * If false, the build will not be optimized. * * Intlayer will replace all calls of dictionaries to optimize chunking. That way the final bundle will import only the dictionaries that are used. * All imports will stay as static import to avoid async processing when loading the dictionaries. * * Note: * - Intlayer will replace all call of `useIntlayer` with the defined mode by the `importMode` option. * - Intlayer will replace all call of `getIntlayer` with `getDictionary`. * - This option relies on the `@intlayer/babel` and `@intlayer/swc` plugins. * - In most cases, "dynamic" will be used for React applications, "async" for Vue.js applications. * - Ensure all keys are declared statically in the `useIntlayer` calls. e.g. `useIntlayer('navbar')`. */ optimize: customConfiguration?.optimize, /** * Indicates the mode of import to use for the dictionaries. * * Available modes: * - "static": The dictionaries are imported statically. * In that case, Intlayer will replace all calls to `useIntlayer` with `useDictionary`. * - "dynamic": The dictionaries are imported dynamically in a synchronous component using the suspense API. * In that case, Intlayer will replace all calls to `useIntlayer` with `useDictionaryDynamic`. * - "live": The dictionaries are imported dynamically using the live sync API. * In that case, Intlayer will replace all calls to `useIntlayer` with `useDictionaryDynamic`. * Live mode will use the live sync API to fetch the dictionaries. If the API call fails, the dictionaries will be imported dynamically as "dynamic" mode. * * Default: "static" * * By default, when a dictionary is loaded, it imports content for all locales as it's imported statically. * * Note: * - Dynamic imports rely on Suspense and may slightly impact rendering performance. * - If disabled all locales will be loaded at once, even if they are not used. * - This option relies on the `@intlayer/babel` and `@intlayer/swc` plugins. * - Ensure all keys are declared statically in the `useIntlayer` calls. e.g. `useIntlayer('navbar')`. * - This option will be ignored if `optimize` is disabled. * - This option will not impact the `getIntlayer`, `getDictionary`, `useDictionary`, `useDictionaryAsync` and `useDictionaryDynamic` functions. You can still use them to refine you code on manual optimization. * - The "live" allows to sync the dictionaries to the live sync server. * * @deprecated Use `dictionary.importMode` instead. */ importMode: customConfiguration?.importMode, /** * Minify the dictionaries to reduce the bundle size. * * Default: false * * Note: * - This option will be ignored if `optimize` is disabled. * - This option will be ignore if `editor.enabled` is true. * - If there is edge cases where the minification is not working properly, the dictionary will be not minified. */ minify: customConfiguration?.minify ?? false, /** * Purge the unused keys in a dictionaries * * Default: false * * Note: * - This option will be ignored if `optimize` is disabled. * - This option will be ignored if `editor.enabled` is true. */ purge: customConfiguration?.purge ?? false, /** * Pattern to traverse the code to optimize. * * Allows to avoid to traverse the code that is not relevant to the optimization. * Improve build performance. * * Default: ['**\/*.{js,ts,mjs,cjs,jsx,tsx}', '!**\/node_modules/**'] * * Example: `['src/**\/*.{ts,tsx}', '../ui-library/**\/*.{ts,tsx}']` * * Note: * - This option will be ignored if `optimize` is disabled. * - Use glob pattern. */ traversePattern: customConfiguration?.traversePattern ?? require_defaultValues_build.TRAVERSE_PATTERN, /** * Output format of the dictionaries * * Can be set on large projects to improve build performance. * * Default: ['cjs', 'esm'] * * The output format of the dictionaries. It can be either 'cjs' or 'esm'. * - 'cjs': The dictionaries are outputted as CommonJS modules. * - 'esm': The dictionaries are outputted as ES modules. */ outputFormat: customConfiguration?.outputFormat ?? require_defaultValues_build.OUTPUT_FORMAT, /** * Cache */ cache: customConfiguration?.cache ?? true, /** * Require function */ require: customConfiguration?.require, /** * Indicates if the build should check TypeScript types * * Default: `false` * * If true, the build will check TypeScript types and log errors. * Note: This can slow down the build. */ checkTypes: customConfiguration?.checkTypes ?? false }); /** * Build the `compiler` section of the Intlayer configuration. * * @param customConfiguration - Partial user-supplied compiler config. * @returns A fully-defaulted {@link CompilerConfig}. */ const buildCompilerFields = (customConfiguration) => ({ /** * Indicates if the compiler should be enabled */ enabled: customConfiguration?.enabled ?? true, /** * Prefix for the extracted dictionary keys */ dictionaryKeyPrefix: customConfiguration?.dictionaryKeyPrefix ?? "", /** * Pattern to traverse the code to optimize. * * @deprecated use `build.traversePattern` instead */ transformPattern: customConfiguration?.transformPattern, /** * Pattern to exclude from the optimization. * * @deprecated use `build.traversePattern` instead */ excludePattern: customConfiguration?.excludePattern, /** * Defines the output files path. Replaces `outputDir`. * * - `./` paths are resolved relative to the component directory. * - `/` paths are resolved relative to the project root (`baseDir`). * * - Including the `{{locale}}` variable in the path will trigger the generation of separate dictionaries per locale. * * @example: * ```ts * { * // Create Multilingual .content.ts files close to the component * output: ({ fileName, extension }) => `./${fileName}${extension}`, * * // output: './{{fileName}}{{extension}}', // Equivalent using template string * } * ``` * * ```ts * { * // Create centralize per-locale JSON at the root of the project * output: ({ key, locale }) => `/locales/${locale}/${key}.content.json`, * * // output: '/locales/{{locale}}/{{key}}.content.json', // Equivalent using template string * } * ``` * * ```ts * { * // Create per-locale JSON files with locale-specific output paths * output: { * en: ({ fileName, locale }) => `${fileName}.${locale}.content.json`, * fr: '{{fileName}}.{{locale}}.content.json', * es: false, // skip this locale * }, * } * ``` * * Variable list: * - `fileName`: The name of the file. * - `key`: The key of the content. * - `locale`: The locale of the content. * - `extension`: The extension of the file. * - `componentFileName`: The name of the component file. * - `componentExtension`: The extension of the component file. * - `format`: The format of the dictionary. * - `componentFormat`: The format of the component dictionary. * - `componentDirPath`: The directory path of the component. */ output: customConfiguration?.output, /** * Indicates if the metadata should be saved in the file. * * If true, the compiler will not save the metadata of the dictionaries. * * If `true`: * * ```json * { * "key": "value" * } * ``` * * If `false`: * * ```json * { * "key": "value", * "content": { * "key": "value" * } * } * ``` * * Default: `false` * * Note: Useful if used with loadJSON plugin */ noMetadata: customConfiguration?.noMetadata ?? false, /** * Indicates if the components should be saved after being transformed. */ saveComponents: customConfiguration?.saveComponents ?? false }); /** * Build the `dictionary` section of the Intlayer configuration. * * @param customConfiguration - Partial user-supplied dictionary config. * @returns A fully-defaulted {@link DictionaryConfig}. */ const buildDictionaryFields = (customConfiguration) => { const contentAutoTransformation = customConfiguration?.contentAutoTransformation ?? false; return { /** * Indicate how the dictionary should be filled using AI. * * Default: `true` * * - If `true`, will consider the `compiler.output` field. * - If `false`, will skip the fill process. * * - `./` paths are resolved relative to the component directory. * - `/` paths are resolved relative to the project root (`baseDir`). * * - If includes `{{locale}}` variable in the path, will trigger the generation of separate dictionaries per locale. * * Example: * ```ts * { * // Create Multilingual .content.ts files close to the component * fill: ({ fileName, extension }) => `./${fileName}${extension}`, * * // fill: './{{fileName}}{{extension}}', // Equivalent using template string * } * ``` * * ```ts * { * // Create centralize per-locale JSON at the root of the project * fill: ({ key, locale }) => `/locales/${locale}/${key}.content.json`, * * // fill: '/locales/{{locale}}/{{key}}.content.json', // Equivalent using template string * } * ``` * * ```ts * { * // Create custom output based on the locale * fill: { * en: ({ key }) => `/locales/en/${key}.content.json`, * fr: '/locales/fr/{{key}}.content.json', * es: false, * de: true, * }, * } * ``` * * * Variable list: * - `fileName`: The name of the file. * - `key`: The key of the content. * - `locale`: The locale of the content. * - `extension`: The extension of the file. * - `componentFileName`: The name of the component file. * - `componentExtension`: The extension of the component file. * - `format`: The format of the dictionary. * - `componentFormat`: The format of the component dictionary. * - `componentDirPath`: The directory path of the component. */ fill: customConfiguration?.fill ?? true, /** * Indicates if the content of the dictionary should be automatically transformed. * * Default: `false` */ contentAutoTransformation: typeof contentAutoTransformation === "object" ? { markdown: contentAutoTransformation.markdown ?? false, html: contentAutoTransformation.html ?? false, insertion: contentAutoTransformation.insertion ?? false } : contentAutoTransformation, /** * The location of the dictionary. * * Default: `"local"` */ location: customConfiguration?.location ?? "local", /** * Transform the dictionary in a per-locale dictionary. * Each field declared in a per-locale dictionary will be transformed in a translation node. * If missing, the dictionary will be treated as a multilingual dictionary. */ locale: customConfiguration?.locale, /** * The title of the dictionary. */ title: customConfiguration?.title, /** * The description of the dictionary. */ description: customConfiguration?.description, /** * Tags to categorize the dictionaries. */ tags: customConfiguration?.tags, /** * The priority of the dictionary. */ priority: customConfiguration?.priority, /** * Indicates the mode of import to use for the dictionary. * * Available modes: * - "static": The dictionaries are imported statically. * - "dynamic": The dictionaries are imported dynamically in a synchronous component using the suspense API. * - "live": The dictionaries are imported dynamically using the live sync API. * * Default: `"static"` */ importMode: customConfiguration?.importMode ?? "static" }; }; /** * Build the complete Intlayer configuration by merging user-supplied values * with defaults. * * Internally this function: * 1. Calls {@link buildBrowserConfiguration} to produce the browser-safe * subset (internationalization, routing, editor public fields, log, metadata). * 2. Extends the result with full server-side fields: * - `internationalization` — adds `requiredLocales` and `strictMode`. * - `editor` — adds `clientId` and `clientSecret`. * - `log` — adds custom log functions. * - `system`, `content`, `ai`, `build`, `compiler`, `dictionary`. * * @param customConfiguration - Optional user-supplied configuration object. * @param baseDir - Project root directory. Defaults to `process.cwd()`. * @param logFunctions - Optional custom logging functions. * @returns A fully-built {@link IntlayerConfig}. */ const buildConfigurationFields = (customConfiguration, baseDir, logFunctions) => { if (customConfiguration) { const result = require_configFile_configurationSchema.intlayerConfigSchema.safeParse(customConfiguration); if (!result.success) { const logError = logFunctions?.error ?? console.error; for (const issue of result.error.issues) logError(`${issue.path.join(".")}: ${issue.message}`); } } const browserConfig = require_configFile_buildBrowserConfiguration.buildBrowserConfiguration(customConfiguration); const internationalizationConfig = require_configFile_buildBrowserConfiguration.buildInternationalizationFields(customConfiguration?.internationalization); const editorConfig = require_configFile_buildBrowserConfiguration.buildEditorFields(customConfiguration?.editor); const logConfig = require_configFile_buildBrowserConfiguration.buildLogFields(customConfiguration?.log, logFunctions); const systemConfig = buildSystemFields(baseDir, customConfiguration?.system); const contentConfig = buildContentFields(systemConfig, customConfiguration?.content); storedConfiguration = { routing: browserConfig.routing, internationalization: internationalizationConfig, editor: editorConfig, log: logConfig, system: systemConfig, content: contentConfig, ai: buildAiFields(customConfiguration?.ai), build: buildBuildFields(customConfiguration?.build), compiler: buildCompilerFields(customConfiguration?.compiler), dictionary: buildDictionaryFields(customConfiguration?.dictionary), plugins: customConfiguration?.plugins, schemas: customConfiguration?.schemas }; return storedConfiguration; }; //#endregion exports.buildBrowserConfiguration = require_configFile_buildBrowserConfiguration.buildBrowserConfiguration; exports.buildConfigurationFields = buildConfigurationFields; exports.buildEditorFields = require_configFile_buildBrowserConfiguration.buildEditorFields; exports.buildInternationalizationFields = require_configFile_buildBrowserConfiguration.buildInternationalizationFields; exports.buildLogFields = require_configFile_buildBrowserConfiguration.buildLogFields; exports.buildRoutingFields = require_configFile_buildBrowserConfiguration.buildRoutingFields; exports.extractBrowserConfiguration = require_configFile_buildBrowserConfiguration.extractBrowserConfiguration; //# sourceMappingURL=buildConfigurationFields.cjs.map