@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
JavaScript
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