vite-intlayer 9.3.2 → 9.4.0-canary.0
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/README.md +1 -1
- package/dist/esm/index.mjs +3 -1
- package/dist/esm/intlayerChunkPlugin.mjs +181 -0
- package/dist/esm/intlayerChunkPlugin.mjs.map +1 -0
- package/dist/esm/intlayerMinifyPlugin.mjs +16 -15
- package/dist/esm/intlayerMinifyPlugin.mjs.map +1 -1
- package/dist/esm/intlayerNitroHandler.mjs +17 -7
- package/dist/esm/intlayerNitroHandler.mjs.map +1 -1
- package/dist/esm/intlayerOptimizePlugin.mjs +6 -5
- package/dist/esm/intlayerOptimizePlugin.mjs.map +1 -1
- package/dist/esm/intlayerPlugin.mjs +4 -0
- package/dist/esm/intlayerPlugin.mjs.map +1 -1
- package/dist/esm/intlayerPreloadPlugin.mjs +88 -0
- package/dist/esm/intlayerPreloadPlugin.mjs.map +1 -0
- package/dist/esm/intlayerProxyHandler.mjs +449 -0
- package/dist/esm/intlayerProxyHandler.mjs.map +1 -0
- package/dist/esm/intlayerProxyPlugin.mjs +10 -406
- package/dist/esm/intlayerProxyPlugin.mjs.map +1 -1
- package/dist/esm/intlayerPrunePlugin.mjs +5 -13
- package/dist/esm/intlayerPrunePlugin.mjs.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/intlayerChunkPlugin.d.ts +88 -0
- package/dist/types/intlayerChunkPlugin.d.ts.map +1 -0
- package/dist/types/intlayerMinifyPlugin.d.ts +5 -1
- package/dist/types/intlayerMinifyPlugin.d.ts.map +1 -1
- package/dist/types/intlayerNitroHandler.d.ts +1 -1
- package/dist/types/intlayerNitroHandler.d.ts.map +1 -1
- package/dist/types/intlayerPlugin.d.ts.map +1 -1
- package/dist/types/intlayerPreloadPlugin.d.ts +34 -0
- package/dist/types/intlayerPreloadPlugin.d.ts.map +1 -0
- package/dist/types/intlayerProxyHandler.d.ts +50 -0
- package/dist/types/intlayerProxyHandler.d.ts.map +1 -0
- package/dist/types/intlayerProxyPlugin.d.ts +4 -11
- package/dist/types/intlayerProxyPlugin.d.ts.map +1 -1
- package/dist/types/intlayerPrunePlugin.d.ts +5 -0
- package/dist/types/intlayerPrunePlugin.d.ts.map +1 -1
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intlayerPrunePlugin.mjs","names":[],"sources":["../../src/intlayerPrunePlugin.ts"],"sourcesContent":["import { join } from 'node:path';\nimport type { PruneContext } from '@intlayer/babel';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { formatPath, runOnce } from '@intlayer/engine/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { PluginOption } from 'vite';\n\n// Dictionary JSON types\n\n/**\n * A compiled intlayer translation node – used in static dictionaries where\n * all locales are bundled in a single file.\n *\n * Structure:\n * { nodeType: \"translation\", translation: { en: { field1, field2 }, fr: {…} } }\n */\ntype TranslationNode = {\n nodeType: 'translation';\n translation: Record<string, unknown>;\n};\n\n/**\n * Compiled intlayer dictionary as stored in a `.json` file.\n *\n * Two content shapes are supported (see `pruneStaticDictionaryContent` and\n * `pruneDynamicDictionaryContent`).\n */\ntype CompiledDictionaryJson = {\n key: string;\n content: TranslationNode | Record<string, unknown>;\n locale?: string; // present in per-locale dynamic dictionary files\n localIds?: string[];\n [extraKey: string]: unknown;\n};\n\n// Type guards\n\nconst isTranslationNode = (value: unknown): value is TranslationNode =>\n typeof value === 'object' &&\n value !== null &&\n (value as Record<string, unknown>).nodeType === 'translation' &&\n typeof (value as Record<string, unknown>).translation === 'object';\n\nconst isPlainRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n// Pruning logic\n\n/**\n * Result of a prune attempt.\n *\n * `wasRecognised` is `false` when the content structure did not match any\n * known shape – the caller should log a warning and skip the file entirely.\n */\ntype PruneResult = {\n prunedDictionary: CompiledDictionaryJson;\n wasRecognised: boolean;\n};\n\n/**\n * Prune a **static** dictionary JSON (all locales in one file).\n *\n * Shape A – the whole `content` is a single translation node:\n * { nodeType: \"translation\", translation: { en: { f1, f2 }, fr: { f1, f2 } } }\n * → prune the field objects inside each locale.\n *\n * Shape B – `content` is a plain record of fields, each being a translated node:\n * { field1: { nodeType: \"translation\", … }, field2: { … } }\n * → prune the top-level keys of `content` directly.\n *\n * Returns `{ wasRecognised: false }` when neither shape matches.\n */\nconst pruneStaticDictionaryContent = (\n dictionary: CompiledDictionaryJson,\n usedFieldNames: Set<string>\n): PruneResult => {\n const { content } = dictionary;\n\n // Shape A\n if (isTranslationNode(content)) {\n const firstLocaleValue = Object.values(content.translation)[0];\n const localeValuesAreRecords = isPlainRecord(firstLocaleValue);\n\n if (localeValuesAreRecords) {\n const prunedTranslationByLocale: Record<string, unknown> = {};\n\n for (const [localeName, localeContent] of Object.entries(\n content.translation\n )) {\n if (!isPlainRecord(localeContent)) {\n // Locale value is not a record (e.g. a primitive) – keep as-is\n prunedTranslationByLocale[localeName] = localeContent;\n continue;\n }\n\n const prunedLocaleFields: Record<string, unknown> = {};\n for (const [fieldName, fieldValue] of Object.entries(localeContent)) {\n if (usedFieldNames.has(fieldName)) {\n prunedLocaleFields[fieldName] = fieldValue;\n }\n }\n prunedTranslationByLocale[localeName] = prunedLocaleFields;\n }\n\n return {\n prunedDictionary: {\n ...dictionary,\n content: { ...content, translation: prunedTranslationByLocale },\n },\n wasRecognised: true,\n };\n }\n }\n\n // Shape B\n if (isPlainRecord(content) && !isTranslationNode(content)) {\n const prunedContentFields: Record<string, unknown> = {};\n\n for (const [fieldName, fieldValue] of Object.entries(content)) {\n if (usedFieldNames.has(fieldName)) {\n prunedContentFields[fieldName] = fieldValue;\n }\n }\n\n return {\n prunedDictionary: {\n ...dictionary,\n content: prunedContentFields as CompiledDictionaryJson['content'],\n },\n wasRecognised: true,\n };\n }\n\n return { prunedDictionary: dictionary, wasRecognised: false };\n};\n\n/**\n * Prune a **dynamic / per-locale** dictionary JSON (one file per locale).\n *\n * Structure:\n * { key, content: { field1: value, field2: value }, locale: \"en\" }\n *\n * The `content` here is already the flat, locale-specific record, so we\n * prune its top-level keys directly.\n */\nconst pruneDynamicDictionaryContent = (\n dictionary: CompiledDictionaryJson,\n usedFieldNames: Set<string>\n): PruneResult => {\n const { content } = dictionary;\n\n if (!isPlainRecord(content)) {\n return { prunedDictionary: dictionary, wasRecognised: false };\n }\n\n const prunedContentFields: Record<string, unknown> = {};\n for (const [fieldName, fieldValue] of Object.entries(content)) {\n if (usedFieldNames.has(fieldName)) {\n prunedContentFields[fieldName] = fieldValue;\n }\n }\n\n return {\n prunedDictionary: {\n ...dictionary,\n content: prunedContentFields as CompiledDictionaryJson['content'],\n },\n wasRecognised: true,\n };\n};\n\n/**\n * Returns the Vite plugin that removes unused content fields from compiled\n * dictionary JSON files during a production build.\n *\n * Targets:\n * - `<dictionariesDir>/**\\/*.json` – static all-locale dictionaries\n * - `<dynamicDictionariesDir>/**\\/*.json` – per-locale dynamic dictionaries\n * - `<fetchDictionariesDir>/**\\/*.json` – per-locale fetch dictionaries\n *\n * Decision table for each dictionary JSON:\n *\n * | condition | action |\n * |------------------------------------------------|-----------------|\n * | key in `dictionariesWithEdgeCases` | skip (warn once)|\n * | JSON parse error / missing key field | skip + warn |\n * | unrecognised content structure | skip + warn |\n * | analysis incomplete + key not in usage map | skip + warn |\n * | usage = 'all' (spread / untracked variable) | skip prune |\n * | usage = Set<string> | prune fields |\n *\n * Pruned dictionaries are returned as compact JSON (minification is handled\n * separately by `intlayerMinifyPlugin`).\n *\n * @param intlayerConfig - Resolved intlayer configuration.\n * @param pruneContext - Shared state produced by the usage analyser that\n * runs inside `intlayerOptimizePlugin`.\n */\nexport const intlayerPrune = (\n intlayerConfig: IntlayerConfig,\n pruneContext: PruneContext\n): PluginOption[] => {\n const logger = getAppLogger(intlayerConfig);\n\n const { optimize, purge } = intlayerConfig.build;\n const editorEnabled = intlayerConfig.editor.enabled;\n\n const {\n dictionariesDir,\n dynamicDictionariesDir,\n fetchDictionariesDir,\n baseDir,\n } = intlayerConfig.system;\n\n /**\n * Tracks dictionary keys whose \"pruned fields\" log has already been emitted\n * during this build session. Using an in-memory Set (instead of `runOnce`\n * file locks) avoids race conditions when client and SSR environments run\n * transforms concurrently — JavaScript's single-threaded event loop ensures\n * the `.has` / `.add` pair is always atomic.\n */\n const loggedPrunedDictionaryKeys = new Set<string>();\n\n /**\n * Accumulated statistics for the build summary.\n */\n const prunedFieldsCountPerDictionary = new Map<string, number>();\n\n const isDictionaryJsonFile = (absoluteFilePath: string): boolean =>\n absoluteFilePath.endsWith('.json') &&\n (absoluteFilePath.startsWith(dictionariesDir) ||\n absoluteFilePath.startsWith(dynamicDictionariesDir) ||\n absoluteFilePath.startsWith(fetchDictionariesDir));\n\n const isDynamicOrFetchDictionaryFile = (absoluteFilePath: string): boolean =>\n absoluteFilePath.startsWith(dynamicDictionariesDir) ||\n absoluteFilePath.startsWith(fetchDictionariesDir);\n\n const isPruneEnabled = (\n _config: unknown,\n env: { command: string }\n ): boolean => {\n const isBuildCommand = env.command === 'build';\n const isOptimizeActive =\n (optimize === undefined && isBuildCommand) || optimize === true;\n\n if (!isBuildCommand) return false;\n if (!isOptimizeActive) return false;\n if (!purge) return false;\n\n if (editorEnabled) {\n runOnce(\n join(\n baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-purge-editor-warning.lock'\n ),\n () =>\n logger([\n 'Dictionary purge is',\n colorize('disabled', ANSIColors.GREY_DARK),\n 'because',\n colorize('editor.enabled', ANSIColors.BLUE),\n 'is',\n colorize('true', ANSIColors.GREY_DARK),\n colorize(\n '— the editor requires full dictionary content',\n ANSIColors.GREY\n ),\n ]),\n { cacheTimeoutMs: 1000 * 10 }\n );\n return false;\n }\n\n return true;\n };\n\n const prunePlugin: PluginOption = {\n name: 'vite-intlayer-dictionary-prune',\n // 'pre' so we receive raw JSON before Vite's built-in JSON → ESM conversion\n enforce: 'pre',\n apply: isPruneEnabled,\n\n transform: (rawJsonCode, moduleId) => {\n const absoluteFilePath = moduleId.split('?', 1)[0];\n\n if (!absoluteFilePath || !isDictionaryJsonFile(absoluteFilePath))\n return null;\n\n // Parse JSON\n let parsedDictionary: CompiledDictionaryJson;\n try {\n parsedDictionary = JSON.parse(rawJsonCode) as CompiledDictionaryJson;\n } catch {\n // Malformed JSON – leave it for Vite to report the error\n return null;\n }\n\n const { key: dictionaryKey } = parsedDictionary;\n\n if (!dictionaryKey) {\n logger(\n [\n `Dictionary file`,\n formatPath(absoluteFilePath),\n `is missing a \"key\" field. Skipping prune for this file.`,\n ],\n { level: 'warn' }\n );\n return null;\n }\n\n // Skip keys already marked as edge cases─\n if (pruneContext.dictionariesWithEdgeCases.has(dictionaryKey)) {\n return null;\n }\n\n const fieldUsage =\n pruneContext.dictionaryKeyToFieldUsageMap.get(dictionaryKey);\n\n // No usage entry in the map─\n if (!fieldUsage) {\n if (pruneContext.hasUnparsableSourceFiles) {\n // At least one source file failed to parse; the unparsable file might\n // reference this key, so we cannot safely prune it.\n pruneContext.dictionariesWithEdgeCases.add(dictionaryKey);\n logger(\n [\n `Skipping prune for dictionary`,\n colorizeKey(dictionaryKey),\n `: analysis is incomplete due to earlier source-file parse failures.`,\n ],\n { level: 'warn' }\n );\n return null;\n }\n\n // Analysis was complete but this key was never referenced in any source\n // file – the dictionary is either unused or loaded dynamically by key.\n // Leave the content unchanged (the minify plugin will compact it).\n return null;\n }\n\n // Usage is 'all': at least one call-site consumes all fields─\n if (fieldUsage === 'all') {\n return null; // nothing to prune\n }\n\n // Prune\n const isDynamicOrFetch = isDynamicOrFetchDictionaryFile(absoluteFilePath);\n\n const { prunedDictionary, wasRecognised } = isDynamicOrFetch\n ? pruneDynamicDictionaryContent(parsedDictionary, fieldUsage)\n : pruneStaticDictionaryContent(parsedDictionary, fieldUsage);\n\n if (!wasRecognised) {\n pruneContext.dictionariesWithEdgeCases.add(dictionaryKey);\n logger(\n [\n `Unrecognised content structure in dictionary`,\n colorizeKey(dictionaryKey),\n `(file:`,\n `${formatPath(absoluteFilePath)}).`,\n `Skipping prune for this dictionary.`,\n ],\n { level: 'warn' }\n );\n return null;\n }\n\n // Log pruned fields\n const originalContent = parsedDictionary.content;\n let originalFieldNames: string[];\n\n if (isTranslationNode(originalContent)) {\n // Shape A – fields live inside each locale object\n const firstLocaleValue = Object.values(originalContent.translation)[0];\n originalFieldNames = isPlainRecord(firstLocaleValue)\n ? Object.keys(firstLocaleValue)\n : [];\n } else if (isPlainRecord(originalContent)) {\n // Shape B / dynamic – flat content record\n originalFieldNames = Object.keys(originalContent);\n } else {\n originalFieldNames = [];\n }\n\n const removedFieldNames = originalFieldNames.filter(\n (fieldName) => !fieldUsage.has(fieldName)\n );\n\n if (removedFieldNames.length > 0) {\n prunedFieldsCountPerDictionary.set(\n dictionaryKey,\n removedFieldNames.length\n );\n\n if (!loggedPrunedDictionaryKeys.has(dictionaryKey)) {\n loggedPrunedDictionaryKeys.add(dictionaryKey);\n logger(\n [\n `Pruned`,\n colorizeNumber(removedFieldNames.length),\n `unused field${removedFieldNames.length === 1 ? '' : 's'} from`,\n `${colorizeKey(dictionaryKey)}:`,\n removedFieldNames\n .map((fieldName) => colorize(fieldName, ANSIColors.GREY_LIGHT))\n .join(', '),\n ],\n { isVerbose: true }\n );\n }\n }\n\n return { code: JSON.stringify(prunedDictionary), map: null };\n },\n\n /**\n * Log a summary of all fields removed during this build.\n */\n buildEnd: () => {\n runOnce(\n join(baseDir, '.intlayer', 'cache', 'intlayer-prune-summary.lock'),\n () => {\n const totalPrunedFieldsCount = [\n ...prunedFieldsCountPerDictionary.values(),\n ].reduce((a, b) => a + b, 0);\n const totalPrunedDictionariesCount =\n prunedFieldsCountPerDictionary.size;\n\n if (totalPrunedFieldsCount > 0) {\n logger([\n `Pruned`,\n colorizeNumber(totalPrunedFieldsCount),\n `unused field${totalPrunedFieldsCount === 1 ? '' : 's'} across`,\n colorizeNumber(totalPrunedDictionariesCount),\n `dictionar${totalPrunedDictionariesCount === 1 ? 'y' : 'ies'}.`,\n ]);\n }\n },\n { cacheTimeoutMs: 1000 * 5 }\n );\n },\n };\n\n return [prunePlugin];\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"intlayerPrunePlugin.mjs","names":[],"sources":["../../src/intlayerPrunePlugin.ts"],"sourcesContent":["import { join } from 'node:path';\nimport type { PruneContext } from '@intlayer/babel';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { formatPath, runOnce } from '@intlayer/engine/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { PluginOption } from 'vite';\n\n// Dictionary JSON types\n\n/**\n * A compiled intlayer translation node – used in static dictionaries where\n * all locales are bundled in a single file.\n *\n * Structure:\n * { nodeType: \"translation\", translation: { en: { field1, field2 }, fr: {…} } }\n */\ntype TranslationNode = {\n nodeType: 'translation';\n translation: Record<string, unknown>;\n};\n\n/**\n * Compiled intlayer dictionary as stored in a `.json` file.\n *\n * Two content shapes are supported (see `pruneStaticDictionaryContent` and\n * `pruneDynamicDictionaryContent`).\n */\ntype CompiledDictionaryJson = {\n key: string;\n content: TranslationNode | Record<string, unknown>;\n locale?: string; // present in per-locale dynamic dictionary files\n localIds?: string[];\n [extraKey: string]: unknown;\n};\n\n// Type guards\n\nconst isTranslationNode = (value: unknown): value is TranslationNode =>\n typeof value === 'object' &&\n value !== null &&\n (value as Record<string, unknown>).nodeType === 'translation' &&\n typeof (value as Record<string, unknown>).translation === 'object';\n\nconst isPlainRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n// Pruning logic\n\n/**\n * Result of a prune attempt.\n *\n * `wasRecognised` is `false` when the content structure did not match any\n * known shape – the caller should log a warning and skip the file entirely.\n */\ntype PruneResult = {\n prunedDictionary: CompiledDictionaryJson;\n wasRecognised: boolean;\n};\n\n/**\n * Prune a **static** dictionary JSON (all locales in one file).\n *\n * Shape A – the whole `content` is a single translation node:\n * { nodeType: \"translation\", translation: { en: { f1, f2 }, fr: { f1, f2 } } }\n * → prune the field objects inside each locale.\n *\n * Shape B – `content` is a plain record of fields, each being a translated node:\n * { field1: { nodeType: \"translation\", … }, field2: { … } }\n * → prune the top-level keys of `content` directly.\n *\n * Returns `{ wasRecognised: false }` when neither shape matches.\n */\nconst pruneStaticDictionaryContent = (\n dictionary: CompiledDictionaryJson,\n usedFieldNames: Set<string>\n): PruneResult => {\n const { content } = dictionary;\n\n // Shape A\n if (isTranslationNode(content)) {\n const firstLocaleValue = Object.values(content.translation)[0];\n const localeValuesAreRecords = isPlainRecord(firstLocaleValue);\n\n if (localeValuesAreRecords) {\n const prunedTranslationByLocale: Record<string, unknown> = {};\n\n for (const [localeName, localeContent] of Object.entries(\n content.translation\n )) {\n if (!isPlainRecord(localeContent)) {\n // Locale value is not a record (e.g. a primitive) – keep as-is\n prunedTranslationByLocale[localeName] = localeContent;\n continue;\n }\n\n const prunedLocaleFields: Record<string, unknown> = {};\n for (const [fieldName, fieldValue] of Object.entries(localeContent)) {\n if (usedFieldNames.has(fieldName)) {\n prunedLocaleFields[fieldName] = fieldValue;\n }\n }\n prunedTranslationByLocale[localeName] = prunedLocaleFields;\n }\n\n return {\n prunedDictionary: {\n ...dictionary,\n content: { ...content, translation: prunedTranslationByLocale },\n },\n wasRecognised: true,\n };\n }\n }\n\n // Shape B\n if (isPlainRecord(content) && !isTranslationNode(content)) {\n const prunedContentFields: Record<string, unknown> = {};\n\n for (const [fieldName, fieldValue] of Object.entries(content)) {\n if (usedFieldNames.has(fieldName)) {\n prunedContentFields[fieldName] = fieldValue;\n }\n }\n\n return {\n prunedDictionary: {\n ...dictionary,\n content: prunedContentFields as CompiledDictionaryJson['content'],\n },\n wasRecognised: true,\n };\n }\n\n return { prunedDictionary: dictionary, wasRecognised: false };\n};\n\n/**\n * Prune a **dynamic / per-locale** dictionary JSON (one file per locale).\n *\n * Structure:\n * { key, content: { field1: value, field2: value }, locale: \"en\" }\n *\n * The `content` here is already the flat, locale-specific record, so we\n * prune its top-level keys directly.\n */\nconst pruneDynamicDictionaryContent = (\n dictionary: CompiledDictionaryJson,\n usedFieldNames: Set<string>\n): PruneResult => {\n const { content } = dictionary;\n\n if (!isPlainRecord(content)) {\n return { prunedDictionary: dictionary, wasRecognised: false };\n }\n\n const prunedContentFields: Record<string, unknown> = {};\n for (const [fieldName, fieldValue] of Object.entries(content)) {\n if (usedFieldNames.has(fieldName)) {\n prunedContentFields[fieldName] = fieldValue;\n }\n }\n\n return {\n prunedDictionary: {\n ...dictionary,\n content: prunedContentFields as CompiledDictionaryJson['content'],\n },\n wasRecognised: true,\n };\n};\n\n/**\n * Returns the Vite plugin that removes unused content fields from compiled\n * dictionary JSON files during a production build.\n *\n * Targets:\n * - `<dictionariesDir>/**\\/*.json` – static all-locale dictionaries\n * - `<dynamicDictionariesDir>/**\\/*.json` – per-locale dynamic dictionaries\n * - `<fetchDictionariesDir>/**\\/*.json` – per-locale fetch dictionaries\n *\n * Decision table for each dictionary JSON:\n *\n * | condition | action |\n * |------------------------------------------------|-----------------|\n * | key in `dictionariesWithEdgeCases` | skip (warn once)|\n * | JSON parse error / missing key field | skip + warn |\n * | unrecognised content structure | skip + warn |\n * | analysis incomplete + key not in usage map | skip + warn |\n * | usage = 'all' (spread / untracked variable) | skip prune |\n * | usage = Set<string> | prune fields |\n *\n * Pruned dictionaries are returned as compact JSON (minification is handled\n * separately by `intlayerMinifyPlugin`).\n *\n * Pruning stays compatible with the visual editor: the editor resolves every\n * edit through `dictionaryKey` + `keyPath` against the unmerged dictionaries\n * entry, which this plugin never touches, and a pruned field is one no source\n * file reads — so it is never rendered and never selectable in the page.\n *\n * @param intlayerConfig - Resolved intlayer configuration.\n * @param pruneContext - Shared state produced by the usage analyser that\n * runs inside `intlayerOptimizePlugin`.\n */\nexport const intlayerPrune = (\n intlayerConfig: IntlayerConfig,\n pruneContext: PruneContext\n): PluginOption[] => {\n const logger = getAppLogger(intlayerConfig);\n\n const { optimize, purge } = intlayerConfig.build;\n\n const {\n dictionariesDir,\n dynamicDictionariesDir,\n fetchDictionariesDir,\n baseDir,\n } = intlayerConfig.system;\n\n /**\n * Tracks dictionary keys whose \"pruned fields\" log has already been emitted\n * during this build session. Using an in-memory Set (instead of `runOnce`\n * file locks) avoids race conditions when client and SSR environments run\n * transforms concurrently — JavaScript's single-threaded event loop ensures\n * the `.has` / `.add` pair is always atomic.\n */\n const loggedPrunedDictionaryKeys = new Set<string>();\n\n /**\n * Accumulated statistics for the build summary.\n */\n const prunedFieldsCountPerDictionary = new Map<string, number>();\n\n const isDictionaryJsonFile = (absoluteFilePath: string): boolean =>\n absoluteFilePath.endsWith('.json') &&\n (absoluteFilePath.startsWith(dictionariesDir) ||\n absoluteFilePath.startsWith(dynamicDictionariesDir) ||\n absoluteFilePath.startsWith(fetchDictionariesDir));\n\n const isDynamicOrFetchDictionaryFile = (absoluteFilePath: string): boolean =>\n absoluteFilePath.startsWith(dynamicDictionariesDir) ||\n absoluteFilePath.startsWith(fetchDictionariesDir);\n\n const isPruneEnabled = (\n _config: unknown,\n env: { command: string }\n ): boolean => {\n const isBuildCommand = env.command === 'build';\n const isOptimizeActive =\n (optimize === undefined && isBuildCommand) || optimize === true;\n\n if (!isBuildCommand) return false;\n if (!isOptimizeActive) return false;\n if (!purge) return false;\n\n return true;\n };\n\n const prunePlugin: PluginOption = {\n name: 'vite-intlayer-dictionary-prune',\n // 'pre' so we receive raw JSON before Vite's built-in JSON → ESM conversion\n enforce: 'pre',\n apply: isPruneEnabled,\n\n transform: (rawJsonCode, moduleId) => {\n const absoluteFilePath = moduleId.split('?', 1)[0];\n\n if (!absoluteFilePath || !isDictionaryJsonFile(absoluteFilePath))\n return null;\n\n // Parse JSON\n let parsedDictionary: CompiledDictionaryJson;\n try {\n parsedDictionary = JSON.parse(rawJsonCode) as CompiledDictionaryJson;\n } catch {\n // Malformed JSON – leave it for Vite to report the error\n return null;\n }\n\n const { key: dictionaryKey } = parsedDictionary;\n\n if (!dictionaryKey) {\n logger(\n [\n `Dictionary file`,\n formatPath(absoluteFilePath),\n `is missing a \"key\" field. Skipping prune for this file.`,\n ],\n { level: 'warn' }\n );\n return null;\n }\n\n // Skip keys already marked as edge cases─\n if (pruneContext.dictionariesWithEdgeCases.has(dictionaryKey)) {\n return null;\n }\n\n const fieldUsage =\n pruneContext.dictionaryKeyToFieldUsageMap.get(dictionaryKey);\n\n // No usage entry in the map─\n if (!fieldUsage) {\n if (pruneContext.hasUnparsableSourceFiles) {\n // At least one source file failed to parse; the unparsable file might\n // reference this key, so we cannot safely prune it.\n pruneContext.dictionariesWithEdgeCases.add(dictionaryKey);\n logger(\n [\n `Skipping prune for dictionary`,\n colorizeKey(dictionaryKey),\n `: analysis is incomplete due to earlier source-file parse failures.`,\n ],\n { level: 'warn' }\n );\n return null;\n }\n\n // Analysis was complete but this key was never referenced in any source\n // file – the dictionary is either unused or loaded dynamically by key.\n // Leave the content unchanged (the minify plugin will compact it).\n return null;\n }\n\n // Usage is 'all': at least one call-site consumes all fields─\n if (fieldUsage === 'all') {\n return null; // nothing to prune\n }\n\n // Prune\n const isDynamicOrFetch = isDynamicOrFetchDictionaryFile(absoluteFilePath);\n\n const { prunedDictionary, wasRecognised } = isDynamicOrFetch\n ? pruneDynamicDictionaryContent(parsedDictionary, fieldUsage)\n : pruneStaticDictionaryContent(parsedDictionary, fieldUsage);\n\n if (!wasRecognised) {\n pruneContext.dictionariesWithEdgeCases.add(dictionaryKey);\n logger(\n [\n `Unrecognised content structure in dictionary`,\n colorizeKey(dictionaryKey),\n `(file:`,\n `${formatPath(absoluteFilePath)}).`,\n `Skipping prune for this dictionary.`,\n ],\n { level: 'warn' }\n );\n return null;\n }\n\n // Log pruned fields\n const originalContent = parsedDictionary.content;\n let originalFieldNames: string[];\n\n if (isTranslationNode(originalContent)) {\n // Shape A – fields live inside each locale object\n const firstLocaleValue = Object.values(originalContent.translation)[0];\n originalFieldNames = isPlainRecord(firstLocaleValue)\n ? Object.keys(firstLocaleValue)\n : [];\n } else if (isPlainRecord(originalContent)) {\n // Shape B / dynamic – flat content record\n originalFieldNames = Object.keys(originalContent);\n } else {\n originalFieldNames = [];\n }\n\n const removedFieldNames = originalFieldNames.filter(\n (fieldName) => !fieldUsage.has(fieldName)\n );\n\n if (removedFieldNames.length > 0) {\n prunedFieldsCountPerDictionary.set(\n dictionaryKey,\n removedFieldNames.length\n );\n\n if (!loggedPrunedDictionaryKeys.has(dictionaryKey)) {\n loggedPrunedDictionaryKeys.add(dictionaryKey);\n logger(\n [\n `Pruned`,\n colorizeNumber(removedFieldNames.length),\n `unused field${removedFieldNames.length === 1 ? '' : 's'} from`,\n `${colorizeKey(dictionaryKey)}:`,\n removedFieldNames\n .map((fieldName) => colorize(fieldName, ANSIColors.GREY_LIGHT))\n .join(', '),\n ],\n { isVerbose: true }\n );\n }\n }\n\n return { code: JSON.stringify(prunedDictionary), map: null };\n },\n\n /**\n * Log a summary of all fields removed during this build.\n */\n buildEnd: () => {\n runOnce(\n join(baseDir, '.intlayer', 'cache', 'intlayer-prune-summary.lock'),\n () => {\n const totalPrunedFieldsCount = [\n ...prunedFieldsCountPerDictionary.values(),\n ].reduce((a, b) => a + b, 0);\n const totalPrunedDictionariesCount =\n prunedFieldsCountPerDictionary.size;\n\n if (totalPrunedFieldsCount > 0) {\n logger([\n `Pruned`,\n colorizeNumber(totalPrunedFieldsCount),\n `unused field${totalPrunedFieldsCount === 1 ? '' : 's'} across`,\n colorizeNumber(totalPrunedDictionariesCount),\n `dictionar${totalPrunedDictionariesCount === 1 ? 'y' : 'ies'}.`,\n ]);\n }\n },\n { cacheTimeoutMs: 1000 * 5 }\n );\n },\n };\n\n return [prunePlugin];\n};\n"],"mappings":"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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { IntlayerCompilerOptions, intlayerCompiler } from "./IntlayerCompilerPlugin.js";
|
|
2
|
+
import { ChunkingContext, DictionaryModule, ModuleGraphNode, createDictionaryModuleParser, intlayerChunk, resolveBoundaries, toBoundaryName } from "./intlayerChunkPlugin.js";
|
|
2
3
|
import { intlayerMinify } from "./intlayerMinifyPlugin.js";
|
|
3
4
|
import { intlayerOptimize } from "./intlayerOptimizePlugin.js";
|
|
4
5
|
import { IntlayerProxyPluginOptions, createIntlayerProxyHandler, intLayerMiddlewarePlugin, intlayerMiddleware, intlayerProxy } from "./intlayerProxyPlugin.js";
|
|
5
6
|
import { IntlayerPluginOptions, intLayerPlugin, intlayer, intlayerPlugin } from "./intlayerPlugin.js";
|
|
7
|
+
import { intlayerPreload } from "./intlayerPreloadPlugin.js";
|
|
6
8
|
import { intlayerPrune } from "./intlayerPrunePlugin.js";
|
|
7
|
-
export { IntlayerCompilerOptions, IntlayerPluginOptions, IntlayerProxyPluginOptions, createIntlayerProxyHandler, intLayerMiddlewarePlugin, intLayerPlugin, intlayer, intlayerCompiler, intlayerMiddleware, intlayerMinify, intlayerOptimize, intlayerPlugin, intlayerProxy, intlayerPrune };
|
|
9
|
+
export { ChunkingContext, DictionaryModule, IntlayerCompilerOptions, IntlayerPluginOptions, IntlayerProxyPluginOptions, ModuleGraphNode, createDictionaryModuleParser, createIntlayerProxyHandler, intLayerMiddlewarePlugin, intLayerPlugin, intlayer, intlayerChunk, intlayerCompiler, intlayerMiddleware, intlayerMinify, intlayerOptimize, intlayerPlugin, intlayerPreload, intlayerProxy, intlayerPrune, resolveBoundaries, toBoundaryName };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { IntlayerConfig } from "@intlayer/types/config";
|
|
2
|
+
import { Plugin } from "vite";
|
|
3
|
+
//#region src/intlayerChunkPlugin.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Module-graph accessor Rolldown passes to a chunk-name function.
|
|
6
|
+
*
|
|
7
|
+
* Declared structurally rather than imported from `rolldown` so this plugin
|
|
8
|
+
* keeps working against the Rolldown bundled inside Vite, whose types are not
|
|
9
|
+
* re-exported.
|
|
10
|
+
*/
|
|
11
|
+
type ChunkingContext = {
|
|
12
|
+
getModuleInfo: (moduleId: string) => ModuleGraphNode | null;
|
|
13
|
+
};
|
|
14
|
+
type ModuleGraphNode = {
|
|
15
|
+
id: string;
|
|
16
|
+
isEntry: boolean;
|
|
17
|
+
/** Ids of the modules that statically `import` this one. */
|
|
18
|
+
importers: string[];
|
|
19
|
+
/** Ids of the modules that reach this one through `import()`. */
|
|
20
|
+
dynamicImporters: string[];
|
|
21
|
+
};
|
|
22
|
+
/** A per-locale dictionary chunk, identified from its module id. */
|
|
23
|
+
type DictionaryModule = {
|
|
24
|
+
key: string;
|
|
25
|
+
locale: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Builds the parser that recognises a per-locale dictionary chunk emitted in
|
|
29
|
+
* `dynamic` import mode.
|
|
30
|
+
*
|
|
31
|
+
* The layout is read relative to `dynamicDictionariesDir` rather than matched
|
|
32
|
+
* anywhere in the path, so an application file that happens to sit under its own
|
|
33
|
+
* `json/<something>/<something>.json` cannot be mistaken for a dictionary.
|
|
34
|
+
* Everything between the key and the locale is a qualifier dimension
|
|
35
|
+
* (collections, variants), which is why the middle segments are skipped instead
|
|
36
|
+
* of being required to be absent.
|
|
37
|
+
*
|
|
38
|
+
* @param dynamicDictionariesDir - Absolute, POSIX-normalized dictionaries root.
|
|
39
|
+
* @returns Parser returning the key and locale, or `null` for any other module.
|
|
40
|
+
*/
|
|
41
|
+
declare const createDictionaryModuleParser: (dynamicDictionariesDir: string) => ((moduleId: string) => DictionaryModule | null);
|
|
42
|
+
/** Turns a boundary module id into a readable chunk-name fragment. */
|
|
43
|
+
declare const toBoundaryName: (moduleId: string) => string;
|
|
44
|
+
/**
|
|
45
|
+
* Walks up the import graph from a dictionary JSON to the code-split boundaries
|
|
46
|
+
* that reach it.
|
|
47
|
+
*
|
|
48
|
+
* A module pulled in through `import()` — a `React.lazy` section, a split
|
|
49
|
+
* route — begins its own chunk, so it is the unit whose dictionaries should
|
|
50
|
+
* travel together: one request per Suspense boundary rather than one per
|
|
51
|
+
* dictionary. The walk deliberately starts from the JSON's importers rather
|
|
52
|
+
* than the JSON itself, because *every* dictionary is reached through
|
|
53
|
+
* `import()` and would otherwise be read as its own boundary.
|
|
54
|
+
*
|
|
55
|
+
* @returns Boundary ids reaching this dictionary, empty when the graph is
|
|
56
|
+
* unavailable (the module was pruned, or resolution failed).
|
|
57
|
+
*/
|
|
58
|
+
declare const resolveBoundaries: (jsonModuleId: string, context: ChunkingContext) => string[];
|
|
59
|
+
/**
|
|
60
|
+
* Groups dynamically imported dictionaries by the code-split boundary that
|
|
61
|
+
* consumes them, so each lazily loaded page or section fetches its content in
|
|
62
|
+
* a single request instead of one per dictionary.
|
|
63
|
+
*
|
|
64
|
+
* Without this, `importMode: 'dynamic'` emits a chunk per dictionary per
|
|
65
|
+
* locale. A page assembled from many components then issues dozens of ~3 KB
|
|
66
|
+
* requests, and because each is discovered only once its consumer has
|
|
67
|
+
* executed, they land in the last wave of the request waterfall — page text
|
|
68
|
+
* arrives after everything else.
|
|
69
|
+
*
|
|
70
|
+
* Switching to `importMode: 'static'` would collapse those requests but inline
|
|
71
|
+
* every locale into the bundle, which does not scale past a handful of
|
|
72
|
+
* languages. Grouping keeps the per-locale split and removes the fragmentation.
|
|
73
|
+
*
|
|
74
|
+
* Dictionaries reached from more than one boundary are placed in a shared
|
|
75
|
+
* chunk, so no page ships another page's content and the bytes are not
|
|
76
|
+
* duplicated across chunks.
|
|
77
|
+
*
|
|
78
|
+
* Because a boundary's dictionaries share one chunk, warming that chunk warms
|
|
79
|
+
* the whole boundary in a single request — which is why the top-level-await
|
|
80
|
+
* preload (`intlayerPreload`) costs one request per boundary rather than one
|
|
81
|
+
* per dictionary, without any page loading content belonging to another.
|
|
82
|
+
*
|
|
83
|
+
* @param intlayerConfig - Resolved Intlayer configuration.
|
|
84
|
+
*/
|
|
85
|
+
declare const intlayerChunk: (intlayerConfig: IntlayerConfig) => Plugin;
|
|
86
|
+
//#endregion
|
|
87
|
+
export { ChunkingContext, DictionaryModule, ModuleGraphNode, createDictionaryModuleParser, intlayerChunk, resolveBoundaries, toBoundaryName };
|
|
88
|
+
//# sourceMappingURL=intlayerChunkPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intlayerChunkPlugin.d.ts","names":[],"sources":["../../src/intlayerChunkPlugin.ts"],"mappings":";;;;;;;;;;KAaY;EACV,gBAAgB,qBAAqB;;KAG3B;EACV;EACA;;EAEA;;EAEA;;;KAIU;EACV;EACA;;;;;;;;;;;;;;;;cAiBW,+BAA4B,qCAEpC,qBAAqB;;cAuCb,iBAAc;;;;;;;;;;;;;;;cAuBd,oBAAiB,sBACR,SACX;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyDE,gBAAa,gBAAoB,mBAAiB"}
|
|
@@ -23,7 +23,11 @@ import { PruneContext } from "@intlayer/babel";
|
|
|
23
23
|
* and should be left completely untouched to avoid shipping broken data.
|
|
24
24
|
*
|
|
25
25
|
* Field renaming (property mangling) is applied only for dictionaries that
|
|
26
|
-
* have a known, finite field usage set in `pruneContext.dictionaryKeyToFieldRenameMap
|
|
26
|
+
* have a known, finite field usage set in `pruneContext.dictionaryKeyToFieldRenameMap`,
|
|
27
|
+
* and only while the visual editor is disabled: renaming rewrites the content
|
|
28
|
+
* keys the interpreter walks, so the `keyPath` reported to the editor would no
|
|
29
|
+
* longer resolve against the unmerged dictionaries. The whitespace compaction
|
|
30
|
+
* and the top-level metadata strip are keyPath-neutral and always run.
|
|
27
31
|
* The corresponding rename is also applied to source-file property accesses by
|
|
28
32
|
* the babel rename pass inside `intlayerOptimize`. Internal intlayer fields
|
|
29
33
|
* such as `nodeType` are never renamed.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intlayerMinifyPlugin.d.ts","names":[],"sources":["../../src/intlayerMinifyPlugin.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"intlayerMinifyPlugin.d.ts","names":[],"sources":["../../src/intlayerMinifyPlugin.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAwKa,iBAAc,gBACT,gBAAc,cAChB,wBACb"}
|
|
@@ -40,7 +40,7 @@ type H3EventLike = {
|
|
|
40
40
|
* and Deno — where `event.node` is `undefined` and `fromNodeMiddleware` crashes with
|
|
41
41
|
* "undefined is not an object (evaluating 'event.node.req')".
|
|
42
42
|
*
|
|
43
|
-
* It bridges h3 v2 events to the Node.js-style
|
|
43
|
+
* It bridges h3 v2 events to the Node.js-style proxy middleware
|
|
44
44
|
* via lightweight IncomingMessage / ServerResponse shims:
|
|
45
45
|
*
|
|
46
46
|
* - **Redirect** (301 / 5xx): builds a Web API `Response` and returns it — Nitro sends
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intlayerNitroHandler.d.ts","names":[],"sources":["../../src/intlayerNitroHandler.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"intlayerNitroHandler.d.ts","names":[],"sources":["../../src/intlayerNitroHandler.ts"],"mappings":";;;;;;;;;KAyBK;;;;;WAKM;;;;;;EAMT,KAAK;;;;;WAKI,SAAS;;;;;WAKT;aACE,SAAS;;;;;;;;;;;;;;;;;;;;;;iBAmCA,SAAA,OAAO,cAAc,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intlayerPlugin.d.ts","names":[],"sources":["../../src/intlayerPlugin.ts"],"mappings":";;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"intlayerPlugin.d.ts","names":[],"sources":["../../src/intlayerPlugin.ts"],"mappings":";;;;;;;;;;;;;;KA2CY,wBAAwB;;;;;;;;EAQlC,gBAAgB;;;;;;;;;;;;;;;EAgBhB,QAAQ,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;cA0BF,iBAAc,gBACT,0BACf;;;;;;;;;;;cAgOU,iBAAQ;;;;;;;;;;;;;cAaR,uBAAc"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { IntlayerConfig } from "@intlayer/types/config";
|
|
2
|
+
import { Plugin } from "vite";
|
|
3
|
+
//#region src/intlayerPreloadPlugin.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Makes dictionaries load with the chunk that needs them, instead of being
|
|
6
|
+
* requested once that chunk renders.
|
|
7
|
+
*
|
|
8
|
+
* In `importMode: 'dynamic'` a component reads its dictionary through a loader
|
|
9
|
+
* map and suspends while the matching chunk is fetched. The fetch is only
|
|
10
|
+
* discovered once the component executes, so it lands at the very end of the
|
|
11
|
+
* request waterfall and every navigation flashes the pending fallback before
|
|
12
|
+
* any text appears — the cost that used to push applications towards
|
|
13
|
+
* `importMode: 'static'`, which inlines every locale into the bundle.
|
|
14
|
+
*
|
|
15
|
+
* Starting that fetch from the generated entry point itself moves it forward to
|
|
16
|
+
* module-evaluation time: it leaves with the chunk that needs it — on a
|
|
17
|
+
* router's `import()` of a route, including a preload on hover — instead of
|
|
18
|
+
* waiting for the consuming component to execute. By the time the read happens
|
|
19
|
+
* the content is usually already there and the render is synchronous.
|
|
20
|
+
*
|
|
21
|
+
* The fetch is deliberately not awaited at the top level. Doing so would make
|
|
22
|
+
* the entry point an async module and hold up every chunk that imports it,
|
|
23
|
+
* which permanently strands `React.lazy` boundaries resolving during hydration
|
|
24
|
+
* on their fallback — see `addDynamicEntryPreload`.
|
|
25
|
+
*
|
|
26
|
+
* Complements the grouping in `intlayerChunk`: grouping decides how many
|
|
27
|
+
* requests a boundary makes, this decides when they start.
|
|
28
|
+
*
|
|
29
|
+
* @param intlayerConfig - Resolved Intlayer configuration.
|
|
30
|
+
*/
|
|
31
|
+
declare const intlayerPreload: (intlayerConfig: IntlayerConfig) => Plugin;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { intlayerPreload };
|
|
34
|
+
//# sourceMappingURL=intlayerPreloadPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intlayerPreloadPlugin.d.ts","names":[],"sources":["../../src/intlayerPreloadPlugin.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAmDa,kBAAe,gBAAoB,mBAAiB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { IntlayerConfig } from "@intlayer/types/config";
|
|
2
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
3
|
+
//#region src/intlayerProxyHandler.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A Node.js-compatible Connect middleware function.
|
|
6
|
+
* Compatible with Vite dev/preview server, Node.js http, Express, and h3's
|
|
7
|
+
* `fromNodeMiddleware` wrapper for Nitro/TanStack Start production use.
|
|
8
|
+
*/
|
|
9
|
+
type NodeMiddleware = (req: IncomingMessage, res: ServerResponse<IncomingMessage>, next: () => void) => void;
|
|
10
|
+
type IntlayerProxyHandlerOptions = {
|
|
11
|
+
/**
|
|
12
|
+
* The resolved Intlayer configuration driving locale detection and routing.
|
|
13
|
+
*/
|
|
14
|
+
configuration: IntlayerConfig;
|
|
15
|
+
/**
|
|
16
|
+
* A function that allows you to ignore specific requests from the intlayer proxy.
|
|
17
|
+
*
|
|
18
|
+
* @param req - The incoming request.
|
|
19
|
+
* @returns A boolean value indicating whether to ignore the request.
|
|
20
|
+
*/
|
|
21
|
+
ignore?: (req: IncomingMessage) => boolean | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Whether a development or preview server is serving the app.
|
|
24
|
+
*
|
|
25
|
+
* It only matters in the proxy's auto mode, where a dev server keeps locale
|
|
26
|
+
* routing URL-driven by ignoring the stored locale as a redirect source.
|
|
27
|
+
*
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
isDevServer?: boolean;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Creates the locale-routing middleware from an already-resolved configuration.
|
|
34
|
+
*
|
|
35
|
+
* This is the runtime core shared by every environment - dev server, preview
|
|
36
|
+
* server and the production Nitro server. It deliberately receives the
|
|
37
|
+
* configuration instead of loading it: loading it would pull
|
|
38
|
+
* `@intlayer/config/node` - and therefore `esbuild` - into the server bundle,
|
|
39
|
+
* which then fails to boot wherever `node_modules` is not shipped next to the
|
|
40
|
+
* build output.
|
|
41
|
+
*
|
|
42
|
+
* @param options - The resolved configuration plus optional handler behaviour.
|
|
43
|
+
* @returns A Connect-compatible `(req, res, next) => void` middleware.
|
|
44
|
+
*
|
|
45
|
+
* @see `createIntlayerProxyHandler` for the public, configuration-loading entry point.
|
|
46
|
+
*/
|
|
47
|
+
declare const createProxyHandler: (options: IntlayerProxyHandlerOptions) => NodeMiddleware;
|
|
48
|
+
//#endregion
|
|
49
|
+
export { IntlayerProxyHandlerOptions, NodeMiddleware, createProxyHandler };
|
|
50
|
+
//# sourceMappingURL=intlayerProxyHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intlayerProxyHandler.d.ts","names":[],"sources":["../../src/intlayerProxyHandler.ts"],"mappings":";;;;;;;;KAyEY,kBACV,KAAK,iBACL,KAAK,eAAe,kBACpB;KAGU;;;;EAIV,eAAe;;;;;;;EAOf,UAAU,KAAK;;;;;;;;;EASf;;;;;;;;;;;;;;;;;cAkBW,qBAAkB,SACpB,gCACR"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { NodeMiddleware } from "./intlayerProxyHandler.js";
|
|
1
2
|
import { GetConfigurationOptions } from "@intlayer/config/node";
|
|
2
3
|
import { Plugin } from "vite";
|
|
3
|
-
import { IncomingMessage
|
|
4
|
+
import { IncomingMessage } from "node:http";
|
|
4
5
|
//#region src/intlayerProxyPlugin.d.ts
|
|
5
6
|
type IntlayerProxyPluginOptions = {
|
|
6
7
|
/**
|
|
@@ -43,18 +44,11 @@ type IntlayerProxyPluginOptions = {
|
|
|
43
44
|
*/
|
|
44
45
|
isDevServer?: boolean;
|
|
45
46
|
};
|
|
46
|
-
/**
|
|
47
|
-
* A Node.js-compatible Connect middleware function.
|
|
48
|
-
* Compatible with Vite dev/preview server, Node.js http, Express, and h3's
|
|
49
|
-
* `fromNodeMiddleware` wrapper for Nitro/TanStack Start production use.
|
|
50
|
-
*/
|
|
51
|
-
type NodeMiddleware = (req: IncomingMessage, res: ServerResponse<IncomingMessage>, next: () => void) => void;
|
|
52
47
|
/**
|
|
53
48
|
* Creates a standalone, framework-agnostic locale-routing middleware.
|
|
54
49
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* used in every environment:
|
|
50
|
+
* It loads the Intlayer configuration and hands it to the runtime handler in
|
|
51
|
+
* `intlayerProxyHandler`, so it can be used in every environment:
|
|
58
52
|
*
|
|
59
53
|
* - **Dev**: wired up automatically by `intlayerProxy` via `configureServer`
|
|
60
54
|
* - **Preview**: wired up automatically by `intlayerProxy` via `configurePreviewServer`
|
|
@@ -71,7 +65,6 @@ type NodeMiddleware = (req: IncomingMessage, res: ServerResponse<IncomingMessage
|
|
|
71
65
|
*
|
|
72
66
|
* @param options - Plugin-specific options, such as path ignoring and Intlayer configuration overrides.
|
|
73
67
|
* @returns A Connect-compatible `(req, res, next) => void` middleware.
|
|
74
|
-
*
|
|
75
68
|
*/
|
|
76
69
|
declare const createIntlayerProxyHandler: (options?: IntlayerProxyPluginOptions) => NodeMiddleware;
|
|
77
70
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intlayerProxyPlugin.d.ts","names":[],"sources":["../../src/intlayerProxyPlugin.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"intlayerProxyPlugin.d.ts","names":[],"sources":["../../src/intlayerProxyPlugin.ts"],"mappings":";;;;;KA6BY;;;;;;;;;;;;;;EAcV,UAAU,KAAK;;;;;;;;;;;EAWf,gBAAgB;;;;;;;;;;;;;;EAchB;;;;;;;;;;;;;;;;;;;;;;;;cAyBW,6BAA0B,UAC3B,+BACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgDU,gBAAa,UAAc,+BAA6B;;;;;;;;;;;;;cAuIxD,2BAAkB;;;;;;;;;;;;;cAclB,iCAAwB"}
|
|
@@ -25,6 +25,11 @@ import { PruneContext } from "@intlayer/babel";
|
|
|
25
25
|
* Pruned dictionaries are returned as compact JSON (minification is handled
|
|
26
26
|
* separately by `intlayerMinifyPlugin`).
|
|
27
27
|
*
|
|
28
|
+
* Pruning stays compatible with the visual editor: the editor resolves every
|
|
29
|
+
* edit through `dictionaryKey` + `keyPath` against the unmerged dictionaries
|
|
30
|
+
* entry, which this plugin never touches, and a pruned field is one no source
|
|
31
|
+
* file reads — so it is never rendered and never selectable in the page.
|
|
32
|
+
*
|
|
28
33
|
* @param intlayerConfig - Resolved intlayer configuration.
|
|
29
34
|
* @param pruneContext - Shared state produced by the usage analyser that
|
|
30
35
|
* runs inside `intlayerOptimizePlugin`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intlayerPrunePlugin.d.ts","names":[],"sources":["../../src/intlayerPrunePlugin.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"intlayerPrunePlugin.d.ts","names":[],"sources":["../../src/intlayerPrunePlugin.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAiNa,gBAAa,gBACR,gBAAc,cAChB,iBACb"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-intlayer",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.4.0-canary.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A Vite plugin for seamless internationalization (i18n), providing locale detection, redirection, and environment-based configuration",
|
|
6
6
|
"keywords": [
|
|
@@ -81,12 +81,12 @@
|
|
|
81
81
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
|
-
"@intlayer/babel": "9.
|
|
85
|
-
"@intlayer/config": "9.
|
|
86
|
-
"@intlayer/core": "9.
|
|
87
|
-
"@intlayer/dictionaries-entry": "9.
|
|
88
|
-
"@intlayer/engine": "9.
|
|
89
|
-
"@intlayer/types": "9.
|
|
84
|
+
"@intlayer/babel": "9.4.0-canary.0",
|
|
85
|
+
"@intlayer/config": "9.4.0-canary.0",
|
|
86
|
+
"@intlayer/core": "9.4.0-canary.0",
|
|
87
|
+
"@intlayer/dictionaries-entry": "9.4.0-canary.0",
|
|
88
|
+
"@intlayer/engine": "9.4.0-canary.0",
|
|
89
|
+
"@intlayer/types": "9.4.0-canary.0"
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
92
|
"@types/node": "26.2.0",
|
|
@@ -96,12 +96,12 @@
|
|
|
96
96
|
"rimraf": "6.1.3",
|
|
97
97
|
"tsdown": "0.22.14",
|
|
98
98
|
"typescript": "7.0.2",
|
|
99
|
-
"vitest": "4.1.
|
|
99
|
+
"vitest": "4.1.11"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
102
|
"@babel/core": ">=6.0.0",
|
|
103
|
-
"@intlayer/svelte-compiler": "9.
|
|
104
|
-
"@intlayer/vue-compiler": "9.
|
|
103
|
+
"@intlayer/svelte-compiler": "9.4.0-canary.0",
|
|
104
|
+
"@intlayer/vue-compiler": "9.4.0-canary.0",
|
|
105
105
|
"h3": ">=1.0.0",
|
|
106
106
|
"vite": ">=4.0.0"
|
|
107
107
|
},
|