@intlayer/babel 8.6.9 → 8.7.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.
Files changed (42) hide show
  1. package/dist/cjs/babel-plugin-intlayer-extract.cjs +1 -1
  2. package/dist/cjs/babel-plugin-intlayer-field-rename.cjs +172 -0
  3. package/dist/cjs/babel-plugin-intlayer-field-rename.cjs.map +1 -0
  4. package/dist/cjs/babel-plugin-intlayer-optimize.cjs +1 -1
  5. package/dist/cjs/babel-plugin-intlayer-usage-analyzer.cjs +171 -0
  6. package/dist/cjs/babel-plugin-intlayer-usage-analyzer.cjs.map +1 -0
  7. package/dist/cjs/extractContent/contentWriter.cjs +1 -1
  8. package/dist/cjs/extractContent/extractContent.cjs +1 -1
  9. package/dist/cjs/extractContent/utils/extractDictionaryInfo.cjs +1 -1
  10. package/dist/cjs/extractContent/utils/resolveDictionaryKey.cjs +1 -1
  11. package/dist/cjs/extractScriptBlocks.cjs +80 -0
  12. package/dist/cjs/extractScriptBlocks.cjs.map +1 -0
  13. package/dist/cjs/getOptimizePluginOptions.cjs +1 -1
  14. package/dist/cjs/index.cjs +19 -0
  15. package/dist/cjs/transformers.cjs +150 -0
  16. package/dist/cjs/transformers.cjs.map +1 -0
  17. package/dist/esm/babel-plugin-intlayer-extract.mjs +1 -1
  18. package/dist/esm/babel-plugin-intlayer-field-rename.mjs +169 -0
  19. package/dist/esm/babel-plugin-intlayer-field-rename.mjs.map +1 -0
  20. package/dist/esm/babel-plugin-intlayer-optimize.mjs +1 -1
  21. package/dist/esm/babel-plugin-intlayer-usage-analyzer.mjs +167 -0
  22. package/dist/esm/babel-plugin-intlayer-usage-analyzer.mjs.map +1 -0
  23. package/dist/esm/extractContent/contentWriter.mjs +1 -1
  24. package/dist/esm/extractContent/extractContent.mjs +1 -1
  25. package/dist/esm/extractContent/utils/extractDictionaryInfo.mjs +1 -1
  26. package/dist/esm/extractContent/utils/resolveDictionaryKey.mjs +1 -1
  27. package/dist/esm/extractScriptBlocks.mjs +77 -0
  28. package/dist/esm/extractScriptBlocks.mjs.map +1 -0
  29. package/dist/esm/getOptimizePluginOptions.mjs +1 -1
  30. package/dist/esm/index.mjs +5 -1
  31. package/dist/esm/transformers.mjs +142 -0
  32. package/dist/esm/transformers.mjs.map +1 -0
  33. package/dist/types/babel-plugin-intlayer-field-rename.d.ts +57 -0
  34. package/dist/types/babel-plugin-intlayer-field-rename.d.ts.map +1 -0
  35. package/dist/types/babel-plugin-intlayer-usage-analyzer.d.ts +119 -0
  36. package/dist/types/babel-plugin-intlayer-usage-analyzer.d.ts.map +1 -0
  37. package/dist/types/extractScriptBlocks.d.ts +45 -0
  38. package/dist/types/extractScriptBlocks.d.ts.map +1 -0
  39. package/dist/types/index.d.ts +5 -1
  40. package/dist/types/transformers.d.ts +64 -0
  41. package/dist/types/transformers.d.ts.map +1 -0
  42. package/package.json +10 -10
@@ -0,0 +1 @@
1
+ {"version":3,"file":"babel-plugin-intlayer-usage-analyzer.mjs","names":[],"sources":["../../src/babel-plugin-intlayer-usage-analyzer.ts"],"sourcesContent":["import type { NodePath, PluginObj, PluginPass } from '@babel/core';\nimport type * as BabelTypes from '@babel/types';\n\n// ── PruneContext types ────────────────────────────────────────────────────────\n\n/**\n * Dictionary field usage result for a single dictionary key.\n *\n * 'all' → could not determine statically which fields are used;\n * keep every field (no pruning possible).\n * Set<string> → the exact top-level content field names that were accessed.\n */\nexport type DictionaryFieldUsage = Set<string> | 'all';\n\n/**\n * One node in the nested field-rename tree.\n *\n * shortName – the compact alias assigned to this field name.\n * children – rename table for the next level of user-defined keys inside\n * this field's value (empty when the value is a leaf / primitive).\n */\nexport type NestedRenameEntry = {\n shortName: string;\n children: NestedRenameMap;\n};\n\n/** A level of the field-rename tree, mapping original field names to entries. */\nexport type NestedRenameMap = Map<string, NestedRenameEntry>;\n\n/**\n * Shared mutable state created once by the vite plugin and passed by reference\n * to the usage-analyzer (writer) and the prune/minify plugins (readers).\n *\n * All mutations happen during the usage-analysis `buildStart` phase; readers\n * only access this state during the subsequent `transform` phase.\n */\nexport type PruneContext = {\n /**\n * Maps every dictionary key seen in source files to the set of top-level\n * content fields statically accessed, or `'all'` when the access pattern\n * could not be determined.\n */\n dictionaryKeyToFieldUsageMap: Map<string, DictionaryFieldUsage>;\n\n /**\n * Dictionary keys for which the prune/minify step must be skipped entirely\n * because an edge case was detected during analysis or structure recognition.\n */\n dictionariesWithEdgeCases: Set<string>;\n\n /**\n * True if at least one source file failed to parse during the analysis phase.\n * The prune plugin uses this flag conservatively: any dictionary key without\n * a usage entry might have been referenced by the unparsable file.\n */\n hasUnparsableSourceFiles: boolean;\n\n /**\n * Maps dictionary keys to the source file paths where the result of\n * `useIntlayer` / `getIntlayer` was assigned to a plain variable, making\n * static field analysis impossible.\n */\n dictionaryKeysWithUntrackedBindings: Map<string, string[]>;\n\n /**\n * Maps each dictionary key to a nested field-rename tree built after the\n * usage analysis phase (only populated when `build.minify` is active and\n * the field usage for that dictionary is a finite `Set<string>`).\n */\n dictionaryKeyToFieldRenameMap: Map<string, NestedRenameMap>;\n\n /**\n * Maps each dictionary key to a per-field list of source locations where\n * the field value is consumed \"opaquely\" (passed as-is to a child component\n * or function argument). When a field is opaque AND has nested user-defined\n * structure, its children must not be renamed.\n *\n * Structure: dictionaryKey → fieldName → [\"filePath:line\", …]\n */\n dictionaryKeysWithOpaqueTopLevelFields: Map<string, Map<string, string[]>>;\n\n /**\n * Dictionary keys for which field-key renaming must be skipped even if a\n * finite field-usage set was determined.\n *\n * Populated for dictionaries whose plain-variable bindings were resolved by\n * the framework-specific extractor (Vue / Svelte SFCs), because the Babel\n * rename plugin cannot update the source-code property accesses for those\n * indirect patterns (Vue `.value.field` / Svelte `$store.field`).\n *\n * Pruning and basic minification still apply; only field-key renaming is\n * suppressed.\n */\n dictionariesSkippingFieldRename: Set<string>;\n\n /**\n * Plain variable bindings that require a framework-specific secondary pass.\n *\n * Populated during the Babel analysis phase for `.vue` and `.svelte` source\n * files where direct field access is not visible to Babel scope analysis:\n * - Vue: `content.value.fieldName` – the `.value` ref-accessor is hidden\n * - Svelte: `$varName.fieldName` – the `$` prefix creates a new identifier\n *\n * Structure: filePath → [{variableName, dictionaryKey}, …]\n */\n pendingFrameworkAnalysis: Map<\n string,\n { variableName: string; dictionaryKey: string }[]\n >;\n};\n\nexport const createPruneContext = (): PruneContext => ({\n dictionaryKeyToFieldUsageMap: new Map(),\n dictionariesWithEdgeCases: new Set(),\n hasUnparsableSourceFiles: false,\n dictionaryKeysWithUntrackedBindings: new Map(),\n dictionaryKeyToFieldRenameMap: new Map(),\n dictionaryKeysWithOpaqueTopLevelFields: new Map<\n string,\n Map<string, string[]>\n >(),\n dictionariesSkippingFieldRename: new Set(),\n pendingFrameworkAnalysis: new Map(),\n});\n\n// ── Usage-analyzer Babel plugin ───────────────────────────────────────────────\n\n/** Canonical intlayer caller names that trigger usage analysis. */\nexport const INTLAYER_CALLER_NAMES = ['useIntlayer', 'getIntlayer'] as const;\nexport type IntlayerCallerName = (typeof INTLAYER_CALLER_NAMES)[number];\n\n/**\n * Records the usage of a specific dictionary key's fields into `pruneContext`.\n * Merges with any previously recorded usage for the same key.\n */\nconst recordFieldUsage = (\n pruneContext: PruneContext,\n dictionaryKey: string,\n fieldUsage: DictionaryFieldUsage\n): void => {\n const existingUsage =\n pruneContext.dictionaryKeyToFieldUsageMap.get(dictionaryKey);\n\n if (existingUsage === 'all') return; // already saturated\n\n if (fieldUsage === 'all') {\n pruneContext.dictionaryKeyToFieldUsageMap.set(dictionaryKey, 'all');\n return;\n }\n\n const mergedFieldSet =\n existingUsage instanceof Set\n ? new Set([...existingUsage, ...fieldUsage])\n : new Set(fieldUsage);\n\n pruneContext.dictionaryKeyToFieldUsageMap.set(dictionaryKey, mergedFieldSet);\n};\n\n/**\n * Analyses how the result of a single `useIntlayer('key')` / `getIntlayer('key')`\n * call expression is consumed, then records the field usage into `pruneContext`.\n *\n * Recognised patterns:\n * const { fieldA, fieldB } = useIntlayer('key') → records {fieldA, fieldB}\n * useIntlayer('key').fieldA → records {fieldA}\n * useIntlayer('key')['fieldA'] → records {fieldA}\n * const { ...rest } = useIntlayer('key') → records 'all' (spread)\n * const result = useIntlayer('key') → records 'all' (untracked binding)\n */\nconst analyzeCallExpressionUsage = (\n babelTypes: typeof BabelTypes,\n pruneContext: PruneContext,\n callExpressionPath: NodePath<BabelTypes.CallExpression>,\n dictionaryKey: string,\n currentSourceFilePath: string,\n isSfcFile: boolean\n): void => {\n const parentNode = callExpressionPath.parent;\n\n /** Mark the dictionary key as having an untracked binding in this file. */\n const markUntrackedBinding = (): void => {\n const existingPaths =\n pruneContext.dictionaryKeysWithUntrackedBindings.get(dictionaryKey) ?? [];\n if (!existingPaths.includes(currentSourceFilePath)) {\n pruneContext.dictionaryKeysWithUntrackedBindings.set(dictionaryKey, [\n ...existingPaths,\n currentSourceFilePath,\n ]);\n }\n recordFieldUsage(pruneContext, dictionaryKey, 'all');\n };\n\n /** Record that a field value is consumed opaquely (not further destructured). */\n const markOpaqueField = (\n fieldName: string,\n line: number | undefined\n ): void => {\n const fieldToLocations =\n pruneContext.dictionaryKeysWithOpaqueTopLevelFields.get(dictionaryKey) ??\n new Map<string, string[]>();\n const location =\n line !== undefined\n ? `${currentSourceFilePath}:${line}`\n : currentSourceFilePath;\n const locations = fieldToLocations.get(fieldName) ?? [];\n if (!locations.includes(location)) locations.push(location);\n fieldToLocations.set(fieldName, locations);\n pruneContext.dictionaryKeysWithOpaqueTopLevelFields.set(\n dictionaryKey,\n fieldToLocations\n );\n };\n\n /** Register a plain variable binding in an SFC file for a second-pass analysis. */\n const deferFrameworkAnalysis = (variableName: string): void => {\n const existing =\n pruneContext.pendingFrameworkAnalysis.get(currentSourceFilePath) ?? [];\n if (\n !existing.some(\n (e) =>\n e.variableName === variableName && e.dictionaryKey === dictionaryKey\n )\n ) {\n existing.push({ variableName, dictionaryKey });\n }\n pruneContext.pendingFrameworkAnalysis.set(currentSourceFilePath, existing);\n };\n\n // ── Pattern 1: const { fieldA, fieldB } = useIntlayer('key') ──────────────\n if (\n babelTypes.isVariableDeclarator(parentNode) &&\n babelTypes.isObjectPattern(parentNode.id)\n ) {\n const hasRestElement = parentNode.id.properties.some((prop) =>\n babelTypes.isRestElement(prop)\n );\n\n if (hasRestElement) {\n recordFieldUsage(pruneContext, dictionaryKey, 'all');\n return;\n }\n\n const accessedFieldNames = new Set<string>();\n for (const property of parentNode.id.properties) {\n if (\n babelTypes.isObjectProperty(property) &&\n babelTypes.isIdentifier(property.key)\n ) {\n accessedFieldNames.add(property.key.name);\n } else if (\n babelTypes.isObjectProperty(property) &&\n babelTypes.isStringLiteral(property.key)\n ) {\n accessedFieldNames.add(property.key.value);\n }\n }\n\n recordFieldUsage(pruneContext, dictionaryKey, accessedFieldNames);\n return;\n }\n\n // ── Pattern 2: useIntlayer('key').fieldA / useIntlayer('key')?.fieldA ──────\n if (\n (babelTypes.isMemberExpression(parentNode) ||\n babelTypes.isOptionalMemberExpression(parentNode)) &&\n (parentNode as BabelTypes.MemberExpression).object ===\n callExpressionPath.node\n ) {\n if (!parentNode.computed && babelTypes.isIdentifier(parentNode.property)) {\n recordFieldUsage(\n pruneContext,\n dictionaryKey,\n new Set([parentNode.property.name])\n );\n } else if (\n parentNode.computed &&\n babelTypes.isStringLiteral(parentNode.property)\n ) {\n recordFieldUsage(\n pruneContext,\n dictionaryKey,\n new Set([parentNode.property.value])\n );\n } else {\n markUntrackedBinding();\n }\n return;\n }\n\n // ── Pattern 3: const content = useIntlayer('key') ─────────────────────────\n if (\n babelTypes.isVariableDeclarator(parentNode) &&\n babelTypes.isIdentifier(parentNode.id)\n ) {\n const variableName = parentNode.id.name;\n const variableBinding = callExpressionPath.scope.getBinding(variableName);\n\n if (!variableBinding) {\n markUntrackedBinding();\n return;\n }\n\n const accessedTopLevelFieldNames = new Set<string>();\n let hasUntrackedReferenceAccess = false;\n\n for (const variableReferencePath of variableBinding.referencePaths) {\n const referenceParentNode = variableReferencePath.parent;\n\n if (\n (babelTypes.isMemberExpression(referenceParentNode) ||\n babelTypes.isOptionalMemberExpression(referenceParentNode)) &&\n (referenceParentNode as BabelTypes.MemberExpression).object ===\n variableReferencePath.node\n ) {\n const memberExpressionNode =\n referenceParentNode as BabelTypes.MemberExpression;\n\n if (\n !memberExpressionNode.computed &&\n babelTypes.isIdentifier(memberExpressionNode.property)\n ) {\n const fieldName = memberExpressionNode.property.name;\n accessedTopLevelFieldNames.add(fieldName);\n\n // Check if the field value is consumed opaquely (not chained further)\n const memberExprPath = variableReferencePath.parentPath;\n const grandParentNode = memberExprPath?.parent;\n const isChainedFurther =\n (babelTypes.isMemberExpression(grandParentNode) ||\n babelTypes.isOptionalMemberExpression(grandParentNode)) &&\n (grandParentNode as BabelTypes.MemberExpression).object ===\n memberExprPath?.node;\n\n if (!isChainedFurther) {\n markOpaqueField(fieldName, memberExprPath?.node.loc?.start.line);\n }\n } else if (\n memberExpressionNode.computed &&\n babelTypes.isStringLiteral(memberExpressionNode.property)\n ) {\n const fieldName = memberExpressionNode.property.value;\n accessedTopLevelFieldNames.add(fieldName);\n\n const memberExprPath = variableReferencePath.parentPath;\n const grandParentNode = memberExprPath?.parent;\n const isChainedFurther =\n (babelTypes.isMemberExpression(grandParentNode) ||\n babelTypes.isOptionalMemberExpression(grandParentNode)) &&\n (grandParentNode as BabelTypes.MemberExpression).object ===\n memberExprPath?.node;\n\n if (!isChainedFurther) {\n markOpaqueField(fieldName, memberExprPath?.node.loc?.start.line);\n }\n } else {\n // Dynamic computed access – cannot resolve statically\n hasUntrackedReferenceAccess = true;\n break;\n }\n } else if (babelTypes.isArrayExpression(referenceParentNode)) {\n // Ignore array literals (e.g. [content]) – uncommon but benign\n } else {\n // Variable used in a non-member-access context (spread, function arg, etc.)\n hasUntrackedReferenceAccess = true;\n break;\n }\n }\n\n if (hasUntrackedReferenceAccess) {\n markUntrackedBinding();\n } else if (isSfcFile) {\n // Vue / Svelte SFC: defer to the framework-specific extractor because\n // Babel scope analysis cannot see through `.value` or `$` indirection.\n deferFrameworkAnalysis(variableName);\n } else if (variableBinding.referencePaths.length === 0) {\n // Non-SFC file with no visible references – conservatively keep all fields.\n markUntrackedBinding();\n } else {\n recordFieldUsage(pruneContext, dictionaryKey, accessedTopLevelFieldNames);\n }\n return;\n }\n\n // ── Pattern 4: bare call – result is discarded ─────────────────────────────\n if (babelTypes.isExpressionStatement(parentNode)) {\n return; // no usage to record\n }\n\n // ── Fallback: result passed as argument, used in ternary, etc. ─────────────\n markUntrackedBinding();\n};\n\n/**\n * Creates a Babel plugin that traverses source files and records which\n * top-level dictionary fields each `useIntlayer` / `getIntlayer` call-site\n * accesses. Results are accumulated into `pruneContext`.\n *\n * This plugin is analysis-only: it does not transform the code (`code: false`\n * should be passed to `transformAsync` when using it).\n */\nexport const makeUsageAnalyzerBabelPlugin =\n (pruneContext: PruneContext) =>\n ({ types: babelTypes }: { types: typeof BabelTypes }): PluginObj => ({\n name: 'intlayer-usage-analyzer',\n visitor: {\n Program: {\n exit: (programPath, state: PluginPass) => {\n const currentSourceFilePath =\n state.file.opts.filename ?? 'unknown file';\n const isSfcFile =\n currentSourceFilePath.endsWith('.vue') ||\n currentSourceFilePath.endsWith('.svelte');\n\n // Phase 1: collect local aliases for useIntlayer / getIntlayer\n const intlayerCallerLocalNameMap = new Map<string, string>();\n\n programPath.traverse({\n ImportDeclaration: (importDeclarationPath) => {\n for (const importSpecifier of importDeclarationPath.node\n .specifiers) {\n if (!babelTypes.isImportSpecifier(importSpecifier)) continue;\n\n const importedName = babelTypes.isIdentifier(\n importSpecifier.imported\n )\n ? importSpecifier.imported.name\n : (importSpecifier.imported as BabelTypes.StringLiteral)\n .value;\n\n if (\n INTLAYER_CALLER_NAMES.includes(\n importedName as IntlayerCallerName\n )\n ) {\n intlayerCallerLocalNameMap.set(\n importSpecifier.local.name,\n importedName\n );\n }\n }\n },\n });\n\n if (intlayerCallerLocalNameMap.size === 0) return;\n\n // Phase 2: analyse each call-site\n programPath.traverse({\n CallExpression: (callExpressionPath) => {\n const calleeNode = callExpressionPath.node.callee;\n let localCallerName: string | undefined;\n\n if (babelTypes.isIdentifier(calleeNode)) {\n localCallerName = calleeNode.name;\n } else if (\n babelTypes.isMemberExpression(calleeNode) &&\n babelTypes.isIdentifier(calleeNode.property)\n ) {\n localCallerName = calleeNode.property.name;\n }\n\n if (\n !localCallerName ||\n !intlayerCallerLocalNameMap.has(localCallerName)\n )\n return;\n\n const callArguments = callExpressionPath.node.arguments;\n if (callArguments.length === 0) return;\n\n const firstArgument = callArguments[0];\n let dictionaryKey: string | undefined;\n\n if (babelTypes.isStringLiteral(firstArgument)) {\n dictionaryKey = firstArgument.value;\n } else if (\n babelTypes.isTemplateLiteral(firstArgument) &&\n firstArgument.expressions.length === 0 &&\n firstArgument.quasis.length === 1\n ) {\n dictionaryKey =\n firstArgument.quasis[0].value.cooked ??\n firstArgument.quasis[0].value.raw;\n }\n\n if (!dictionaryKey) return; // dynamic key – cannot resolve which dictionary\n\n analyzeCallExpressionUsage(\n babelTypes,\n pruneContext,\n callExpressionPath,\n dictionaryKey,\n currentSourceFilePath,\n isSfcFile\n );\n },\n });\n },\n },\n },\n });\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { resolveContentFilePaths } from "./utils/extractDictionaryInfo.mjs";
2
+ import { dirname, relative } from "node:path";
2
3
  import { existsSync } from "node:fs";
3
4
  import { mkdir } from "node:fs/promises";
4
- import { dirname, relative } from "node:path";
5
5
  import { buildDictionary, ensureIntlayerBundle, loadContentDeclaration, writeContentDeclaration } from "@intlayer/chokidar/build";
6
6
  import { insertContentInDictionary } from "@intlayer/core/plugins";
7
7
  import * as NodeTypes from "@intlayer/types/nodeType";
@@ -5,11 +5,11 @@ import { generateKey } from "./utils/generateKey.mjs";
5
5
  import { shouldExtract } from "./utils/shouldExtract.mjs";
6
6
  import { extractTsContent } from "./babelProcessor.mjs";
7
7
  import { processTsxFile } from "./processTsxFile.mjs";
8
+ import { extname, relative } from "node:path";
8
9
  import * as ANSIColors from "@intlayer/config/colors";
9
10
  import { colorize, colorizePath, getAppLogger } from "@intlayer/config/logger";
10
11
  import { getConfiguration } from "@intlayer/config/node";
11
12
  import { readFileSync } from "node:fs";
12
- import { extname, relative } from "node:path";
13
13
  import { getProjectRequire } from "@intlayer/config/utils";
14
14
  import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
15
15
  import { detectFormatCommand } from "@intlayer/chokidar/cli";
@@ -1,8 +1,8 @@
1
1
  import { extractDictionaryKey } from "./extractDictionaryKey.mjs";
2
+ import { basename, dirname, extname, relative, resolve } from "node:path";
2
3
  import { getFormatFromExtension, resolveRelativePath } from "@intlayer/chokidar/utils";
3
4
  import * as ANSIColors from "@intlayer/config/colors";
4
5
  import { colorize } from "@intlayer/config/logger";
5
- import { basename, dirname, extname, relative, resolve } from "node:path";
6
6
  import { parseStringPattern } from "@intlayer/config/utils";
7
7
  import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
8
8
 
@@ -1,5 +1,5 @@
1
- import { existsSync } from "node:fs";
2
1
  import { dirname, join } from "node:path";
2
+ import { existsSync } from "node:fs";
3
3
  import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
4
4
 
5
5
  //#region src/extractContent/utils/resolveDictionaryKey.ts
@@ -0,0 +1,77 @@
1
+ import { extname } from "node:path";
2
+
3
+ //#region src/extractScriptBlocks.ts
4
+ /**
5
+ * Regex that matches every `<script …>…<\/script>` block in an SFC (Vue or
6
+ * Svelte). Works for both instance scripts and module/setup scripts.
7
+ *
8
+ * Limitations (shared with `@intlayer/svelte-compiler`'s own approach):
9
+ * - A literal `<\/script>` inside a string or comment inside the script block
10
+ * would prematurely close the match. This is an accepted trade-off for the
11
+ * vast majority of real-world files.
12
+ */
13
+ const SFC_SCRIPT_RE = /<script([^>]*)>([\s\S]*?)<\/script>/g;
14
+ /**
15
+ * Extracts all `<script>` blocks from a Vue SFC or Svelte source string,
16
+ * returning each block's text content together with its start/end byte offsets
17
+ * in the original source.
18
+ *
19
+ * Uses the same regex strategy as `@intlayer/svelte-compiler` internally.
20
+ */
21
+ const extractSFCScriptBlocks = (code) => {
22
+ const blocks = [];
23
+ SFC_SCRIPT_RE.lastIndex = 0;
24
+ for (let match = SFC_SCRIPT_RE.exec(code); match !== null; match = SFC_SCRIPT_RE.exec(code)) {
25
+ const openingTagLength = 7 + match[1].length + 1;
26
+ const contentStart = match.index + openingTagLength;
27
+ const content = match[2];
28
+ blocks.push({
29
+ content,
30
+ contentStartOffset: contentStart,
31
+ contentEndOffset: contentStart + content.length
32
+ });
33
+ }
34
+ return blocks;
35
+ };
36
+ /**
37
+ * Extracts the script block(s) from a source file, dispatching by extension:
38
+ *
39
+ * - `.vue` / `.svelte` → searches for `<script>` blocks using a regex
40
+ * (same approach used by `@intlayer/svelte-compiler`). Returns one entry
41
+ * per block found (instance script + module/setup script).
42
+ * Returns an **empty array** when no `<script>` tag is found, which
43
+ * happens both for template-only SFCs and for already-compiled JS that
44
+ * Vite passes to `enforce:'post'` transform hooks.
45
+ * - everything else → treats the whole file as a single script block and
46
+ * returns it wrapped in a single-element array.
47
+ */
48
+ const extractScriptBlocks = (filePath, code) => {
49
+ const ext = extname(filePath);
50
+ if (ext === ".vue" || ext === ".svelte") return extractSFCScriptBlocks(code);
51
+ return [{
52
+ content: code,
53
+ contentStartOffset: 0,
54
+ contentEndOffset: code.length
55
+ }];
56
+ };
57
+ /**
58
+ * Applies modified script block content back into the original source string.
59
+ *
60
+ * Each entry in `modifications` pairs an original `ScriptBlock` (as returned
61
+ * by `extractScriptBlocks`) with the replacement text for its content.
62
+ * Replacements are applied in reverse offset order so that earlier offsets
63
+ * remain valid while later replacements are being processed.
64
+ *
65
+ * Returns `originalCode` unchanged when `modifications` is empty.
66
+ */
67
+ const injectScriptBlocks = (originalCode, modifications) => {
68
+ if (modifications.length === 0) return originalCode;
69
+ const sorted = [...modifications].sort((a, b) => b.block.contentStartOffset - a.block.contentStartOffset);
70
+ let result = originalCode;
71
+ for (const { block, modifiedContent } of sorted) result = result.slice(0, block.contentStartOffset) + modifiedContent + result.slice(block.contentEndOffset);
72
+ return result;
73
+ };
74
+
75
+ //#endregion
76
+ export { extractScriptBlocks, injectScriptBlocks };
77
+ //# sourceMappingURL=extractScriptBlocks.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractScriptBlocks.mjs","names":[],"sources":["../../src/extractScriptBlocks.ts"],"sourcesContent":["import { extname } from 'node:path';\n\n/**\n * A script block extracted from an SFC (Vue or Svelte) source file.\n *\n * `content` – The raw JS/TS text between the opening and closing\n * `<script>` tags (does NOT include the tags).\n * `contentStartOffset` – Byte offset of `content[0]` in the full source string.\n * `contentEndOffset` – Byte offset one past the last byte of `content` in the\n * full source string (i.e. `source.slice(start, end) === content`).\n */\nexport type ScriptBlock = {\n content: string;\n contentStartOffset: number;\n contentEndOffset: number;\n};\n\n// ── SFC script extraction ─────────────────────────────────────────────────────\n\n/**\n * Regex that matches every `<script …>…</script>` block in an SFC (Vue or\n * Svelte). Works for both instance scripts and module/setup scripts.\n *\n * Limitations (shared with `@intlayer/svelte-compiler`'s own approach):\n * - A literal `</script>` inside a string or comment inside the script block\n * would prematurely close the match. This is an accepted trade-off for the\n * vast majority of real-world files.\n */\nconst SFC_SCRIPT_RE = /<script([^>]*)>([\\s\\S]*?)<\\/script>/g;\n\n/**\n * Extracts all `<script>` blocks from a Vue SFC or Svelte source string,\n * returning each block's text content together with its start/end byte offsets\n * in the original source.\n *\n * Uses the same regex strategy as `@intlayer/svelte-compiler` internally.\n */\nconst extractSFCScriptBlocks = (code: string): ScriptBlock[] => {\n const blocks: ScriptBlock[] = [];\n SFC_SCRIPT_RE.lastIndex = 0;\n\n for (\n let match = SFC_SCRIPT_RE.exec(code);\n match !== null;\n match = SFC_SCRIPT_RE.exec(code)\n ) {\n // match[0]: full `<script ATTRS>CONTENT</script>`\n // match[1]: the attribute string (may be empty)\n // match[2]: the script content\n const openingTagLength = '<script'.length + match[1].length + '>'.length;\n const contentStart = match.index + openingTagLength;\n const content = match[2];\n blocks.push({\n content,\n contentStartOffset: contentStart,\n contentEndOffset: contentStart + content.length,\n });\n }\n\n return blocks;\n};\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Extracts the script block(s) from a source file, dispatching by extension:\n *\n * - `.vue` / `.svelte` → searches for `<script>` blocks using a regex\n * (same approach used by `@intlayer/svelte-compiler`). Returns one entry\n * per block found (instance script + module/setup script).\n * Returns an **empty array** when no `<script>` tag is found, which\n * happens both for template-only SFCs and for already-compiled JS that\n * Vite passes to `enforce:'post'` transform hooks.\n * - everything else → treats the whole file as a single script block and\n * returns it wrapped in a single-element array.\n */\nexport const extractScriptBlocks = (\n filePath: string,\n code: string\n): ScriptBlock[] => {\n const ext = extname(filePath);\n\n if (ext === '.vue' || ext === '.svelte') {\n return extractSFCScriptBlocks(code);\n }\n\n // Plain JS / TS / JSX / TSX / MJS / CJS – the whole file is the script.\n return [\n {\n content: code,\n contentStartOffset: 0,\n contentEndOffset: code.length,\n },\n ];\n};\n\n/**\n * Applies modified script block content back into the original source string.\n *\n * Each entry in `modifications` pairs an original `ScriptBlock` (as returned\n * by `extractScriptBlocks`) with the replacement text for its content.\n * Replacements are applied in reverse offset order so that earlier offsets\n * remain valid while later replacements are being processed.\n *\n * Returns `originalCode` unchanged when `modifications` is empty.\n */\nexport const injectScriptBlocks = (\n originalCode: string,\n modifications: ReadonlyArray<{\n block: ScriptBlock;\n modifiedContent: string;\n }>\n): string => {\n if (modifications.length === 0) return originalCode;\n\n const sorted = [...modifications].sort(\n (a, b) => b.block.contentStartOffset - a.block.contentStartOffset\n );\n\n let result = originalCode;\n for (const { block, modifiedContent } of sorted) {\n result =\n result.slice(0, block.contentStartOffset) +\n modifiedContent +\n result.slice(block.contentEndOffset);\n }\n\n return result;\n};\n"],"mappings":";;;;;;;;;;;;AA4BA,MAAM,gBAAgB;;;;;;;;AAStB,MAAM,0BAA0B,SAAgC;CAC9D,MAAM,SAAwB,EAAE;AAChC,eAAc,YAAY;AAE1B,MACE,IAAI,QAAQ,cAAc,KAAK,KAAK,EACpC,UAAU,MACV,QAAQ,cAAc,KAAK,KAAK,EAChC;EAIA,MAAM,mBAAmB,IAAmB,MAAM,GAAG,SAAS;EAC9D,MAAM,eAAe,MAAM,QAAQ;EACnC,MAAM,UAAU,MAAM;AACtB,SAAO,KAAK;GACV;GACA,oBAAoB;GACpB,kBAAkB,eAAe,QAAQ;GAC1C,CAAC;;AAGJ,QAAO;;;;;;;;;;;;;;AAiBT,MAAa,uBACX,UACA,SACkB;CAClB,MAAM,MAAM,QAAQ,SAAS;AAE7B,KAAI,QAAQ,UAAU,QAAQ,UAC5B,QAAO,uBAAuB,KAAK;AAIrC,QAAO,CACL;EACE,SAAS;EACT,oBAAoB;EACpB,kBAAkB,KAAK;EACxB,CACF;;;;;;;;;;;;AAaH,MAAa,sBACX,cACA,kBAIW;AACX,KAAI,cAAc,WAAW,EAAG,QAAO;CAEvC,MAAM,SAAS,CAAC,GAAG,cAAc,CAAC,MAC/B,GAAG,MAAM,EAAE,MAAM,qBAAqB,EAAE,MAAM,mBAChD;CAED,IAAI,SAAS;AACb,MAAK,MAAM,EAAE,OAAO,qBAAqB,OACvC,UACE,OAAO,MAAM,GAAG,MAAM,mBAAmB,GACzC,kBACA,OAAO,MAAM,MAAM,iBAAiB;AAGxC,QAAO"}
@@ -1,7 +1,7 @@
1
1
  import { __require } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { join } from "node:path";
2
3
  import { buildComponentFilesList } from "@intlayer/chokidar/utils";
3
4
  import { getConfiguration } from "@intlayer/config/node";
4
- import { join } from "node:path";
5
5
 
6
6
  //#region src/getOptimizePluginOptions.ts
7
7
  /**
@@ -1,3 +1,5 @@
1
+ import { INTLAYER_CALLER_NAMES, createPruneContext, makeUsageAnalyzerBabelPlugin } from "./babel-plugin-intlayer-usage-analyzer.mjs";
2
+ import { extractScriptBlocks, injectScriptBlocks } from "./extractScriptBlocks.mjs";
1
3
  import { extractDictionaryKey, extractDictionaryKeyFromPath } from "./extractContent/utils/extractDictionaryKey.mjs";
2
4
  import { extractDictionaryInfo, getOutput, resolveContentFilePaths } from "./extractContent/utils/extractDictionaryInfo.mjs";
3
5
  import { mergeWithExistingMultilingualDictionary, mergeWithExistingPerLocaleDictionary, writeContentHelper } from "./extractContent/contentWriter.mjs";
@@ -8,7 +10,9 @@ import { generateKey } from "./extractContent/utils/generateKey.mjs";
8
10
  import { getComponentName } from "./extractContent/utils/getComponentName.mjs";
9
11
  import { extractContent, extractContentSync } from "./extractContent/extractContent.mjs";
10
12
  import { intlayerExtractBabelPlugin } from "./babel-plugin-intlayer-extract.mjs";
13
+ import { buildNestedRenameMapFromContent, generateShortFieldName, makeFieldRenameBabelPlugin } from "./babel-plugin-intlayer-field-rename.mjs";
11
14
  import { getOptimizePluginOptions } from "./getOptimizePluginOptions.mjs";
12
15
  import { intlayerOptimizeBabelPlugin } from "./babel-plugin-intlayer-optimize.mjs";
16
+ import { BABEL_PARSER_OPTIONS, INTLAYER_USAGE_REGEX, SOURCE_FILE_REGEX, analyzeFieldUsageInFile, optimizeSourceFile, renameFieldsInCode, renameFieldsInSourceFile } from "./transformers.mjs";
13
17
 
14
- export { ATTRIBUTES_TO_EXTRACT, SERVER_CAPABLE_PACKAGES, detectPackageName, extractContent, extractContentSync, extractDictionaryInfo, extractDictionaryKey, extractDictionaryKeyFromPath, generateKey, getComponentName, getExtractPluginOptions, getOptimizePluginOptions, getOutput, intlayerExtractBabelPlugin, intlayerOptimizeBabelPlugin, mergeWithExistingMultilingualDictionary, mergeWithExistingPerLocaleDictionary, packageList, resolveContentFilePaths, writeContentHelper };
18
+ export { ATTRIBUTES_TO_EXTRACT, BABEL_PARSER_OPTIONS, INTLAYER_CALLER_NAMES, INTLAYER_USAGE_REGEX, SERVER_CAPABLE_PACKAGES, SOURCE_FILE_REGEX, analyzeFieldUsageInFile, buildNestedRenameMapFromContent, createPruneContext, detectPackageName, extractContent, extractContentSync, extractDictionaryInfo, extractDictionaryKey, extractDictionaryKeyFromPath, extractScriptBlocks, generateKey, generateShortFieldName, getComponentName, getExtractPluginOptions, getOptimizePluginOptions, getOutput, injectScriptBlocks, intlayerExtractBabelPlugin, intlayerOptimizeBabelPlugin, makeFieldRenameBabelPlugin, makeUsageAnalyzerBabelPlugin, mergeWithExistingMultilingualDictionary, mergeWithExistingPerLocaleDictionary, optimizeSourceFile, packageList, renameFieldsInCode, renameFieldsInSourceFile, resolveContentFilePaths, writeContentHelper };
@@ -0,0 +1,142 @@
1
+ import { makeUsageAnalyzerBabelPlugin } from "./babel-plugin-intlayer-usage-analyzer.mjs";
2
+ import { extractScriptBlocks, injectScriptBlocks } from "./extractScriptBlocks.mjs";
3
+ import { makeFieldRenameBabelPlugin } from "./babel-plugin-intlayer-field-rename.mjs";
4
+ import { intlayerOptimizeBabelPlugin } from "./babel-plugin-intlayer-optimize.mjs";
5
+ import { transformAsync } from "@babel/core";
6
+
7
+ //#region src/transformers.ts
8
+ /**
9
+ * Babel parser options covering the superset of syntaxes used across all
10
+ * supported frameworks (React / Vue / Svelte / Angular / …).
11
+ */
12
+ const BABEL_PARSER_OPTIONS = {
13
+ sourceType: "module",
14
+ allowImportExportEverywhere: true,
15
+ plugins: [
16
+ "typescript",
17
+ "jsx",
18
+ "decorators-legacy",
19
+ "classProperties",
20
+ "objectRestSpread",
21
+ "asyncGenerators",
22
+ "functionBind",
23
+ "exportDefaultFrom",
24
+ "exportNamespaceFrom",
25
+ "dynamicImport",
26
+ "nullishCoalescingOperator",
27
+ "optionalChaining"
28
+ ]
29
+ };
30
+ /**
31
+ * Fast pre-check: matches files that could contain intlayer calls.
32
+ * Avoids running Babel on files with no relevant identifiers.
33
+ */
34
+ const INTLAYER_USAGE_REGEX = /\b(use|get)Intlayer\b/;
35
+ /**
36
+ * Matches source files that are valid targets for usage analysis and Babel
37
+ * transformation. Excludes sourcemap files, declaration files, and other
38
+ * non-source extensions.
39
+ */
40
+ const SOURCE_FILE_REGEX = /\.(tsx?|[mc]?jsx?|vue|svelte)$/;
41
+ /**
42
+ * Runs the usage-analysis Babel plugin on a single JS/TS code string.
43
+ *
44
+ * This is analysis-only: the transformed code output is discarded.
45
+ * Throws if Babel cannot parse the content.
46
+ */
47
+ const analyzeScriptContent = async (scriptContent, sourceFilePath, pruneContext) => {
48
+ await transformAsync(scriptContent, {
49
+ filename: sourceFilePath,
50
+ plugins: [makeUsageAnalyzerBabelPlugin(pruneContext)],
51
+ parserOpts: BABEL_PARSER_OPTIONS,
52
+ ast: false,
53
+ code: false
54
+ });
55
+ };
56
+ /**
57
+ * Runs the usage-analysis Babel plugin on a source file, accumulating
58
+ * field-usage data into `pruneContext`.
59
+ *
60
+ * For Vue / Svelte SFC files, script blocks are extracted before analysis so
61
+ * Babel does not attempt to parse the full SFC syntax (templates, styles, …).
62
+ * For plain JS/TS files, the whole file content is analysed directly.
63
+ *
64
+ * This is analysis-only: the transformed code output is discarded.
65
+ * Throws if Babel cannot parse the file (caller should handle and flag
66
+ * `pruneContext.hasUnparsableSourceFiles`).
67
+ */
68
+ const analyzeFieldUsageInFile = async (sourceFilePath, code, pruneContext) => {
69
+ const scriptBlocks = extractScriptBlocks(sourceFilePath, code);
70
+ for (const block of scriptBlocks) {
71
+ if (!INTLAYER_USAGE_REGEX.test(block.content)) continue;
72
+ await analyzeScriptContent(block.content, sourceFilePath, pruneContext);
73
+ }
74
+ };
75
+ /**
76
+ * Applies field-renaming to a single JS/TS code string (not an SFC).
77
+ *
78
+ * Returns the renamed code string, or `null` if nothing changed or if
79
+ * Babel failed to parse the input (caller should fall back to original code).
80
+ */
81
+ const renameFieldsInCode = async (code, sourceFilePath, pruneContext) => {
82
+ try {
83
+ return (await transformAsync(code, {
84
+ filename: sourceFilePath,
85
+ plugins: [makeFieldRenameBabelPlugin(pruneContext)],
86
+ parserOpts: BABEL_PARSER_OPTIONS,
87
+ ast: false
88
+ }))?.code ?? null;
89
+ } catch {
90
+ return null;
91
+ }
92
+ };
93
+ /**
94
+ * Applies field-renaming to a source file, correctly handling both plain
95
+ * JS/TS files and SFC files (Vue / Svelte) by operating on each script block
96
+ * individually and injecting the results back into the original source.
97
+ *
98
+ * Returns the renamed code string, or `null` if nothing changed.
99
+ */
100
+ const renameFieldsInSourceFile = async (sourceFilePath, code, pruneContext) => {
101
+ if (pruneContext.dictionaryKeyToFieldRenameMap.size === 0) return null;
102
+ if (!INTLAYER_USAGE_REGEX.test(code)) return null;
103
+ const scriptBlocks = extractScriptBlocks(sourceFilePath, code);
104
+ if (scriptBlocks.length > 0 && (scriptBlocks[0].contentStartOffset > 0 || scriptBlocks.length > 1)) {
105
+ const modifications = [];
106
+ for (const block of scriptBlocks) {
107
+ if (!INTLAYER_USAGE_REGEX.test(block.content)) continue;
108
+ const renamedCode = await renameFieldsInCode(block.content, sourceFilePath, pruneContext);
109
+ if (renamedCode && renamedCode !== block.content) modifications.push({
110
+ block,
111
+ modifiedContent: renamedCode
112
+ });
113
+ }
114
+ if (modifications.length === 0) return null;
115
+ return injectScriptBlocks(code, modifications);
116
+ }
117
+ return renameFieldsInCode(code, sourceFilePath, pruneContext);
118
+ };
119
+ /**
120
+ * Runs the intlayer optimize Babel plugin on a source file, transforming
121
+ * `useIntlayer('key')` / `getIntlayer('key')` calls into `useDictionary(_hash)`
122
+ * / `getDictionary(_hash)` and injecting the corresponding dictionary imports.
123
+ *
124
+ * Returns `{ code, map }` on success, or `null` if the transformation produced
125
+ * no output.
126
+ */
127
+ const optimizeSourceFile = async (code, sourceFilePath, options) => {
128
+ const result = await transformAsync(code, {
129
+ filename: sourceFilePath,
130
+ plugins: [[intlayerOptimizeBabelPlugin, options]],
131
+ parserOpts: BABEL_PARSER_OPTIONS
132
+ });
133
+ if (!result?.code) return null;
134
+ return {
135
+ code: result.code,
136
+ map: result.map
137
+ };
138
+ };
139
+
140
+ //#endregion
141
+ export { BABEL_PARSER_OPTIONS, INTLAYER_USAGE_REGEX, SOURCE_FILE_REGEX, analyzeFieldUsageInFile, optimizeSourceFile, renameFieldsInCode, renameFieldsInSourceFile };
142
+ //# sourceMappingURL=transformers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformers.mjs","names":[],"sources":["../../src/transformers.ts"],"sourcesContent":["import { type TransformOptions, transformAsync } from '@babel/core';\nimport { makeFieldRenameBabelPlugin } from './babel-plugin-intlayer-field-rename';\nimport {\n intlayerOptimizeBabelPlugin,\n type OptimizePluginOptions,\n} from './babel-plugin-intlayer-optimize';\nimport {\n makeUsageAnalyzerBabelPlugin,\n type PruneContext,\n} from './babel-plugin-intlayer-usage-analyzer';\nimport { extractScriptBlocks, injectScriptBlocks } from './extractScriptBlocks';\n\n// ── Shared Babel parser configuration ─────────────────────────────────────────\n\n/**\n * Babel parser options covering the superset of syntaxes used across all\n * supported frameworks (React / Vue / Svelte / Angular / …).\n */\nexport const BABEL_PARSER_OPTIONS: NonNullable<TransformOptions['parserOpts']> =\n {\n sourceType: 'module',\n allowImportExportEverywhere: true,\n plugins: [\n 'typescript',\n 'jsx',\n 'decorators-legacy',\n 'classProperties',\n 'objectRestSpread',\n 'asyncGenerators',\n 'functionBind',\n 'exportDefaultFrom',\n 'exportNamespaceFrom',\n 'dynamicImport',\n 'nullishCoalescingOperator',\n 'optionalChaining',\n ],\n };\n\n/**\n * Fast pre-check: matches files that could contain intlayer calls.\n * Avoids running Babel on files with no relevant identifiers.\n */\nexport const INTLAYER_USAGE_REGEX = /\\b(use|get)Intlayer\\b/;\n\n/**\n * Matches source files that are valid targets for usage analysis and Babel\n * transformation. Excludes sourcemap files, declaration files, and other\n * non-source extensions.\n */\nexport const SOURCE_FILE_REGEX = /\\.(tsx?|[mc]?jsx?|vue|svelte)$/;\n\n// ── High-level transformer functions ──────────────────────────────────────────\n\n/**\n * Runs the usage-analysis Babel plugin on a single JS/TS code string.\n *\n * This is analysis-only: the transformed code output is discarded.\n * Throws if Babel cannot parse the content.\n */\nconst analyzeScriptContent = async (\n scriptContent: string,\n sourceFilePath: string,\n pruneContext: PruneContext\n): Promise<void> => {\n await transformAsync(scriptContent, {\n filename: sourceFilePath,\n plugins: [makeUsageAnalyzerBabelPlugin(pruneContext)],\n parserOpts: BABEL_PARSER_OPTIONS,\n ast: false,\n code: false, // analysis only – no output needed\n });\n};\n\n/**\n * Runs the usage-analysis Babel plugin on a source file, accumulating\n * field-usage data into `pruneContext`.\n *\n * For Vue / Svelte SFC files, script blocks are extracted before analysis so\n * Babel does not attempt to parse the full SFC syntax (templates, styles, …).\n * For plain JS/TS files, the whole file content is analysed directly.\n *\n * This is analysis-only: the transformed code output is discarded.\n * Throws if Babel cannot parse the file (caller should handle and flag\n * `pruneContext.hasUnparsableSourceFiles`).\n */\nexport const analyzeFieldUsageInFile = async (\n sourceFilePath: string,\n code: string,\n pruneContext: PruneContext\n): Promise<void> => {\n const scriptBlocks = extractScriptBlocks(sourceFilePath, code);\n\n // For SFC files (Vue / Svelte): scriptBlocks[0].contentStartOffset > 0\n // means we extracted actual <script> tags from the file.\n // For plain JS/TS: extractScriptBlocks returns the whole file as a single\n // block with offset 0, so we fall through to the same path.\n for (const block of scriptBlocks) {\n if (!INTLAYER_USAGE_REGEX.test(block.content)) continue;\n await analyzeScriptContent(block.content, sourceFilePath, pruneContext);\n }\n};\n\n/**\n * Applies field-renaming to a single JS/TS code string (not an SFC).\n *\n * Returns the renamed code string, or `null` if nothing changed or if\n * Babel failed to parse the input (caller should fall back to original code).\n */\nexport const renameFieldsInCode = async (\n code: string,\n sourceFilePath: string,\n pruneContext: PruneContext\n): Promise<string | null> => {\n try {\n const result = await transformAsync(code, {\n filename: sourceFilePath,\n plugins: [makeFieldRenameBabelPlugin(pruneContext)],\n parserOpts: BABEL_PARSER_OPTIONS,\n ast: false,\n });\n return result?.code ?? null;\n } catch {\n return null; // parse failure – caller falls back to original code\n }\n};\n\n/**\n * Applies field-renaming to a source file, correctly handling both plain\n * JS/TS files and SFC files (Vue / Svelte) by operating on each script block\n * individually and injecting the results back into the original source.\n *\n * Returns the renamed code string, or `null` if nothing changed.\n */\nexport const renameFieldsInSourceFile = async (\n sourceFilePath: string,\n code: string,\n pruneContext: PruneContext\n): Promise<string | null> => {\n if (pruneContext.dictionaryKeyToFieldRenameMap.size === 0) return null;\n if (!INTLAYER_USAGE_REGEX.test(code)) return null;\n\n const scriptBlocks = extractScriptBlocks(sourceFilePath, code);\n\n const isSFC =\n scriptBlocks.length > 0 &&\n (scriptBlocks[0].contentStartOffset > 0 || scriptBlocks.length > 1);\n\n if (isSFC) {\n // Raw SFC: rename each script block individually and inject back.\n const modifications: Array<{\n block: (typeof scriptBlocks)[number];\n modifiedContent: string;\n }> = [];\n\n for (const block of scriptBlocks) {\n if (!INTLAYER_USAGE_REGEX.test(block.content)) continue;\n\n const renamedCode = await renameFieldsInCode(\n block.content,\n sourceFilePath,\n pruneContext\n );\n if (renamedCode && renamedCode !== block.content) {\n modifications.push({ block, modifiedContent: renamedCode });\n }\n }\n\n if (modifications.length === 0) return null;\n return injectScriptBlocks(code, modifications);\n }\n\n // Plain JS/TS or compiled SFC (no block delimiters) – rename the whole file.\n return renameFieldsInCode(code, sourceFilePath, pruneContext);\n};\n\n/**\n * Runs the intlayer optimize Babel plugin on a source file, transforming\n * `useIntlayer('key')` / `getIntlayer('key')` calls into `useDictionary(_hash)`\n * / `getDictionary(_hash)` and injecting the corresponding dictionary imports.\n *\n * Returns `{ code, map }` on success, or `null` if the transformation produced\n * no output.\n */\nexport const optimizeSourceFile = async (\n code: string,\n sourceFilePath: string,\n options: OptimizePluginOptions\n): Promise<{\n code: string;\n map: string | object | null | undefined;\n} | null> => {\n const result = await transformAsync(code, {\n filename: sourceFilePath,\n plugins: [[intlayerOptimizeBabelPlugin, options]],\n parserOpts: BABEL_PARSER_OPTIONS,\n });\n\n if (!result?.code) return null;\n\n return { code: result.code, map: result.map };\n};\n"],"mappings":";;;;;;;;;;;AAkBA,MAAa,uBACX;CACE,YAAY;CACZ,6BAA6B;CAC7B,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACF;;;;;AAMH,MAAa,uBAAuB;;;;;;AAOpC,MAAa,oBAAoB;;;;;;;AAUjC,MAAM,uBAAuB,OAC3B,eACA,gBACA,iBACkB;AAClB,OAAM,eAAe,eAAe;EAClC,UAAU;EACV,SAAS,CAAC,6BAA6B,aAAa,CAAC;EACrD,YAAY;EACZ,KAAK;EACL,MAAM;EACP,CAAC;;;;;;;;;;;;;;AAeJ,MAAa,0BAA0B,OACrC,gBACA,MACA,iBACkB;CAClB,MAAM,eAAe,oBAAoB,gBAAgB,KAAK;AAM9D,MAAK,MAAM,SAAS,cAAc;AAChC,MAAI,CAAC,qBAAqB,KAAK,MAAM,QAAQ,CAAE;AAC/C,QAAM,qBAAqB,MAAM,SAAS,gBAAgB,aAAa;;;;;;;;;AAU3E,MAAa,qBAAqB,OAChC,MACA,gBACA,iBAC2B;AAC3B,KAAI;AAOF,UANe,MAAM,eAAe,MAAM;GACxC,UAAU;GACV,SAAS,CAAC,2BAA2B,aAAa,CAAC;GACnD,YAAY;GACZ,KAAK;GACN,CAAC,GACa,QAAQ;SACjB;AACN,SAAO;;;;;;;;;;AAWX,MAAa,2BAA2B,OACtC,gBACA,MACA,iBAC2B;AAC3B,KAAI,aAAa,8BAA8B,SAAS,EAAG,QAAO;AAClE,KAAI,CAAC,qBAAqB,KAAK,KAAK,CAAE,QAAO;CAE7C,MAAM,eAAe,oBAAoB,gBAAgB,KAAK;AAM9D,KAHE,aAAa,SAAS,MACrB,aAAa,GAAG,qBAAqB,KAAK,aAAa,SAAS,IAExD;EAET,MAAM,gBAGD,EAAE;AAEP,OAAK,MAAM,SAAS,cAAc;AAChC,OAAI,CAAC,qBAAqB,KAAK,MAAM,QAAQ,CAAE;GAE/C,MAAM,cAAc,MAAM,mBACxB,MAAM,SACN,gBACA,aACD;AACD,OAAI,eAAe,gBAAgB,MAAM,QACvC,eAAc,KAAK;IAAE;IAAO,iBAAiB;IAAa,CAAC;;AAI/D,MAAI,cAAc,WAAW,EAAG,QAAO;AACvC,SAAO,mBAAmB,MAAM,cAAc;;AAIhD,QAAO,mBAAmB,MAAM,gBAAgB,aAAa;;;;;;;;;;AAW/D,MAAa,qBAAqB,OAChC,MACA,gBACA,YAIW;CACX,MAAM,SAAS,MAAM,eAAe,MAAM;EACxC,UAAU;EACV,SAAS,CAAC,CAAC,6BAA6B,QAAQ,CAAC;EACjD,YAAY;EACb,CAAC;AAEF,KAAI,CAAC,QAAQ,KAAM,QAAO;AAE1B,QAAO;EAAE,MAAM,OAAO;EAAM,KAAK,OAAO;EAAK"}
@@ -0,0 +1,57 @@
1
+ import { NestedRenameMap, PruneContext } from "./babel-plugin-intlayer-usage-analyzer.js";
2
+ import { PluginObj } from "@babel/core";
3
+ import * as BabelTypes from "@babel/types";
4
+
5
+ //#region src/babel-plugin-intlayer-field-rename.d.ts
6
+ /**
7
+ * Converts a zero-based index to a short alphabetic identifier.
8
+ * 0 → 'a', 1 → 'b', …, 25 → 'z', 26 → 'aa', 27 → 'ab', …
9
+ */
10
+ declare const generateShortFieldName: (index: number) => string;
11
+ /**
12
+ * Recursively builds a `NestedRenameMap` from a compiled dictionary content
13
+ * value by traversing the intlayer node structure.
14
+ *
15
+ * Rules:
16
+ * - If the value has `nodeType: 'translation'`, user-defined fields live
17
+ * inside `translation[locale]`. Recurse into the first locale's value.
18
+ * - All other intlayer runtime nodes (enumeration, condition, gender, …) are
19
+ * treated as leaves — their internal keys must never be renamed.
20
+ * - Plain objects are user-defined records: rename their keys (filtered by
21
+ * `usedFieldFilter` at the top level) and recurse into each value.
22
+ * - Primitives and arrays produce an empty map (no further renaming).
23
+ *
24
+ * @param contentValue - The dictionary content value to analyse.
25
+ * @param usedFieldFilter - When provided, only keys in this set are included
26
+ * at the current level (matches the usage-analysis
27
+ * results so we don't rename purged fields).
28
+ */
29
+ declare const buildNestedRenameMapFromContent: (contentValue: unknown, usedFieldFilter?: Set<string>) => NestedRenameMap;
30
+ /**
31
+ * Creates a Babel plugin that rewrites dictionary content field accesses in
32
+ * source files to their short aliases defined in
33
+ * `pruneContext.dictionaryKeyToFieldRenameMap`.
34
+ *
35
+ * Handled patterns (mirrors the usage analyser):
36
+ *
37
+ * const { fieldA, fieldB } = useIntlayer('key')
38
+ * → const { shortA: fieldA, shortB: fieldB } = useIntlayer('key')
39
+ *
40
+ * useIntlayer('key').fieldA
41
+ * → useIntlayer('key').shortA
42
+ *
43
+ * const result = useIntlayer('key'); result.fieldA
44
+ * → const result = useIntlayer('key'); result.shortA
45
+ *
46
+ * This plugin must run in a separate `transformAsync` pass **before**
47
+ * `intlayerOptimizeBabelPlugin`, because the latter replaces `useIntlayer`
48
+ * with `useDictionary`, erasing the dictionary-key information needed here.
49
+ */
50
+ declare const makeFieldRenameBabelPlugin: (pruneContext: PruneContext) => ({
51
+ types: babelTypes
52
+ }: {
53
+ types: typeof BabelTypes;
54
+ }) => PluginObj;
55
+ //#endregion
56
+ export { buildNestedRenameMapFromContent, generateShortFieldName, makeFieldRenameBabelPlugin };
57
+ //# sourceMappingURL=babel-plugin-intlayer-field-rename.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"babel-plugin-intlayer-field-rename.d.ts","names":[],"sources":["../../src/babel-plugin-intlayer-field-rename.ts"],"mappings":";;;;;;;AAsBA;;cAAa,sBAAA,GAA0B,KAAA;;;AA2BvC;;;;;;;;;;AAqIA;;;;;;cArIa,+BAAA,GACX,YAAA,WACA,eAAA,GAAkB,GAAA,aACjB,eAAA;;;;;;;;;;;;;;;;;;;;;cAkIU,0BAAA,GACV,YAAA,EAAc,YAAA;EACd,KAAA,EAAA;AAAA;EAAyB,KAAA,SAAc,UAAA;AAAA,MAAe,SAAA"}
@@ -0,0 +1,119 @@
1
+ import { PluginObj } from "@babel/core";
2
+ import * as BabelTypes from "@babel/types";
3
+
4
+ //#region src/babel-plugin-intlayer-usage-analyzer.d.ts
5
+ /**
6
+ * Dictionary field usage result for a single dictionary key.
7
+ *
8
+ * 'all' → could not determine statically which fields are used;
9
+ * keep every field (no pruning possible).
10
+ * Set<string> → the exact top-level content field names that were accessed.
11
+ */
12
+ type DictionaryFieldUsage = Set<string> | 'all';
13
+ /**
14
+ * One node in the nested field-rename tree.
15
+ *
16
+ * shortName – the compact alias assigned to this field name.
17
+ * children – rename table for the next level of user-defined keys inside
18
+ * this field's value (empty when the value is a leaf / primitive).
19
+ */
20
+ type NestedRenameEntry = {
21
+ shortName: string;
22
+ children: NestedRenameMap;
23
+ };
24
+ /** A level of the field-rename tree, mapping original field names to entries. */
25
+ type NestedRenameMap = Map<string, NestedRenameEntry>;
26
+ /**
27
+ * Shared mutable state created once by the vite plugin and passed by reference
28
+ * to the usage-analyzer (writer) and the prune/minify plugins (readers).
29
+ *
30
+ * All mutations happen during the usage-analysis `buildStart` phase; readers
31
+ * only access this state during the subsequent `transform` phase.
32
+ */
33
+ type PruneContext = {
34
+ /**
35
+ * Maps every dictionary key seen in source files to the set of top-level
36
+ * content fields statically accessed, or `'all'` when the access pattern
37
+ * could not be determined.
38
+ */
39
+ dictionaryKeyToFieldUsageMap: Map<string, DictionaryFieldUsage>;
40
+ /**
41
+ * Dictionary keys for which the prune/minify step must be skipped entirely
42
+ * because an edge case was detected during analysis or structure recognition.
43
+ */
44
+ dictionariesWithEdgeCases: Set<string>;
45
+ /**
46
+ * True if at least one source file failed to parse during the analysis phase.
47
+ * The prune plugin uses this flag conservatively: any dictionary key without
48
+ * a usage entry might have been referenced by the unparsable file.
49
+ */
50
+ hasUnparsableSourceFiles: boolean;
51
+ /**
52
+ * Maps dictionary keys to the source file paths where the result of
53
+ * `useIntlayer` / `getIntlayer` was assigned to a plain variable, making
54
+ * static field analysis impossible.
55
+ */
56
+ dictionaryKeysWithUntrackedBindings: Map<string, string[]>;
57
+ /**
58
+ * Maps each dictionary key to a nested field-rename tree built after the
59
+ * usage analysis phase (only populated when `build.minify` is active and
60
+ * the field usage for that dictionary is a finite `Set<string>`).
61
+ */
62
+ dictionaryKeyToFieldRenameMap: Map<string, NestedRenameMap>;
63
+ /**
64
+ * Maps each dictionary key to a per-field list of source locations where
65
+ * the field value is consumed "opaquely" (passed as-is to a child component
66
+ * or function argument). When a field is opaque AND has nested user-defined
67
+ * structure, its children must not be renamed.
68
+ *
69
+ * Structure: dictionaryKey → fieldName → ["filePath:line", …]
70
+ */
71
+ dictionaryKeysWithOpaqueTopLevelFields: Map<string, Map<string, string[]>>;
72
+ /**
73
+ * Dictionary keys for which field-key renaming must be skipped even if a
74
+ * finite field-usage set was determined.
75
+ *
76
+ * Populated for dictionaries whose plain-variable bindings were resolved by
77
+ * the framework-specific extractor (Vue / Svelte SFCs), because the Babel
78
+ * rename plugin cannot update the source-code property accesses for those
79
+ * indirect patterns (Vue `.value.field` / Svelte `$store.field`).
80
+ *
81
+ * Pruning and basic minification still apply; only field-key renaming is
82
+ * suppressed.
83
+ */
84
+ dictionariesSkippingFieldRename: Set<string>;
85
+ /**
86
+ * Plain variable bindings that require a framework-specific secondary pass.
87
+ *
88
+ * Populated during the Babel analysis phase for `.vue` and `.svelte` source
89
+ * files where direct field access is not visible to Babel scope analysis:
90
+ * - Vue: `content.value.fieldName` – the `.value` ref-accessor is hidden
91
+ * - Svelte: `$varName.fieldName` – the `$` prefix creates a new identifier
92
+ *
93
+ * Structure: filePath → [{variableName, dictionaryKey}, …]
94
+ */
95
+ pendingFrameworkAnalysis: Map<string, {
96
+ variableName: string;
97
+ dictionaryKey: string;
98
+ }[]>;
99
+ };
100
+ declare const createPruneContext: () => PruneContext;
101
+ /** Canonical intlayer caller names that trigger usage analysis. */
102
+ declare const INTLAYER_CALLER_NAMES: readonly ["useIntlayer", "getIntlayer"];
103
+ type IntlayerCallerName = (typeof INTLAYER_CALLER_NAMES)[number];
104
+ /**
105
+ * Creates a Babel plugin that traverses source files and records which
106
+ * top-level dictionary fields each `useIntlayer` / `getIntlayer` call-site
107
+ * accesses. Results are accumulated into `pruneContext`.
108
+ *
109
+ * This plugin is analysis-only: it does not transform the code (`code: false`
110
+ * should be passed to `transformAsync` when using it).
111
+ */
112
+ declare const makeUsageAnalyzerBabelPlugin: (pruneContext: PruneContext) => ({
113
+ types: babelTypes
114
+ }: {
115
+ types: typeof BabelTypes;
116
+ }) => PluginObj;
117
+ //#endregion
118
+ export { DictionaryFieldUsage, INTLAYER_CALLER_NAMES, IntlayerCallerName, NestedRenameEntry, NestedRenameMap, PruneContext, createPruneContext, makeUsageAnalyzerBabelPlugin };
119
+ //# sourceMappingURL=babel-plugin-intlayer-usage-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"babel-plugin-intlayer-usage-analyzer.d.ts","names":[],"sources":["../../src/babel-plugin-intlayer-usage-analyzer.ts"],"mappings":";;;;;;AAYA;;;;;KAAY,oBAAA,GAAuB,GAAA;;;;;;;;KASvB,iBAAA;EACV,SAAA;EACA,QAAA,EAAU,eAAA;AAAA;;KAIA,eAAA,GAAkB,GAAA,SAAY,iBAAA;AAS1C;;;;;;;AAAA,KAAY,YAAA;EAiCqB;;;;;EA3B/B,4BAAA,EAA8B,GAAA,SAAY,oBAAA;EA+Db;;;;EAzD7B,yBAAA,EAA2B,GAAA;EAAA;;;;;EAO3B,wBAAA;EAc2C;;;;;EAP3C,mCAAA,EAAqC,GAAA;EA2CrC;;;;;EApCA,6BAAA,EAA+B,GAAA,SAAY,eAAA;EA0ChC;;;;;AAiBb;;;EAjDE,sCAAA,EAAwC,GAAA,SAAY,GAAA;EAiDsB;AAC5E;;;;;AA+QA;;;;;;EAnTE,+BAAA,EAAiC,GAAA;EAsZ/B;;;;;;;;;;EA1YF,wBAAA,EAA0B,GAAA;IAEtB,YAAA;IAAsB,aAAA;EAAA;AAAA;AAAA,cAIf,kBAAA,QAAyB,YAAA;;cAiBzB,qBAAA;AAAA,KACD,kBAAA,WAA6B,qBAAA;;;;;;;;;cA+Q5B,4BAAA,GACV,YAAA,EAAc,YAAA;EACd,KAAA,EAAA;AAAA;EAAyB,KAAA,SAAc,UAAA;AAAA,MAAe,SAAA"}
@@ -0,0 +1,45 @@
1
+ //#region src/extractScriptBlocks.d.ts
2
+ /**
3
+ * A script block extracted from an SFC (Vue or Svelte) source file.
4
+ *
5
+ * `content` – The raw JS/TS text between the opening and closing
6
+ * `<script>` tags (does NOT include the tags).
7
+ * `contentStartOffset` – Byte offset of `content[0]` in the full source string.
8
+ * `contentEndOffset` – Byte offset one past the last byte of `content` in the
9
+ * full source string (i.e. `source.slice(start, end) === content`).
10
+ */
11
+ type ScriptBlock = {
12
+ content: string;
13
+ contentStartOffset: number;
14
+ contentEndOffset: number;
15
+ };
16
+ /**
17
+ * Extracts the script block(s) from a source file, dispatching by extension:
18
+ *
19
+ * - `.vue` / `.svelte` → searches for `<script>` blocks using a regex
20
+ * (same approach used by `@intlayer/svelte-compiler`). Returns one entry
21
+ * per block found (instance script + module/setup script).
22
+ * Returns an **empty array** when no `<script>` tag is found, which
23
+ * happens both for template-only SFCs and for already-compiled JS that
24
+ * Vite passes to `enforce:'post'` transform hooks.
25
+ * - everything else → treats the whole file as a single script block and
26
+ * returns it wrapped in a single-element array.
27
+ */
28
+ declare const extractScriptBlocks: (filePath: string, code: string) => ScriptBlock[];
29
+ /**
30
+ * Applies modified script block content back into the original source string.
31
+ *
32
+ * Each entry in `modifications` pairs an original `ScriptBlock` (as returned
33
+ * by `extractScriptBlocks`) with the replacement text for its content.
34
+ * Replacements are applied in reverse offset order so that earlier offsets
35
+ * remain valid while later replacements are being processed.
36
+ *
37
+ * Returns `originalCode` unchanged when `modifications` is empty.
38
+ */
39
+ declare const injectScriptBlocks: (originalCode: string, modifications: ReadonlyArray<{
40
+ block: ScriptBlock;
41
+ modifiedContent: string;
42
+ }>) => string;
43
+ //#endregion
44
+ export { ScriptBlock, extractScriptBlocks, injectScriptBlocks };
45
+ //# sourceMappingURL=extractScriptBlocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractScriptBlocks.d.ts","names":[],"sources":["../../src/extractScriptBlocks.ts"],"mappings":";;AAWA;;;;;;;;KAAY,WAAA;EACV,OAAA;EACA,kBAAA;EACA,gBAAA;AAAA;;;;;;AA4FF;;;;;;;cA9Ba,mBAAA,GACX,QAAA,UACA,IAAA,aACC,WAAA;;;;;;;;;;;cA2BU,kBAAA,GACX,YAAA,UACA,aAAA,EAAe,aAAA;EACb,KAAA,EAAO,WAAA;EACP,eAAA;AAAA"}