@intlayer/lsp 9.7.0 → 9.7.2

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 (49) hide show
  1. package/dist/esm/_virtual/_rolldown/runtime.mjs +5 -6
  2. package/dist/esm/callers/editorCallers.mjs +1 -2
  3. package/dist/esm/callers/index.mjs +2 -3
  4. package/dist/esm/callers/patterns.mjs +2 -3
  5. package/dist/esm/dictionaryUtils.mjs +1 -2
  6. package/dist/esm/findFieldDeclaration.mjs +2 -3
  7. package/dist/esm/findFieldInFile.mjs +1 -2
  8. package/dist/esm/findKeyAtOffset.mjs +1 -2
  9. package/dist/esm/findKeyInContentFile.mjs +1 -2
  10. package/dist/esm/findTCallAtOffset.mjs +1 -2
  11. package/dist/esm/findUsageFieldAtOffset.mjs +1 -2
  12. package/dist/esm/index.mjs +9 -10
  13. package/dist/esm/index.mjs.map +1 -1
  14. package/dist/esm/oxcUtils.mjs +1 -2
  15. package/dist/esm/oxcUtils.mjs.map +1 -1
  16. package/dist/esm/protocol.mjs +1 -2
  17. package/dist/esm/resolveDictionaryTarget.mjs +1 -2
  18. package/dist/esm/usageAnalyzer.mjs +2 -3
  19. package/dist/esm/usageAnalyzer.mjs.map +1 -1
  20. package/dist/esm/utils.mjs +7 -7
  21. package/dist/types/callers/editorCallers.d.ts +0 -2
  22. package/dist/types/callers/editorCallers.d.ts.map +1 -1
  23. package/dist/types/callers/index.d.ts +12 -1
  24. package/dist/types/callers/index.d.ts.map +1 -1
  25. package/dist/types/callers/patterns.d.ts +0 -2
  26. package/dist/types/callers/patterns.d.ts.map +1 -1
  27. package/dist/types/dictionaryUtils.d.ts +0 -2
  28. package/dist/types/dictionaryUtils.d.ts.map +1 -1
  29. package/dist/types/findFieldDeclaration.d.ts +0 -2
  30. package/dist/types/findFieldDeclaration.d.ts.map +1 -1
  31. package/dist/types/findFieldInFile.d.ts +0 -2
  32. package/dist/types/findFieldInFile.d.ts.map +1 -1
  33. package/dist/types/findKeyAtOffset.d.ts +0 -2
  34. package/dist/types/findKeyAtOffset.d.ts.map +1 -1
  35. package/dist/types/findKeyInContentFile.d.ts +1 -2
  36. package/dist/types/findKeyInContentFile.d.ts.map +1 -1
  37. package/dist/types/findTCallAtOffset.d.ts +0 -2
  38. package/dist/types/findTCallAtOffset.d.ts.map +1 -1
  39. package/dist/types/findUsageFieldAtOffset.d.ts +0 -2
  40. package/dist/types/findUsageFieldAtOffset.d.ts.map +1 -1
  41. package/dist/types/oxcUtils.d.ts +0 -2
  42. package/dist/types/oxcUtils.d.ts.map +1 -1
  43. package/dist/types/protocol.d.ts +0 -2
  44. package/dist/types/protocol.d.ts.map +1 -1
  45. package/dist/types/resolveDictionaryTarget.d.ts +1 -2
  46. package/dist/types/resolveDictionaryTarget.d.ts.map +1 -1
  47. package/dist/types/usageAnalyzer.d.ts +0 -2
  48. package/dist/types/usageAnalyzer.d.ts.map +1 -1
  49. package/package.json +42 -42
@@ -1,20 +1,20 @@
1
- //#region \0rolldown/runtime.js
1
+ // \0rolldown/runtime.js
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __exportAll = (all, no_symbols) => {
7
- let target = {};
6
+ var __exportAll = (all, flags) => {
7
+ let target = Object.setPrototypeOf({}, null);
8
8
  for (var name in all) {
9
9
  __defProp(target, name, {
10
10
  get: all[name],
11
11
  enumerable: true
12
12
  });
13
13
  }
14
- if (!no_symbols) {
14
+ if (!(flags & 1)) {
15
15
  __defProp(target, Symbol.toStringTag, { value: "Module" });
16
16
  }
17
- return target;
17
+ return flags & 2 ? target : Object.preventExtensions(target);
18
18
  };
19
19
  var __copyProps = (to, from, except, desc) => {
20
20
  if (from && typeof from === "object" || typeof from === "function") {
@@ -32,5 +32,4 @@ var __copyProps = (to, from, except, desc) => {
32
32
  };
33
33
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
34
34
 
35
- //#endregion
36
35
  export { __exportAll, __reExport };
@@ -1,6 +1,6 @@
1
1
  import { COMPAT_CALLERS } from "@intlayer/config/callers";
2
2
 
3
- //#region src/callers/editorCallers.ts
3
+ // src/callers/editorCallers.ts
4
4
  /**
5
5
  * Every module a compat adapter is imported from (original library names and
6
6
  * their `@intlayer/*` equivalents), derived from the shared registry so a new
@@ -34,6 +34,5 @@ const EDITOR_CALLERS = [{
34
34
  translationFunction: "self"
35
35
  }];
36
36
 
37
- //#endregion
38
37
  export { EDITOR_CALLERS };
39
38
  //# sourceMappingURL=editorCallers.mjs.map
@@ -2,11 +2,10 @@ import { __exportAll, __reExport } from "../_virtual/_rolldown/runtime.mjs";
2
2
 
3
3
  export * from "@intlayer/config/callers"
4
4
 
5
- //#region src/callers/index.ts
6
- var callers_exports = /* @__PURE__ */ __exportAll({});
5
+ // src/callers/index.ts
6
+ var callers_exports = /* @__PURE__ */ __exportAll({}, 2);
7
7
  import * as import__intlayer_config_callers from "@intlayer/config/callers";
8
8
  __reExport(callers_exports, import__intlayer_config_callers);
9
9
 
10
- //#endregion
11
10
  export { callers_exports };
12
11
  //# sourceMappingURL=index.mjs.map
@@ -1,7 +1,7 @@
1
- import { callers_exports } from "./index.mjs";
2
1
  import { escapeRegularExpression } from "../findFieldInFile.mjs";
2
+ import { callers_exports } from "./index.mjs";
3
3
 
4
- //#region src/callers/patterns.ts
4
+ // src/callers/patterns.ts
5
5
  /**
6
6
  * Alternation of every caller name whose dictionary key appears as a
7
7
  * positional string argument — for building text-search patterns.
@@ -32,6 +32,5 @@ const buildKeyUsagePatterns = (key) => {
32
32
  */
33
33
  const buildCallerWithKeyPattern = (key) => new RegExp(`\\b(?:${getCallerNamesAlternation()})\\b(?:<[^<>()]*>)?\\s*\\(\\s*['"\`]${escapeRegularExpression(key)}['"\`]`);
34
34
 
35
- //#endregion
36
35
  export { buildCallerWithKeyPattern, buildKeyUsagePatterns, getCallerNamesAlternation };
37
36
  //# sourceMappingURL=patterns.mjs.map
@@ -1,4 +1,4 @@
1
- //#region src/dictionaryUtils.ts
1
+ // src/dictionaryUtils.ts
2
2
  const LOCALE_PATTERN = /^[a-z]{2}(-[A-Z]{2})?$/;
3
3
  const META_KEYS = /* @__PURE__ */ new Set([
4
4
  "nodeType",
@@ -79,6 +79,5 @@ const formatFieldHover = (dictionaries, dictionaryKey, fieldPath) => {
79
79
  return null;
80
80
  };
81
81
 
82
- //#endregion
83
82
  export { formatDictionaryHover, formatFieldHover, formatFieldValue, getFieldByPath, getFieldsAtPath, getTopLevelFields };
84
83
  //# sourceMappingURL=dictionaryUtils.mjs.map
@@ -1,8 +1,8 @@
1
- import { getPropertyKeyName, nodeEnd, nodeStart, parseText } from "./oxcUtils.mjs";
2
1
  import { escapeRegularExpression } from "./findFieldInFile.mjs";
2
+ import { getPropertyKeyName, nodeEnd, nodeStart, parseText } from "./oxcUtils.mjs";
3
3
  import { extname } from "node:path";
4
4
 
5
- //#region src/findFieldDeclaration.ts
5
+ // src/findFieldDeclaration.ts
6
6
  const JSON_EXTENSIONS = /* @__PURE__ */ new Set([
7
7
  ".json",
8
8
  ".jsonc",
@@ -147,6 +147,5 @@ const findFieldDeclaration = (text, filePath, dictionaryKey, fieldPath) => {
147
147
  return findLeafPropertyByText(text, fieldPath);
148
148
  };
149
149
 
150
- //#endregion
151
150
  export { findFieldDeclaration };
152
151
  //# sourceMappingURL=findFieldDeclaration.mjs.map
@@ -1,7 +1,7 @@
1
1
  import { collectMessageUsages } from "./usageAnalyzer.mjs";
2
2
  import { Position, Range } from "vscode-languageserver/node";
3
3
 
4
- //#region src/findFieldInFile.ts
4
+ // src/findFieldInFile.ts
5
5
  const escapeRegularExpression = (content) => content.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6
6
  const offsetToRange = (text, start, end) => {
7
7
  const beforeStart = text.slice(0, start);
@@ -48,6 +48,5 @@ const findFieldRangesInFile = (text, escapedKey, _fieldName, escapedField, field
48
48
  return ranges;
49
49
  };
50
50
 
51
- //#endregion
52
51
  export { escapeRegularExpression, findFieldRangesInFile, offsetToRange };
53
52
  //# sourceMappingURL=findFieldInFile.mjs.map
@@ -1,6 +1,6 @@
1
1
  import { collectNamespaceReferences } from "./usageAnalyzer.mjs";
2
2
 
3
- //#region src/findKeyAtOffset.ts
3
+ // src/findKeyAtOffset.ts
4
4
  /**
5
5
  * Scan `text` for a caller whose namespace (dictionary key) is addressed at
6
6
  * `offset`. Returns the namespace string when the cursor is on the caller
@@ -26,6 +26,5 @@ const findKeyAtOffset = (text, offset) => {
26
26
  return null;
27
27
  };
28
28
 
29
- //#endregion
30
29
  export { findKeyAtOffset };
31
30
  //# sourceMappingURL=findKeyAtOffset.mjs.map
@@ -1,6 +1,6 @@
1
1
  import { parseText, walkAst } from "./oxcUtils.mjs";
2
2
 
3
- //#region src/findKeyInContentFile.ts
3
+ // src/findKeyInContentFile.ts
4
4
  const KEY_DECLARATION_REGULAR_EXPRESSION = /(?:^|[{,\s])"?key"?\s*:\s*(?:(['"`])([^'"`\r\n]+)\1|([^ \t'"`{},\r\n]+))/g;
5
5
  /**
6
6
  * If `offset` falls within a `key:` declaration in a content file,
@@ -347,6 +347,5 @@ const findContentFieldAtOffsetOxc = (_text, offset, program) => {
347
347
  return foundResult;
348
348
  };
349
349
 
350
- //#endregion
351
350
  export { findContentFieldAtOffset, findKeyInContentFile };
352
351
  //# sourceMappingURL=findKeyInContentFile.mjs.map
@@ -1,6 +1,6 @@
1
1
  import { findMessageUsageAtOffset } from "./usageAnalyzer.mjs";
2
2
 
3
- //#region src/findTCallAtOffset.ts
3
+ // src/findTCallAtOffset.ts
4
4
  /**
5
5
  * Detect when the cursor is on a message usage carrying an explicit id: a
6
6
  * `t('key')` call from any translator caller, a self call
@@ -28,6 +28,5 @@ const findTCallAtOffset = (text, offset) => {
28
28
  };
29
29
  };
30
30
 
31
- //#endregion
32
31
  export { findTCallAtOffset };
33
32
  //# sourceMappingURL=findTCallAtOffset.mjs.map
@@ -1,6 +1,6 @@
1
1
  import { findMessageUsageAtOffset } from "./usageAnalyzer.mjs";
2
2
 
3
- //#region src/findUsageFieldAtOffset.ts
3
+ // src/findUsageFieldAtOffset.ts
4
4
  /**
5
5
  * Detect when the cursor is on a field name that was obtained from a
6
6
  * useIntlayer / getIntlayer call in the same file. Two patterns:
@@ -28,6 +28,5 @@ const findUsageFieldAtOffset = (text, offset) => {
28
28
  };
29
29
  };
30
30
 
31
- //#endregion
32
31
  export { findUsageFieldAtOffset };
33
32
  //# sourceMappingURL=findUsageFieldAtOffset.mjs.map
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { parseText, walkAst } from "./oxcUtils.mjs";
3
- import { DICTIONARIES_NOT_BUILT_NOTIFICATION } from "./protocol.mjs";
4
- import { collectNamespaceReferences, findMessageUsageAtOffset, matchCallNamespace } from "./usageAnalyzer.mjs";
5
- import { escapeRegularExpression, findFieldRangesInFile, offsetToRange } from "./findFieldInFile.mjs";
6
- import { findFieldDeclaration } from "./findFieldDeclaration.mjs";
7
- import { formatDictionaryHover, formatFieldHover, formatFieldValue, getFieldByPath, getFieldsAtPath } from "./dictionaryUtils.mjs";
8
- import { resolveDictionaryTarget } from "./resolveDictionaryTarget.mjs";
9
2
  import { buildCallerWithKeyPattern, buildKeyUsagePatterns, getCallerNamesAlternation } from "./callers/patterns.mjs";
3
+ import { formatDictionaryHover, formatFieldHover, formatFieldValue, getFieldByPath, getFieldsAtPath } from "./dictionaryUtils.mjs";
4
+ import { findFieldDeclaration } from "./findFieldDeclaration.mjs";
5
+ import { escapeRegularExpression, findFieldRangesInFile, offsetToRange } from "./findFieldInFile.mjs";
10
6
  import { findKeyAtOffset } from "./findKeyAtOffset.mjs";
11
7
  import { findContentFieldAtOffset, findKeyInContentFile } from "./findKeyInContentFile.mjs";
12
- import { dirname, isAbsolute, join } from "node:path";
8
+ import { parseText, walkAst } from "./oxcUtils.mjs";
9
+ import { DICTIONARIES_NOT_BUILT_NOTIFICATION } from "./protocol.mjs";
10
+ import { resolveDictionaryTarget } from "./resolveDictionaryTarget.mjs";
11
+ import { collectNamespaceReferences, findMessageUsageAtOffset, matchCallNamespace } from "./usageAnalyzer.mjs";
13
12
  import { CompletionItemKind, DiagnosticSeverity, Location, MarkupKind, Position, ProposedFeatures, Range, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver/node";
13
+ import { dirname, isAbsolute, join } from "node:path";
14
14
  import { readFile } from "node:fs/promises";
15
15
  import { pathToFileURL } from "node:url";
16
16
  import { getConfiguration, searchConfigurationFile } from "@intlayer/config/node";
@@ -19,7 +19,7 @@ import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
19
19
  import { buildComponentFilesList } from "@intlayer/engine/utils";
20
20
  import { TextDocument } from "vscode-languageserver-textdocument";
21
21
 
22
- //#region src/index.ts
22
+ // src/index.ts
23
23
  const connection = createConnection(ProposedFeatures.all);
24
24
  const documents = new TextDocuments(TextDocument);
25
25
  /**
@@ -582,5 +582,4 @@ connection.onDidChangeWatchedFiles(() => {
582
582
  documents.listen(connection);
583
583
  connection.listen();
584
584
 
585
- //#endregion
586
585
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n getConfiguration,\n searchConfigurationFile,\n} from '@intlayer/config/node';\nimport { clearAllCache } from '@intlayer/config/utils';\nimport { getUnmergedDictionaries } from '@intlayer/dictionaries-entry/unmerged';\nimport { buildComponentFilesList } from '@intlayer/engine/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport {\n type CompletionItem,\n CompletionItemKind,\n type CompletionParams,\n createConnection,\n type Diagnostic,\n DiagnosticSeverity,\n type Hover,\n type HoverParams,\n type InitializeParams,\n type InitializeResult,\n Location,\n MarkupKind,\n Position,\n ProposedFeatures,\n Range,\n type ReferenceParams,\n TextDocumentSyncKind,\n TextDocuments,\n} from 'vscode-languageserver/node';\nimport { TextDocument } from 'vscode-languageserver-textdocument';\nimport {\n buildCallerWithKeyPattern,\n buildKeyUsagePatterns,\n getCallerNamesAlternation,\n} from './callers/patterns';\nimport {\n formatDictionaryHover,\n formatFieldHover,\n formatFieldValue,\n getFieldByPath,\n getFieldsAtPath,\n} from './dictionaryUtils';\nimport { findFieldDeclaration } from './findFieldDeclaration';\nimport {\n escapeRegularExpression,\n findFieldRangesInFile,\n offsetToRange,\n} from './findFieldInFile';\nimport { findKeyAtOffset } from './findKeyAtOffset';\nimport {\n findContentFieldAtOffset,\n findKeyInContentFile,\n} from './findKeyInContentFile';\nimport { type OxcNode, parseText, walkAst } from './oxcUtils';\nimport {\n DICTIONARIES_NOT_BUILT_NOTIFICATION,\n type DictionariesNotBuiltParams,\n} from './protocol';\nimport {\n type ResolvedDictionaryTarget,\n resolveDictionaryTarget,\n type UsageTarget,\n} from './resolveDictionaryTarget';\nimport {\n collectNamespaceReferences,\n findMessageUsageAtOffset,\n matchCallNamespace,\n} from './usageAnalyzer';\n\n// Create a connection for the server, using Node's IPC as a transport.\n// Also include all preview / proposed LSP features.\nconst connection = createConnection(ProposedFeatures.all);\n\n// Create a simple text document manager.\nconst documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);\n\n/**\n * Send a log line to the client's Output panel (View → Output → \"Intlayer LSP\").\n * Uses connection.console.log so it travels over the JSON-RPC channel and never\n * touches stdout (which is reserved for the LSP protocol).\n */\nconst log = (message: string): void =>\n connection.console.log(`[Intlayer LSP] ${message}`);\n\nlet workspaceRoot: string | null | undefined;\n\ntype WorkspaceConfig = Pick<\n IntlayerConfig,\n 'system' | 'build' | 'content' | 'compiler' | 'internationalization'\n>;\n\n/**\n * Resolve (and cache) the Intlayer configuration for the current workspace.\n * The LSP process cwd is not the project root, so we must resolve the config\n * from `workspaceRoot` to get the right `system`/`build`/`content` paths.\n */\nconst projectConfigs = new Map<string, WorkspaceConfig>();\n\ntype DictionariesCache = {\n dictionaries: ReturnType<typeof getUnmergedDictionaries>;\n cachedAt: number;\n};\nconst projectDictionaries = new Map<string, DictionariesCache>();\nconst DICTIONARY_CACHE_TTL_MS = 5_000;\n\ntype SourceFilesCache = {\n files: string[];\n cachedAt: number;\n};\nconst projectSourceFiles = new Map<string, SourceFilesCache>();\nconst SOURCE_FILES_CACHE_TTL_MS = 30_000;\n\n/**\n * Directory of the file → directory holding the closest intlayer config.\n * Populated lazily by `getProjectRoot`.\n */\nconst projectRoots = new Map<string, string>();\n\n/**\n * Project roots already reported to the client as having no built dictionary.\n * Keeps the notification to one per project instead of one per open document;\n * cleared on invalidation so a later regression is reported again.\n */\nconst buildRequestedProjects = new Set<string>();\n\nconst invalidateConfigCaches = () => {\n // `getConfiguration` memoizes by options: without this a changed\n // configuration file would keep resolving to its previous evaluation.\n clearAllCache();\n projectConfigs.clear();\n projectDictionaries.clear();\n projectRoots.clear();\n buildRequestedProjects.clear();\n\n projectSourceFiles.clear();\n};\n\n/**\n * Resolve the project root for a file — the directory that holds the closest\n * intlayer config file.\n *\n * `getConfiguration({ baseDir })` treats `baseDir` as the project root and\n * derives every `system.*` path from it, so passing the file's own directory\n * would resolve `.intlayer/` next to the file (e.g. `src/.intlayer/…`) instead\n * of at the project root — leaving every dictionary lookup empty.\n */\nconst getProjectRoot = (fileDirectory: string): string => {\n const cached = projectRoots.get(fileDirectory);\n\n if (cached !== undefined) return cached;\n\n const { configurationFilePath } = searchConfigurationFile(fileDirectory);\n const root = configurationFilePath\n ? dirname(configurationFilePath)\n : (workspaceRoot ?? fileDirectory);\n\n projectRoots.set(fileDirectory, root);\n return root;\n};\n\nconst getProjectConfig = (absolutePath: string): WorkspaceConfig | null => {\n try {\n // Find the closest intlayer config relative to the file.\n const baseDir = getProjectRoot(dirname(absolutePath));\n const { system, build, content, compiler, internationalization } =\n getConfiguration({ baseDir });\n const cacheKey = system.baseDir;\n\n if (!projectConfigs.has(cacheKey)) {\n projectConfigs.set(cacheKey, {\n system,\n build,\n content,\n compiler,\n internationalization,\n });\n log(\n `getProjectConfig — OK baseDir=${system.baseDir} unmergedDictionariesDir=${system.unmergedDictionariesDir}`\n );\n }\n return projectConfigs.get(cacheKey)!;\n } catch (error) {\n log(`getProjectConfig — FAILED: ${error}`);\n return null;\n }\n};\n\nconst getUnmergedDictionariesCached = (\n config: WorkspaceConfig\n): ReturnType<typeof getUnmergedDictionaries> => {\n const cacheKey = config.system.baseDir;\n const cached = projectDictionaries.get(cacheKey);\n const now = Date.now();\n\n if (cached && now - cached.cachedAt < DICTIONARY_CACHE_TTL_MS) {\n return cached.dictionaries;\n }\n\n const dictionaries = getUnmergedDictionaries(config);\n projectDictionaries.set(cacheKey, { dictionaries, cachedAt: now });\n return dictionaries;\n};\n\n// ---------------------------------------------------------------------------\n// Pattern helpers — derived lazily from the live config.\n// ---------------------------------------------------------------------------\n\nconst isContentFile = (uri: string, config: WorkspaceConfig): boolean => {\n const fileExtensions = config.content.fileExtensions ?? [];\n return fileExtensions.some((extension) => uri.endsWith(extension));\n};\n\n// ---------------------------------------------------------------------------\n// Async I/O helpers\n// ---------------------------------------------------------------------------\n\n/** Read a file as UTF-8, returning null on any error. */\nconst tryReadFile = async (filePath: string): Promise<string | null> => {\n try {\n return await readFile(filePath, 'utf-8');\n } catch {\n return null;\n }\n};\n\n/**\n * Run `callback` over every item with at most `limit` tasks in-flight at once.\n * JS is single-threaded so `results.push` inside callback is race-free.\n */\nconst runConcurrent = async <T>(\n items: T[],\n callback: (item: T) => Promise<void>,\n limit = 50\n): Promise<void> => {\n if (items.length === 0) return;\n let index = 0;\n const worker = async () => {\n while (index < items.length) {\n await callback(items[index++]!);\n }\n };\n await Promise.all(\n Array.from({ length: Math.min(limit, items.length) }, worker)\n );\n};\n\n/** Built dictionaries registered under `dictionaryKey`. */\nconst getDictionariesForKey = (\n config: WorkspaceConfig,\n dictionaryKey: string\n): Dictionary[] =>\n (\n getUnmergedDictionariesCached(config) as Record<\n string,\n Dictionary[] | undefined\n >\n )[dictionaryKey] ?? [];\n\n/**\n * Match a usage (or a bare dictionary key) against the built dictionaries,\n * following the compat adapters' fallbacks (`index` whole-file catalog, flat\n * dotted keys) — see `resolveDictionaryTarget`.\n */\nconst resolveTarget = (\n target: UsageTarget,\n config: WorkspaceConfig\n): Promise<ResolvedDictionaryTarget<Dictionary> | null> =>\n resolveDictionaryTarget(target, (dictionaryKey) =>\n getDictionariesForKey(config, dictionaryKey)\n );\n\n/**\n * Where a resolved target is declared: one location per source file (content\n * file, or one JSON catalog per locale), the default locale first so VS Code\n * previews it.\n */\nconst getDeclarationLocations = async (\n target: UsageTarget,\n absolutePath: string\n): Promise<Location[]> => {\n const config = getProjectConfig(absolutePath);\n\n if (!config) return [];\n\n const resolved = await resolveTarget(target, config);\n\n if (!resolved) return [];\n\n const defaultLocale = config.internationalization?.defaultLocale;\n const sourcePaths = [\n ...new Set(\n [...resolved.dictionaries]\n .sort(\n (first, second) =>\n Number(second.locale === defaultLocale) -\n Number(first.locale === defaultLocale)\n )\n .map((dictionary) => dictionary.filePath)\n .filter((filePath): filePath is string => Boolean(filePath))\n .map((filePath) =>\n isAbsolute(filePath)\n ? filePath\n : join(config.system.baseDir, filePath)\n )\n ),\n ];\n\n const locations = await Promise.all(\n sourcePaths.map(async (sourcePath) => {\n const text = await tryReadFile(sourcePath);\n\n if (text === null) return null;\n\n const span = findFieldDeclaration(\n text,\n sourcePath,\n resolved.dictionaryKey,\n resolved.fieldPath\n );\n const range = span\n ? offsetToRange(text, span.start, span.end)\n : Range.create(Position.create(0, 0), Position.create(0, 0));\n\n return Location.create(pathToFileURL(sourcePath).href, range);\n })\n );\n\n return locations.filter(\n (location): location is Location => location !== null\n );\n};\n\n/**\n * Return the cached source-file list, re-scanning only when the cache has\n * expired or been explicitly invalidated (config change, watched-file event).\n */\nconst getSourceFiles = async (\n directory: string,\n config: WorkspaceConfig\n): Promise<string[]> => {\n const now = Date.now();\n const cached = projectSourceFiles.get(directory);\n\n if (cached && now - cached.cachedAt < SOURCE_FILES_CACHE_TTL_MS) {\n return cached.files;\n }\n const startTime = Date.now();\n const files = buildComponentFilesList(config as unknown as IntlayerConfig, [\n '**/.output/**',\n '**/.svelte-kit/**',\n '**/src-tauri/**',\n '**/.next/**',\n '**/.nuxt/**',\n '**/.expo/**',\n '**/.vercel/**',\n '**/.turbo/**',\n '**/.tanstack/**',\n '**/dist/**',\n '**/build/**',\n '**/node_modules/**',\n ]);\n projectSourceFiles.set(directory, { files, cachedAt: Date.now() });\n log(\n `source files scanned: ${files.length} file(s) in ${Date.now() - startTime}ms`\n );\n return files;\n};\n\n/**\n * Return every source-file location that calls useIntlayer / getIntlayer with\n * the given key.\n */\nconst getKeyUsageLocations = async (\n key: string,\n absolutePath: string\n): Promise<Location[]> => {\n const config = getProjectConfig(absolutePath);\n\n if (!config) return [];\n\n const usagePatterns = buildKeyUsagePatterns(key);\n\n const filePaths = await getSourceFiles(config.system.baseDir, config);\n const locations: Location[] = [];\n const startTime = Date.now();\n\n await runConcurrent(filePaths, async (filePath) => {\n const text = await tryReadFile(filePath);\n // Quick string check before running the regexes — skips the vast majority of files\n\n if (!text?.includes(key)) return;\n\n const seenStarts = new Set<number>();\n\n for (const usagePattern of usagePatterns) {\n for (const match of text.matchAll(usagePattern)) {\n const matchStart = match.index!;\n\n if (seenStarts.has(matchStart)) continue;\n\n seenStarts.add(matchStart);\n const matchEnd = matchStart + match[0].length;\n locations.push(\n Location.create(\n pathToFileURL(filePath).href,\n offsetToRange(text, matchStart, matchEnd)\n )\n );\n }\n }\n });\n\n log(\n `getKeyUsageLocations(\"${key}\") — ${filePaths.length} file(s) searched in ${Date.now() - startTime}ms → ${locations.length} hit(s)`\n );\n return locations;\n};\n\n/**\n * Return every source-file location that accesses the field at `fieldPath` on\n * the result of `useIntlayer(dictionaryKey)` / `getIntlayer(dictionaryKey)`.\n *\n * `fieldPath` is an array of property names from the content root to the\n * target field (e.g. `['searchInput', 'text']` for a nested field).\n * Single-element paths (e.g. `['greet']`) behave like the previous\n * single-`fieldName` API.\n */\nconst getFieldUsageLocations = async (\n dictionaryKey: string,\n fieldPath: string[],\n absolutePath: string\n): Promise<Location[]> => {\n const config = getProjectConfig(absolutePath);\n\n if (!config || fieldPath.length === 0) return [];\n\n const leafFieldName = fieldPath[fieldPath.length - 1]!;\n const escapedKey = escapeRegularExpression(dictionaryKey);\n const escapedLeaf = escapeRegularExpression(leafFieldName);\n const useCallRegularExpression = buildCallerWithKeyPattern(dictionaryKey);\n\n const filePaths = await getSourceFiles(config.system.baseDir, config);\n const locations: Location[] = [];\n const startTime = Date.now();\n\n await runConcurrent(filePaths, async (filePath) => {\n const text = await tryReadFile(filePath);\n\n if (!text) return;\n // Pre-filter: dictionaryKey and every segment of the path must appear in the file\n\n if (!text.includes(dictionaryKey)) return;\n\n if (fieldPath.some((segment) => !text.includes(segment))) return;\n\n if (!useCallRegularExpression.test(text)) return;\n\n for (const range of findFieldRangesInFile(\n text,\n escapedKey,\n leafFieldName,\n escapedLeaf,\n fieldPath\n )) {\n locations.push(Location.create(pathToFileURL(filePath).href, range));\n }\n });\n\n log(\n `getFieldUsageLocations(\"${dictionaryKey}.${fieldPath.join('.')}\") — ${filePaths.length} file(s) searched in ${Date.now() - startTime}ms → ${locations.length} hit(s)`\n );\n return locations;\n};\n\n// ---------------------------------------------------------------------------\n// Diagnostics — push-based (no capability needed)\n// ---------------------------------------------------------------------------\n\nconst DIAGNOSTIC_DEBOUNCE_MS = 300;\nconst diagnosticTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n/**\n * Ask the client to build the project's dictionaries, at most once per project\n * root. The client answers by running the build, whose output trips the\n * watcher on `.intlayer/unmerged_dictionary/*.json` and invalidates the caches\n * here, which re-publishes diagnostics for every open document.\n */\nconst requestDictionaryBuild = (baseDir: string): void => {\n if (buildRequestedProjects.has(baseDir)) return;\n\n buildRequestedProjects.add(baseDir);\n log(`no built dictionary under ${baseDir} — asking the client to build`);\n connection.sendNotification(DICTIONARIES_NOT_BUILT_NOTIFICATION, {\n baseDir,\n } satisfies DictionariesNotBuiltParams);\n};\n\nconst computeDiagnostics = (\n text: string,\n config: WorkspaceConfig\n): Diagnostic[] => {\n const namespaceReferences = collectNamespaceReferences(text);\n\n if (namespaceReferences.length === 0) return [];\n\n const unmergedDictionaries = getUnmergedDictionariesCached(config);\n const knownKeys = new Set(\n Object.values(unmergedDictionaries)\n .flat()\n .map((dictionary) => dictionary?.key)\n .filter((key): key is string => typeof key === 'string')\n );\n\n // Nothing built: the project has never run a build, or a production build\n // cleaned `.intlayer/unmerged_dictionary` without rewriting it (only dev\n // builds and editor-enabled builds write there). Every reference would be\n // flagged, so request a build rather than fill the file with false\n // positives.\n if (knownKeys.size === 0) {\n requestDictionaryBuild(config.system.baseDir);\n return [];\n }\n\n const diagnostics: Diagnostic[] = [];\n\n for (const reference of namespaceReferences) {\n if (knownKeys.has(reference.dictionaryKey)) continue;\n\n diagnostics.push({\n range: offsetToRange(\n text,\n reference.namespaceStart,\n reference.namespaceEnd\n ),\n severity: DiagnosticSeverity.Warning,\n source: 'intlayer',\n message: `Dictionary key \"${reference.dictionaryKey}\" is not declared in any content file`,\n });\n }\n\n return diagnostics;\n};\n\nconst sendDiagnosticsForDocument = (\n uri: string,\n text: string,\n version: number | null\n): void => {\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const config = getProjectConfig(absolutePath);\n\n const diagnostics =\n config && !isContentFile(uri, config)\n ? computeDiagnostics(text, config)\n : [];\n\n connection.sendDiagnostics({\n uri,\n version: version ?? undefined,\n diagnostics,\n });\n};\n\nconst scheduleDiagnostics = (\n uri: string,\n text: string,\n version: number | null\n): void => {\n const existing = diagnosticTimers.get(uri);\n\n if (existing !== undefined) clearTimeout(existing);\n\n diagnosticTimers.set(\n uri,\n setTimeout(() => {\n diagnosticTimers.delete(uri);\n sendDiagnosticsForDocument(uri, text, version);\n }, DIAGNOSTIC_DEBOUNCE_MS)\n );\n};\n\n// ---------------------------------------------------------------------------\n// Completion context detection\n// ---------------------------------------------------------------------------\n\ntype CompletionCtx =\n | { kind: 'key' }\n | { kind: 'field'; dictionaryKey: string; fieldPath: string[] }\n | null;\n\n/** Find the dictionary key for a simple variable assigned from any registered caller. */\nconst resolveVariableToDictionaryKey = (\n program: OxcNode,\n varName: string\n): string | null => {\n let result: string | null = null;\n\n walkAst(program, (node) => {\n if (result) return true;\n\n if (node['type'] !== 'VariableDeclarator') return;\n const id = node['id'] as OxcNode | undefined;\n\n if (id?.['type'] !== 'Identifier' || (id['name'] as string) !== varName)\n return;\n const init = node['init'] as OxcNode | undefined;\n\n if (!init) return;\n result = matchCallNamespace(init);\n return true;\n });\n return result;\n};\n\n/**\n * Detect what kind of completion is appropriate at the given offset.\n *\n * Handles:\n * - key completion: cursor inside first string arg of useIntlayer/getIntlayer\n * - field (member): cursor after `variable.` where variable came from useIntlayer\n * - field (destructure): cursor inside `const { | } = useIntlayer(\"key\")`\n */\nconst getCompletionContext = (\n textBefore: string,\n textAfter: string,\n program: OxcNode | null\n): CompletionCtx => {\n // Key completion\n\n if (\n new RegExp(\n `\\\\b(?:${getCallerNamesAlternation()})\\\\b(?:<[^<>]*>)?\\\\s*\\\\(\\\\s*['\"\\`][^'\"\\`\\\\n]*$`\n ).test(textBefore)\n ) {\n return { kind: 'key' };\n }\n\n // Field completion via member access: rootVar.path.to.\n const memberMatch = /\\b(\\w+)((?:\\.\\w+)*)\\.$/.exec(textBefore);\n\n if (memberMatch && program) {\n const rootVar = memberMatch[1]!;\n const chainStr = memberMatch[2]!;\n const dictKey = resolveVariableToDictionaryKey(program, rootVar);\n\n if (dictKey) {\n const fieldPath = chainStr ? chainStr.slice(1).split('.') : [];\n return { kind: 'field', dictionaryKey: dictKey, fieldPath };\n }\n }\n\n // Field completion inside destructuring: const { | } = useIntlayer(\"key\")\n\n if (/(?:const|let|var)\\s*\\{[^}]*$/.test(textBefore)) {\n const destructMatch = new RegExp(\n `^[^}]*\\\\}\\\\s*=\\\\s*(?:${getCallerNamesAlternation()})(?:<[^<>]*>)?\\\\s*\\\\(\\\\s*['\"\\`]([^'\"\\`\\\\n]+)['\"\\`]`\n ).exec(textAfter);\n\n if (destructMatch) {\n return { kind: 'field', dictionaryKey: destructMatch[1]!, fieldPath: [] };\n }\n }\n\n return null;\n};\n\nconnection.onInitialize((parameters: InitializeParams): InitializeResult => {\n workspaceRoot =\n parameters.rootPath ||\n (parameters.workspaceFolders?.[0]?.uri\n ? decodeURIComponent(new URL(parameters.workspaceFolders[0].uri).pathname)\n : undefined);\n invalidateConfigCaches();\n\n log(`initialized — workspaceRoot: ${workspaceRoot ?? '(none)'}`);\n\n if (parameters.workspaceFolders?.length) {\n log(\n `workspaceFolders: ${parameters.workspaceFolders.map((folder) => folder.name).join(', ')}`\n );\n }\n\n return {\n capabilities: {\n textDocumentSync: TextDocumentSyncKind.Incremental,\n definitionProvider: true,\n referencesProvider: true,\n hoverProvider: true,\n completionProvider: {\n triggerCharacters: ['\"', \"'\", '`', '.'],\n resolveProvider: false,\n },\n },\n };\n});\n\nconnection.onDefinition(async (parameters) => {\n const uri = parameters.textDocument.uri;\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const position = parameters.position;\n const document = documents.get(uri);\n\n if (!document) {\n log(`onDefinition — document not found in cache: ${uri}`);\n return null;\n }\n\n const text = document.getText();\n const offset = document.offsetAt(position);\n const shortenedUri = uri\n .replace(/^file:\\/\\//, '')\n .split('/')\n .slice(-2)\n .join('/');\n\n log(`onDefinition — ${shortenedUri} ${position.line}:${position.character}`);\n const config = getProjectConfig(absolutePath);\n\n if (config && isContentFile(uri, config)) {\n // Content-file → component navigation is handled exclusively by the VS Code\n // extension's intlayerContentDefinitionProvider (which returns DefinitionLink[]\n // with a proper originSelectionRange). Returning null here prevents the LSP\n // from emitting a duplicate set of Location[] results for the same request.\n log('onDefinition — content file: delegating to extension provider');\n return null;\n }\n\n // Cursor on the key string / function name → navigate to content declaration.\n const key = findKeyAtOffset(text, offset);\n\n if (key) {\n log(\n `onDefinition — useIntlayer key: \"${key}\" → searching content declarations`\n );\n const locations = await getDeclarationLocations(\n { dictionaryKey: key, fieldPath: [] },\n absolutePath\n );\n log(\n `onDefinition — found ${locations.length} content declaration(s) for key \"${key}\"`\n );\n return locations.length ? locations : null;\n }\n\n // Cursor on any message usage: destructured property, member access,\n // t('path.to.field') call, formatMessage({ id }), <Trans>, lingui t`…`, …\n const usage = findMessageUsageAtOffset(text, offset);\n\n // Dictionary-level usages (a bare content variable) land on the dictionary.\n if (usage) {\n log(\n `onDefinition — usage: \"${[usage.dictionaryKey, ...usage.fieldPath].join('.')}\" (${usage.callerName}) → searching declaration`\n );\n const locations = await getDeclarationLocations(usage, absolutePath);\n log(`onDefinition — found ${locations.length} declaration(s)`);\n return locations.length ? locations : null;\n }\n\n log('onDefinition — no Intlayer key at cursor, no result');\n return null;\n});\n\n/**\n * \"Find All References\" handler (Shift+F12).\n *\n * From a content file, returns every source-file location that uses the key or\n * field at the cursor — the reverse direction of Go-to-Definition.\n *\n * From a source file, returns all content-file declarations for the key or\n * field at the cursor — complementing what Go-to-Definition already handles.\n */\nconnection.onReferences(async (parameters: ReferenceParams) => {\n const uri = parameters.textDocument.uri;\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const position = parameters.position;\n const document = documents.get(uri);\n\n if (!document) return null;\n\n const text = document.getText();\n const offset = document.offsetAt(position);\n const shortenedUri = uri\n .replace(/^file:\\/\\//, '')\n .split('/')\n .slice(-2)\n .join('/');\n\n log(`onReferences — ${shortenedUri} ${position.line}:${position.character}`);\n const config = getProjectConfig(absolutePath);\n\n if (config && isContentFile(uri, config)) {\n // Cursor on `key: \"…\"` → all useIntlayer(key) call sites\n const key = findKeyInContentFile(text, offset);\n\n if (key) {\n const locations = await getKeyUsageLocations(key, absolutePath);\n log(`onReferences — key \"${key}\" → ${locations.length} call site(s)`);\n return locations.length ? locations : null;\n }\n\n // Cursor on a content field → all field usage sites\n const extMatch = uri.match(/\\.[^.]+$/);\n const ext = extMatch ? extMatch[0] : '';\n const fieldInfo = findContentFieldAtOffset(text, offset, ext);\n\n if (fieldInfo) {\n const locations = await getFieldUsageLocations(\n fieldInfo.dictionaryKey,\n fieldInfo.fieldPath,\n absolutePath\n );\n log(\n `onReferences — field \"${fieldInfo.fieldPath.join('.')}\" → ${locations.length} usage(s)`\n );\n return locations.length ? locations : null;\n }\n\n return null;\n }\n\n // Source file: key string → content-file declarations\n const key = findKeyAtOffset(text, offset);\n\n if (key) {\n const locations = await getDeclarationLocations(\n { dictionaryKey: key, fieldPath: [] },\n absolutePath\n );\n log(`onReferences — key \"${key}\" → ${locations.length} declaration(s)`);\n return locations.length ? locations : null;\n }\n\n // Source file: field property, t('field') call, JSX message component, …\n // → content-file field definitions\n const usage = findMessageUsageAtOffset(text, offset);\n\n if (usage && usage.fieldPath.length > 0) {\n const locations = await getDeclarationLocations(usage, absolutePath);\n log(\n `onReferences — field \"${usage.fieldPath.join('.')}\" → ${locations.length} content definition(s)`\n );\n return locations.length ? locations : null;\n }\n\n return null;\n});\n\n// ---------------------------------------------------------------------------\n// Hover\n// ---------------------------------------------------------------------------\n\nconnection.onHover(async (parameters: HoverParams): Promise<Hover | null> => {\n const uri = parameters.textDocument.uri;\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const document = documents.get(uri);\n\n if (!document) return null;\n\n const text = document.getText();\n const offset = document.offsetAt(parameters.position);\n const config = getProjectConfig(absolutePath);\n\n if (!config) return null;\n\n if (isContentFile(uri, config)) {\n // Cursor on `key: \"...\"` declaration\n const key = findKeyInContentFile(text, offset);\n\n if (key) {\n const dicts = getDictionariesForKey(config, key);\n\n if (dicts.length > 0) {\n return {\n contents: {\n kind: MarkupKind.Markdown,\n value: formatDictionaryHover(dicts, key),\n },\n };\n }\n }\n\n // Cursor on a content field\n const ext = uri.match(/\\.[^.]+$/)?.[0] ?? '';\n const fieldInfo = findContentFieldAtOffset(text, offset, ext);\n\n if (fieldInfo) {\n const dicts = getDictionariesForKey(config, fieldInfo.dictionaryKey);\n\n if (dicts.length > 0) {\n const hoverText = formatFieldHover(\n dicts,\n fieldInfo.dictionaryKey,\n fieldInfo.fieldPath\n );\n\n if (hoverText) {\n return {\n contents: { kind: MarkupKind.Markdown, value: hoverText },\n };\n }\n }\n }\n\n return null;\n }\n\n // Source file: cursor on key string inside useIntlayer(\"key\")\n const key = findKeyAtOffset(text, offset);\n\n // Source file: cursor on the key string inside useIntlayer(\"key\"), or on\n // any message usage (field property, t('field'), formatMessage({ id }),\n // <Trans>, lingui t`…`, …)\n const target: UsageTarget | null = key\n ? { dictionaryKey: key, fieldPath: [] }\n : findMessageUsageAtOffset(text, offset);\n\n if (!target) return null;\n\n const resolved = await resolveTarget(target, config);\n\n if (!resolved) return null;\n\n if (resolved.fieldPath.length === 0) {\n return {\n contents: {\n kind: MarkupKind.Markdown,\n value: formatDictionaryHover(\n resolved.dictionaries,\n resolved.dictionaryKey\n ),\n },\n };\n }\n\n const hoverText = formatFieldHover(\n resolved.dictionaries,\n resolved.dictionaryKey,\n resolved.fieldPath\n );\n\n return hoverText\n ? { contents: { kind: MarkupKind.Markdown, value: hoverText } }\n : null;\n});\n\n// ---------------------------------------------------------------------------\n// Completion\n// ---------------------------------------------------------------------------\n\nconnection.onCompletion(\n async (parameters: CompletionParams): Promise<CompletionItem[] | null> => {\n const uri = parameters.textDocument.uri;\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const document = documents.get(uri);\n\n if (!document) return null;\n\n const text = document.getText();\n const offset = document.offsetAt(parameters.position);\n const config = getProjectConfig(absolutePath);\n\n if (!config) return null;\n\n const program = parseText(text);\n const context = getCompletionContext(\n text.slice(0, offset),\n text.slice(offset),\n program\n );\n\n if (!context) return null;\n\n const unmergedDictionaries = getUnmergedDictionariesCached(config);\n\n if (context.kind === 'key') {\n const seen = new Set<string>();\n const items: CompletionItem[] = [];\n for (const dictionary of Object.values(unmergedDictionaries).flat()) {\n if (!dictionary?.key || seen.has(dictionary.key)) continue;\n\n seen.add(dictionary.key);\n items.push({\n label: dictionary.key,\n kind: CompletionItemKind.Value,\n detail: dictionary.title ?? undefined,\n documentation: dictionary.description\n ? { kind: MarkupKind.Markdown, value: dictionary.description }\n : undefined,\n });\n }\n log(`onCompletion — key: ${items.length} dictionary keys`);\n return items.length > 0 ? items : null;\n }\n\n if (context.kind === 'field') {\n const dictionaries = Object.values(unmergedDictionaries)\n .flat()\n .filter((dictionary) => dictionary?.key === context.dictionaryKey);\n\n if (dictionaries.length === 0) return null;\n\n const primary = dictionaries[0]!;\n const fields = getFieldsAtPath(primary.content, context.fieldPath);\n const items: CompletionItem[] = fields.map((fieldName) => {\n const value = getFieldByPath(primary.content, [\n ...context.fieldPath,\n fieldName,\n ]);\n return {\n label: fieldName,\n kind: CompletionItemKind.Field,\n detail: formatFieldValue(value),\n };\n });\n log(\n `onCompletion — field \"${context.dictionaryKey}.${context.fieldPath.join('.')}\": ${items.length} field(s)`\n );\n return items.length > 0 ? items : null;\n }\n\n return null;\n }\n);\n\n// ---------------------------------------------------------------------------\n// Push diagnostics\n// ---------------------------------------------------------------------------\n\ndocuments.onDidOpen((event) => {\n sendDiagnosticsForDocument(\n event.document.uri,\n event.document.getText(),\n event.document.version\n );\n});\n\ndocuments.onDidChangeContent((event) => {\n scheduleDiagnostics(\n event.document.uri,\n event.document.getText(),\n event.document.version\n );\n});\n\ndocuments.onDidClose((event) => {\n const uri = event.document.uri;\n const timer = diagnosticTimers.get(uri);\n\n if (timer !== undefined) {\n clearTimeout(timer);\n diagnosticTimers.delete(uri);\n }\n connection.sendDiagnostics({ uri, diagnostics: [] });\n});\n\n// Invalidate the source-file list whenever the client reports a file change.\n// The client registers a watcher for *.content.* files; any creation or\n// deletion means the project layout may have changed.\nconnection.onDidChangeWatchedFiles(() => {\n invalidateConfigCaches();\n log(\n 'watched files changed — source file list and dictionary cache invalidated'\n );\n\n // Diagnostics are only recomputed on open/change, so a key reported as\n // undeclared would stay flagged after its content file (or the built\n // dictionaries) appear. Re-publish for every open document.\n for (const document of documents.all()) {\n scheduleDiagnostics(document.uri, document.getText(), document.version);\n }\n});\n\n// Make the text document manager listen on the connection\n// for open, change and close text document events\ndocuments.listen(connection);\n\n// Listen on the connection\nconnection.listen();\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n getConfiguration,\n searchConfigurationFile,\n} from '@intlayer/config/node';\nimport { clearAllCache } from '@intlayer/config/utils';\nimport { getUnmergedDictionaries } from '@intlayer/dictionaries-entry/unmerged';\nimport { buildComponentFilesList } from '@intlayer/engine/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { TextDocument } from 'vscode-languageserver-textdocument';\nimport {\n type CompletionItem,\n CompletionItemKind,\n type CompletionParams,\n createConnection,\n type Diagnostic,\n DiagnosticSeverity,\n type Hover,\n type HoverParams,\n type InitializeParams,\n type InitializeResult,\n Location,\n MarkupKind,\n Position,\n ProposedFeatures,\n Range,\n type ReferenceParams,\n TextDocumentSyncKind,\n TextDocuments,\n} from 'vscode-languageserver/node';\nimport {\n buildCallerWithKeyPattern,\n buildKeyUsagePatterns,\n getCallerNamesAlternation,\n} from './callers/patterns';\nimport {\n formatDictionaryHover,\n formatFieldHover,\n formatFieldValue,\n getFieldByPath,\n getFieldsAtPath,\n} from './dictionaryUtils';\nimport { findFieldDeclaration } from './findFieldDeclaration';\nimport {\n escapeRegularExpression,\n findFieldRangesInFile,\n offsetToRange,\n} from './findFieldInFile';\nimport { findKeyAtOffset } from './findKeyAtOffset';\nimport {\n findContentFieldAtOffset,\n findKeyInContentFile,\n} from './findKeyInContentFile';\nimport { type OxcNode, parseText, walkAst } from './oxcUtils';\nimport {\n DICTIONARIES_NOT_BUILT_NOTIFICATION,\n type DictionariesNotBuiltParams,\n} from './protocol';\nimport {\n type ResolvedDictionaryTarget,\n resolveDictionaryTarget,\n type UsageTarget,\n} from './resolveDictionaryTarget';\nimport {\n collectNamespaceReferences,\n findMessageUsageAtOffset,\n matchCallNamespace,\n} from './usageAnalyzer';\n\n// Create a connection for the server, using Node's IPC as a transport.\n// Also include all preview / proposed LSP features.\nconst connection = createConnection(ProposedFeatures.all);\n\n// Create a simple text document manager.\nconst documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);\n\n/**\n * Send a log line to the client's Output panel (View → Output → \"Intlayer LSP\").\n * Uses connection.console.log so it travels over the JSON-RPC channel and never\n * touches stdout (which is reserved for the LSP protocol).\n */\nconst log = (message: string): void =>\n connection.console.log(`[Intlayer LSP] ${message}`);\n\nlet workspaceRoot: string | null | undefined;\n\ntype WorkspaceConfig = Pick<\n IntlayerConfig,\n 'system' | 'build' | 'content' | 'compiler' | 'internationalization'\n>;\n\n/**\n * Resolve (and cache) the Intlayer configuration for the current workspace.\n * The LSP process cwd is not the project root, so we must resolve the config\n * from `workspaceRoot` to get the right `system`/`build`/`content` paths.\n */\nconst projectConfigs = new Map<string, WorkspaceConfig>();\n\ntype DictionariesCache = {\n dictionaries: ReturnType<typeof getUnmergedDictionaries>;\n cachedAt: number;\n};\nconst projectDictionaries = new Map<string, DictionariesCache>();\nconst DICTIONARY_CACHE_TTL_MS = 5_000;\n\ntype SourceFilesCache = {\n files: string[];\n cachedAt: number;\n};\nconst projectSourceFiles = new Map<string, SourceFilesCache>();\nconst SOURCE_FILES_CACHE_TTL_MS = 30_000;\n\n/**\n * Directory of the file → directory holding the closest intlayer config.\n * Populated lazily by `getProjectRoot`.\n */\nconst projectRoots = new Map<string, string>();\n\n/**\n * Project roots already reported to the client as having no built dictionary.\n * Keeps the notification to one per project instead of one per open document;\n * cleared on invalidation so a later regression is reported again.\n */\nconst buildRequestedProjects = new Set<string>();\n\nconst invalidateConfigCaches = () => {\n // `getConfiguration` memoizes by options: without this a changed\n // configuration file would keep resolving to its previous evaluation.\n clearAllCache();\n projectConfigs.clear();\n projectDictionaries.clear();\n projectRoots.clear();\n buildRequestedProjects.clear();\n\n projectSourceFiles.clear();\n};\n\n/**\n * Resolve the project root for a file — the directory that holds the closest\n * intlayer config file.\n *\n * `getConfiguration({ baseDir })` treats `baseDir` as the project root and\n * derives every `system.*` path from it, so passing the file's own directory\n * would resolve `.intlayer/` next to the file (e.g. `src/.intlayer/…`) instead\n * of at the project root — leaving every dictionary lookup empty.\n */\nconst getProjectRoot = (fileDirectory: string): string => {\n const cached = projectRoots.get(fileDirectory);\n\n if (cached !== undefined) return cached;\n\n const { configurationFilePath } = searchConfigurationFile(fileDirectory);\n const root = configurationFilePath\n ? dirname(configurationFilePath)\n : (workspaceRoot ?? fileDirectory);\n\n projectRoots.set(fileDirectory, root);\n return root;\n};\n\nconst getProjectConfig = (absolutePath: string): WorkspaceConfig | null => {\n try {\n // Find the closest intlayer config relative to the file.\n const baseDir = getProjectRoot(dirname(absolutePath));\n const { system, build, content, compiler, internationalization } =\n getConfiguration({ baseDir });\n const cacheKey = system.baseDir;\n\n if (!projectConfigs.has(cacheKey)) {\n projectConfigs.set(cacheKey, {\n system,\n build,\n content,\n compiler,\n internationalization,\n });\n log(\n `getProjectConfig — OK baseDir=${system.baseDir} unmergedDictionariesDir=${system.unmergedDictionariesDir}`\n );\n }\n return projectConfigs.get(cacheKey)!;\n } catch (error) {\n log(`getProjectConfig — FAILED: ${error}`);\n return null;\n }\n};\n\nconst getUnmergedDictionariesCached = (\n config: WorkspaceConfig\n): ReturnType<typeof getUnmergedDictionaries> => {\n const cacheKey = config.system.baseDir;\n const cached = projectDictionaries.get(cacheKey);\n const now = Date.now();\n\n if (cached && now - cached.cachedAt < DICTIONARY_CACHE_TTL_MS) {\n return cached.dictionaries;\n }\n\n const dictionaries = getUnmergedDictionaries(config);\n projectDictionaries.set(cacheKey, { dictionaries, cachedAt: now });\n return dictionaries;\n};\n\n// ---------------------------------------------------------------------------\n// Pattern helpers — derived lazily from the live config.\n// ---------------------------------------------------------------------------\n\nconst isContentFile = (uri: string, config: WorkspaceConfig): boolean => {\n const fileExtensions = config.content.fileExtensions ?? [];\n return fileExtensions.some((extension) => uri.endsWith(extension));\n};\n\n// ---------------------------------------------------------------------------\n// Async I/O helpers\n// ---------------------------------------------------------------------------\n\n/** Read a file as UTF-8, returning null on any error. */\nconst tryReadFile = async (filePath: string): Promise<string | null> => {\n try {\n return await readFile(filePath, 'utf-8');\n } catch {\n return null;\n }\n};\n\n/**\n * Run `callback` over every item with at most `limit` tasks in-flight at once.\n * JS is single-threaded so `results.push` inside callback is race-free.\n */\nconst runConcurrent = async <T>(\n items: T[],\n callback: (item: T) => Promise<void>,\n limit = 50\n): Promise<void> => {\n if (items.length === 0) return;\n let index = 0;\n const worker = async () => {\n while (index < items.length) {\n await callback(items[index++]!);\n }\n };\n await Promise.all(\n Array.from({ length: Math.min(limit, items.length) }, worker)\n );\n};\n\n/** Built dictionaries registered under `dictionaryKey`. */\nconst getDictionariesForKey = (\n config: WorkspaceConfig,\n dictionaryKey: string\n): Dictionary[] =>\n (\n getUnmergedDictionariesCached(config) as Record<\n string,\n Dictionary[] | undefined\n >\n )[dictionaryKey] ?? [];\n\n/**\n * Match a usage (or a bare dictionary key) against the built dictionaries,\n * following the compat adapters' fallbacks (`index` whole-file catalog, flat\n * dotted keys) — see `resolveDictionaryTarget`.\n */\nconst resolveTarget = (\n target: UsageTarget,\n config: WorkspaceConfig\n): Promise<ResolvedDictionaryTarget<Dictionary> | null> =>\n resolveDictionaryTarget(target, (dictionaryKey) =>\n getDictionariesForKey(config, dictionaryKey)\n );\n\n/**\n * Where a resolved target is declared: one location per source file (content\n * file, or one JSON catalog per locale), the default locale first so VS Code\n * previews it.\n */\nconst getDeclarationLocations = async (\n target: UsageTarget,\n absolutePath: string\n): Promise<Location[]> => {\n const config = getProjectConfig(absolutePath);\n\n if (!config) return [];\n\n const resolved = await resolveTarget(target, config);\n\n if (!resolved) return [];\n\n const defaultLocale = config.internationalization?.defaultLocale;\n const sourcePaths = [\n ...new Set(\n [...resolved.dictionaries]\n .sort(\n (first, second) =>\n Number(second.locale === defaultLocale) -\n Number(first.locale === defaultLocale)\n )\n .map((dictionary) => dictionary.filePath)\n .filter((filePath): filePath is string => Boolean(filePath))\n .map((filePath) =>\n isAbsolute(filePath)\n ? filePath\n : join(config.system.baseDir, filePath)\n )\n ),\n ];\n\n const locations = await Promise.all(\n sourcePaths.map(async (sourcePath) => {\n const text = await tryReadFile(sourcePath);\n\n if (text === null) return null;\n\n const span = findFieldDeclaration(\n text,\n sourcePath,\n resolved.dictionaryKey,\n resolved.fieldPath\n );\n const range = span\n ? offsetToRange(text, span.start, span.end)\n : Range.create(Position.create(0, 0), Position.create(0, 0));\n\n return Location.create(pathToFileURL(sourcePath).href, range);\n })\n );\n\n return locations.filter(\n (location): location is Location => location !== null\n );\n};\n\n/**\n * Return the cached source-file list, re-scanning only when the cache has\n * expired or been explicitly invalidated (config change, watched-file event).\n */\nconst getSourceFiles = async (\n directory: string,\n config: WorkspaceConfig\n): Promise<string[]> => {\n const now = Date.now();\n const cached = projectSourceFiles.get(directory);\n\n if (cached && now - cached.cachedAt < SOURCE_FILES_CACHE_TTL_MS) {\n return cached.files;\n }\n const startTime = Date.now();\n const files = buildComponentFilesList(config as unknown as IntlayerConfig, [\n '**/.output/**',\n '**/.svelte-kit/**',\n '**/src-tauri/**',\n '**/.next/**',\n '**/.nuxt/**',\n '**/.expo/**',\n '**/.vercel/**',\n '**/.turbo/**',\n '**/.tanstack/**',\n '**/dist/**',\n '**/build/**',\n '**/node_modules/**',\n ]);\n projectSourceFiles.set(directory, { files, cachedAt: Date.now() });\n log(\n `source files scanned: ${files.length} file(s) in ${Date.now() - startTime}ms`\n );\n return files;\n};\n\n/**\n * Return every source-file location that calls useIntlayer / getIntlayer with\n * the given key.\n */\nconst getKeyUsageLocations = async (\n key: string,\n absolutePath: string\n): Promise<Location[]> => {\n const config = getProjectConfig(absolutePath);\n\n if (!config) return [];\n\n const usagePatterns = buildKeyUsagePatterns(key);\n\n const filePaths = await getSourceFiles(config.system.baseDir, config);\n const locations: Location[] = [];\n const startTime = Date.now();\n\n await runConcurrent(filePaths, async (filePath) => {\n const text = await tryReadFile(filePath);\n // Quick string check before running the regexes — skips the vast majority of files\n\n if (!text?.includes(key)) return;\n\n const seenStarts = new Set<number>();\n\n for (const usagePattern of usagePatterns) {\n for (const match of text.matchAll(usagePattern)) {\n const matchStart = match.index!;\n\n if (seenStarts.has(matchStart)) continue;\n\n seenStarts.add(matchStart);\n const matchEnd = matchStart + match[0].length;\n locations.push(\n Location.create(\n pathToFileURL(filePath).href,\n offsetToRange(text, matchStart, matchEnd)\n )\n );\n }\n }\n });\n\n log(\n `getKeyUsageLocations(\"${key}\") — ${filePaths.length} file(s) searched in ${Date.now() - startTime}ms → ${locations.length} hit(s)`\n );\n return locations;\n};\n\n/**\n * Return every source-file location that accesses the field at `fieldPath` on\n * the result of `useIntlayer(dictionaryKey)` / `getIntlayer(dictionaryKey)`.\n *\n * `fieldPath` is an array of property names from the content root to the\n * target field (e.g. `['searchInput', 'text']` for a nested field).\n * Single-element paths (e.g. `['greet']`) behave like the previous\n * single-`fieldName` API.\n */\nconst getFieldUsageLocations = async (\n dictionaryKey: string,\n fieldPath: string[],\n absolutePath: string\n): Promise<Location[]> => {\n const config = getProjectConfig(absolutePath);\n\n if (!config || fieldPath.length === 0) return [];\n\n const leafFieldName = fieldPath[fieldPath.length - 1]!;\n const escapedKey = escapeRegularExpression(dictionaryKey);\n const escapedLeaf = escapeRegularExpression(leafFieldName);\n const useCallRegularExpression = buildCallerWithKeyPattern(dictionaryKey);\n\n const filePaths = await getSourceFiles(config.system.baseDir, config);\n const locations: Location[] = [];\n const startTime = Date.now();\n\n await runConcurrent(filePaths, async (filePath) => {\n const text = await tryReadFile(filePath);\n\n if (!text) return;\n // Pre-filter: dictionaryKey and every segment of the path must appear in the file\n\n if (!text.includes(dictionaryKey)) return;\n\n if (fieldPath.some((segment) => !text.includes(segment))) return;\n\n if (!useCallRegularExpression.test(text)) return;\n\n for (const range of findFieldRangesInFile(\n text,\n escapedKey,\n leafFieldName,\n escapedLeaf,\n fieldPath\n )) {\n locations.push(Location.create(pathToFileURL(filePath).href, range));\n }\n });\n\n log(\n `getFieldUsageLocations(\"${dictionaryKey}.${fieldPath.join('.')}\") — ${filePaths.length} file(s) searched in ${Date.now() - startTime}ms → ${locations.length} hit(s)`\n );\n return locations;\n};\n\n// ---------------------------------------------------------------------------\n// Diagnostics — push-based (no capability needed)\n// ---------------------------------------------------------------------------\n\nconst DIAGNOSTIC_DEBOUNCE_MS = 300;\nconst diagnosticTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n/**\n * Ask the client to build the project's dictionaries, at most once per project\n * root. The client answers by running the build, whose output trips the\n * watcher on `.intlayer/unmerged_dictionary/*.json` and invalidates the caches\n * here, which re-publishes diagnostics for every open document.\n */\nconst requestDictionaryBuild = (baseDir: string): void => {\n if (buildRequestedProjects.has(baseDir)) return;\n\n buildRequestedProjects.add(baseDir);\n log(`no built dictionary under ${baseDir} — asking the client to build`);\n connection.sendNotification(DICTIONARIES_NOT_BUILT_NOTIFICATION, {\n baseDir,\n } satisfies DictionariesNotBuiltParams);\n};\n\nconst computeDiagnostics = (\n text: string,\n config: WorkspaceConfig\n): Diagnostic[] => {\n const namespaceReferences = collectNamespaceReferences(text);\n\n if (namespaceReferences.length === 0) return [];\n\n const unmergedDictionaries = getUnmergedDictionariesCached(config);\n const knownKeys = new Set(\n Object.values(unmergedDictionaries)\n .flat()\n .map((dictionary) => dictionary?.key)\n .filter((key): key is string => typeof key === 'string')\n );\n\n // Nothing built: the project has never run a build, or a production build\n // cleaned `.intlayer/unmerged_dictionary` without rewriting it (only dev\n // builds and editor-enabled builds write there). Every reference would be\n // flagged, so request a build rather than fill the file with false\n // positives.\n if (knownKeys.size === 0) {\n requestDictionaryBuild(config.system.baseDir);\n return [];\n }\n\n const diagnostics: Diagnostic[] = [];\n\n for (const reference of namespaceReferences) {\n if (knownKeys.has(reference.dictionaryKey)) continue;\n\n diagnostics.push({\n range: offsetToRange(\n text,\n reference.namespaceStart,\n reference.namespaceEnd\n ),\n severity: DiagnosticSeverity.Warning,\n source: 'intlayer',\n message: `Dictionary key \"${reference.dictionaryKey}\" is not declared in any content file`,\n });\n }\n\n return diagnostics;\n};\n\nconst sendDiagnosticsForDocument = (\n uri: string,\n text: string,\n version: number | null\n): void => {\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const config = getProjectConfig(absolutePath);\n\n const diagnostics =\n config && !isContentFile(uri, config)\n ? computeDiagnostics(text, config)\n : [];\n\n connection.sendDiagnostics({\n uri,\n version: version ?? undefined,\n diagnostics,\n });\n};\n\nconst scheduleDiagnostics = (\n uri: string,\n text: string,\n version: number | null\n): void => {\n const existing = diagnosticTimers.get(uri);\n\n if (existing !== undefined) clearTimeout(existing);\n\n diagnosticTimers.set(\n uri,\n setTimeout(() => {\n diagnosticTimers.delete(uri);\n sendDiagnosticsForDocument(uri, text, version);\n }, DIAGNOSTIC_DEBOUNCE_MS)\n );\n};\n\n// ---------------------------------------------------------------------------\n// Completion context detection\n// ---------------------------------------------------------------------------\n\ntype CompletionCtx =\n | { kind: 'key' }\n | { kind: 'field'; dictionaryKey: string; fieldPath: string[] }\n | null;\n\n/** Find the dictionary key for a simple variable assigned from any registered caller. */\nconst resolveVariableToDictionaryKey = (\n program: OxcNode,\n varName: string\n): string | null => {\n let result: string | null = null;\n\n walkAst(program, (node) => {\n if (result) return true;\n\n if (node['type'] !== 'VariableDeclarator') return;\n const id = node['id'] as OxcNode | undefined;\n\n if (id?.['type'] !== 'Identifier' || (id['name'] as string) !== varName)\n return;\n const init = node['init'] as OxcNode | undefined;\n\n if (!init) return;\n result = matchCallNamespace(init);\n return true;\n });\n return result;\n};\n\n/**\n * Detect what kind of completion is appropriate at the given offset.\n *\n * Handles:\n * - key completion: cursor inside first string arg of useIntlayer/getIntlayer\n * - field (member): cursor after `variable.` where variable came from useIntlayer\n * - field (destructure): cursor inside `const { | } = useIntlayer(\"key\")`\n */\nconst getCompletionContext = (\n textBefore: string,\n textAfter: string,\n program: OxcNode | null\n): CompletionCtx => {\n // Key completion\n\n if (\n new RegExp(\n `\\\\b(?:${getCallerNamesAlternation()})\\\\b(?:<[^<>]*>)?\\\\s*\\\\(\\\\s*['\"\\`][^'\"\\`\\\\n]*$`\n ).test(textBefore)\n ) {\n return { kind: 'key' };\n }\n\n // Field completion via member access: rootVar.path.to.\n const memberMatch = /\\b(\\w+)((?:\\.\\w+)*)\\.$/.exec(textBefore);\n\n if (memberMatch && program) {\n const rootVar = memberMatch[1]!;\n const chainStr = memberMatch[2]!;\n const dictKey = resolveVariableToDictionaryKey(program, rootVar);\n\n if (dictKey) {\n const fieldPath = chainStr ? chainStr.slice(1).split('.') : [];\n return { kind: 'field', dictionaryKey: dictKey, fieldPath };\n }\n }\n\n // Field completion inside destructuring: const { | } = useIntlayer(\"key\")\n\n if (/(?:const|let|var)\\s*\\{[^}]*$/.test(textBefore)) {\n const destructMatch = new RegExp(\n `^[^}]*\\\\}\\\\s*=\\\\s*(?:${getCallerNamesAlternation()})(?:<[^<>]*>)?\\\\s*\\\\(\\\\s*['\"\\`]([^'\"\\`\\\\n]+)['\"\\`]`\n ).exec(textAfter);\n\n if (destructMatch) {\n return { kind: 'field', dictionaryKey: destructMatch[1]!, fieldPath: [] };\n }\n }\n\n return null;\n};\n\nconnection.onInitialize((parameters: InitializeParams): InitializeResult => {\n workspaceRoot =\n parameters.rootPath ||\n (parameters.workspaceFolders?.[0]?.uri\n ? decodeURIComponent(new URL(parameters.workspaceFolders[0].uri).pathname)\n : undefined);\n invalidateConfigCaches();\n\n log(`initialized — workspaceRoot: ${workspaceRoot ?? '(none)'}`);\n\n if (parameters.workspaceFolders?.length) {\n log(\n `workspaceFolders: ${parameters.workspaceFolders.map((folder) => folder.name).join(', ')}`\n );\n }\n\n return {\n capabilities: {\n textDocumentSync: TextDocumentSyncKind.Incremental,\n definitionProvider: true,\n referencesProvider: true,\n hoverProvider: true,\n completionProvider: {\n triggerCharacters: ['\"', \"'\", '`', '.'],\n resolveProvider: false,\n },\n },\n };\n});\n\nconnection.onDefinition(async (parameters) => {\n const uri = parameters.textDocument.uri;\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const position = parameters.position;\n const document = documents.get(uri);\n\n if (!document) {\n log(`onDefinition — document not found in cache: ${uri}`);\n return null;\n }\n\n const text = document.getText();\n const offset = document.offsetAt(position);\n const shortenedUri = uri\n .replace(/^file:\\/\\//, '')\n .split('/')\n .slice(-2)\n .join('/');\n\n log(`onDefinition — ${shortenedUri} ${position.line}:${position.character}`);\n const config = getProjectConfig(absolutePath);\n\n if (config && isContentFile(uri, config)) {\n // Content-file → component navigation is handled exclusively by the VS Code\n // extension's intlayerContentDefinitionProvider (which returns DefinitionLink[]\n // with a proper originSelectionRange). Returning null here prevents the LSP\n // from emitting a duplicate set of Location[] results for the same request.\n log('onDefinition — content file: delegating to extension provider');\n return null;\n }\n\n // Cursor on the key string / function name → navigate to content declaration.\n const key = findKeyAtOffset(text, offset);\n\n if (key) {\n log(\n `onDefinition — useIntlayer key: \"${key}\" → searching content declarations`\n );\n const locations = await getDeclarationLocations(\n { dictionaryKey: key, fieldPath: [] },\n absolutePath\n );\n log(\n `onDefinition — found ${locations.length} content declaration(s) for key \"${key}\"`\n );\n return locations.length ? locations : null;\n }\n\n // Cursor on any message usage: destructured property, member access,\n // t('path.to.field') call, formatMessage({ id }), <Trans>, lingui t`…`, …\n const usage = findMessageUsageAtOffset(text, offset);\n\n // Dictionary-level usages (a bare content variable) land on the dictionary.\n if (usage) {\n log(\n `onDefinition — usage: \"${[usage.dictionaryKey, ...usage.fieldPath].join('.')}\" (${usage.callerName}) → searching declaration`\n );\n const locations = await getDeclarationLocations(usage, absolutePath);\n log(`onDefinition — found ${locations.length} declaration(s)`);\n return locations.length ? locations : null;\n }\n\n log('onDefinition — no Intlayer key at cursor, no result');\n return null;\n});\n\n/**\n * \"Find All References\" handler (Shift+F12).\n *\n * From a content file, returns every source-file location that uses the key or\n * field at the cursor — the reverse direction of Go-to-Definition.\n *\n * From a source file, returns all content-file declarations for the key or\n * field at the cursor — complementing what Go-to-Definition already handles.\n */\nconnection.onReferences(async (parameters: ReferenceParams) => {\n const uri = parameters.textDocument.uri;\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const position = parameters.position;\n const document = documents.get(uri);\n\n if (!document) return null;\n\n const text = document.getText();\n const offset = document.offsetAt(position);\n const shortenedUri = uri\n .replace(/^file:\\/\\//, '')\n .split('/')\n .slice(-2)\n .join('/');\n\n log(`onReferences — ${shortenedUri} ${position.line}:${position.character}`);\n const config = getProjectConfig(absolutePath);\n\n if (config && isContentFile(uri, config)) {\n // Cursor on `key: \"…\"` → all useIntlayer(key) call sites\n const key = findKeyInContentFile(text, offset);\n\n if (key) {\n const locations = await getKeyUsageLocations(key, absolutePath);\n log(`onReferences — key \"${key}\" → ${locations.length} call site(s)`);\n return locations.length ? locations : null;\n }\n\n // Cursor on a content field → all field usage sites\n const extMatch = uri.match(/\\.[^.]+$/);\n const ext = extMatch ? extMatch[0] : '';\n const fieldInfo = findContentFieldAtOffset(text, offset, ext);\n\n if (fieldInfo) {\n const locations = await getFieldUsageLocations(\n fieldInfo.dictionaryKey,\n fieldInfo.fieldPath,\n absolutePath\n );\n log(\n `onReferences — field \"${fieldInfo.fieldPath.join('.')}\" → ${locations.length} usage(s)`\n );\n return locations.length ? locations : null;\n }\n\n return null;\n }\n\n // Source file: key string → content-file declarations\n const key = findKeyAtOffset(text, offset);\n\n if (key) {\n const locations = await getDeclarationLocations(\n { dictionaryKey: key, fieldPath: [] },\n absolutePath\n );\n log(`onReferences — key \"${key}\" → ${locations.length} declaration(s)`);\n return locations.length ? locations : null;\n }\n\n // Source file: field property, t('field') call, JSX message component, …\n // → content-file field definitions\n const usage = findMessageUsageAtOffset(text, offset);\n\n if (usage && usage.fieldPath.length > 0) {\n const locations = await getDeclarationLocations(usage, absolutePath);\n log(\n `onReferences — field \"${usage.fieldPath.join('.')}\" → ${locations.length} content definition(s)`\n );\n return locations.length ? locations : null;\n }\n\n return null;\n});\n\n// ---------------------------------------------------------------------------\n// Hover\n// ---------------------------------------------------------------------------\n\nconnection.onHover(async (parameters: HoverParams): Promise<Hover | null> => {\n const uri = parameters.textDocument.uri;\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const document = documents.get(uri);\n\n if (!document) return null;\n\n const text = document.getText();\n const offset = document.offsetAt(parameters.position);\n const config = getProjectConfig(absolutePath);\n\n if (!config) return null;\n\n if (isContentFile(uri, config)) {\n // Cursor on `key: \"...\"` declaration\n const key = findKeyInContentFile(text, offset);\n\n if (key) {\n const dicts = getDictionariesForKey(config, key);\n\n if (dicts.length > 0) {\n return {\n contents: {\n kind: MarkupKind.Markdown,\n value: formatDictionaryHover(dicts, key),\n },\n };\n }\n }\n\n // Cursor on a content field\n const ext = uri.match(/\\.[^.]+$/)?.[0] ?? '';\n const fieldInfo = findContentFieldAtOffset(text, offset, ext);\n\n if (fieldInfo) {\n const dicts = getDictionariesForKey(config, fieldInfo.dictionaryKey);\n\n if (dicts.length > 0) {\n const hoverText = formatFieldHover(\n dicts,\n fieldInfo.dictionaryKey,\n fieldInfo.fieldPath\n );\n\n if (hoverText) {\n return {\n contents: { kind: MarkupKind.Markdown, value: hoverText },\n };\n }\n }\n }\n\n return null;\n }\n\n // Source file: cursor on key string inside useIntlayer(\"key\")\n const key = findKeyAtOffset(text, offset);\n\n // Source file: cursor on the key string inside useIntlayer(\"key\"), or on\n // any message usage (field property, t('field'), formatMessage({ id }),\n // <Trans>, lingui t`…`, …)\n const target: UsageTarget | null = key\n ? { dictionaryKey: key, fieldPath: [] }\n : findMessageUsageAtOffset(text, offset);\n\n if (!target) return null;\n\n const resolved = await resolveTarget(target, config);\n\n if (!resolved) return null;\n\n if (resolved.fieldPath.length === 0) {\n return {\n contents: {\n kind: MarkupKind.Markdown,\n value: formatDictionaryHover(\n resolved.dictionaries,\n resolved.dictionaryKey\n ),\n },\n };\n }\n\n const hoverText = formatFieldHover(\n resolved.dictionaries,\n resolved.dictionaryKey,\n resolved.fieldPath\n );\n\n return hoverText\n ? { contents: { kind: MarkupKind.Markdown, value: hoverText } }\n : null;\n});\n\n// ---------------------------------------------------------------------------\n// Completion\n// ---------------------------------------------------------------------------\n\nconnection.onCompletion(\n async (parameters: CompletionParams): Promise<CompletionItem[] | null> => {\n const uri = parameters.textDocument.uri;\n const absolutePath = decodeURIComponent(new URL(uri).pathname);\n const document = documents.get(uri);\n\n if (!document) return null;\n\n const text = document.getText();\n const offset = document.offsetAt(parameters.position);\n const config = getProjectConfig(absolutePath);\n\n if (!config) return null;\n\n const program = parseText(text);\n const context = getCompletionContext(\n text.slice(0, offset),\n text.slice(offset),\n program\n );\n\n if (!context) return null;\n\n const unmergedDictionaries = getUnmergedDictionariesCached(config);\n\n if (context.kind === 'key') {\n const seen = new Set<string>();\n const items: CompletionItem[] = [];\n for (const dictionary of Object.values(unmergedDictionaries).flat()) {\n if (!dictionary?.key || seen.has(dictionary.key)) continue;\n\n seen.add(dictionary.key);\n items.push({\n label: dictionary.key,\n kind: CompletionItemKind.Value,\n detail: dictionary.title ?? undefined,\n documentation: dictionary.description\n ? { kind: MarkupKind.Markdown, value: dictionary.description }\n : undefined,\n });\n }\n log(`onCompletion — key: ${items.length} dictionary keys`);\n return items.length > 0 ? items : null;\n }\n\n if (context.kind === 'field') {\n const dictionaries = Object.values(unmergedDictionaries)\n .flat()\n .filter((dictionary) => dictionary?.key === context.dictionaryKey);\n\n if (dictionaries.length === 0) return null;\n\n const primary = dictionaries[0]!;\n const fields = getFieldsAtPath(primary.content, context.fieldPath);\n const items: CompletionItem[] = fields.map((fieldName) => {\n const value = getFieldByPath(primary.content, [\n ...context.fieldPath,\n fieldName,\n ]);\n return {\n label: fieldName,\n kind: CompletionItemKind.Field,\n detail: formatFieldValue(value),\n };\n });\n log(\n `onCompletion — field \"${context.dictionaryKey}.${context.fieldPath.join('.')}\": ${items.length} field(s)`\n );\n return items.length > 0 ? items : null;\n }\n\n return null;\n }\n);\n\n// ---------------------------------------------------------------------------\n// Push diagnostics\n// ---------------------------------------------------------------------------\n\ndocuments.onDidOpen((event) => {\n sendDiagnosticsForDocument(\n event.document.uri,\n event.document.getText(),\n event.document.version\n );\n});\n\ndocuments.onDidChangeContent((event) => {\n scheduleDiagnostics(\n event.document.uri,\n event.document.getText(),\n event.document.version\n );\n});\n\ndocuments.onDidClose((event) => {\n const uri = event.document.uri;\n const timer = diagnosticTimers.get(uri);\n\n if (timer !== undefined) {\n clearTimeout(timer);\n diagnosticTimers.delete(uri);\n }\n connection.sendDiagnostics({ uri, diagnostics: [] });\n});\n\n// Invalidate the source-file list whenever the client reports a file change.\n// The client registers a watcher for *.content.* files; any creation or\n// deletion means the project layout may have changed.\nconnection.onDidChangeWatchedFiles(() => {\n invalidateConfigCaches();\n log(\n 'watched files changed — source file list and dictionary cache invalidated'\n );\n\n // Diagnostics are only recomputed on open/change, so a key reported as\n // undeclared would stay flagged after its content file (or the built\n // dictionaries) appear. Re-publish for every open document.\n for (const document of documents.all()) {\n scheduleDiagnostics(document.uri, document.getText(), document.version);\n }\n});\n\n// Make the text document manager listen on the connection\n// for open, change and close text document events\ndocuments.listen(connection);\n\n// Listen on the connection\nconnection.listen();\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { parseSync } from "oxc-parser";
2
2
 
3
- //#region src/oxcUtils.ts
3
+ // src/oxcUtils.ts
4
4
  /**
5
5
  * Parse `text` into an Oxc program AST.
6
6
  * Returns null when the parse completely fails (e.g. unsupported syntax).
@@ -125,6 +125,5 @@ const getPropertyKeyName = (keyNode) => {
125
125
  return null;
126
126
  };
127
127
 
128
- //#endregion
129
128
  export { buildParentMap, getFirstStringArg, getObjectPropertyNode, getObjectPropertyString, getPropertyKeyName, getStaticStringValue, getStringArgAt, isStringLiteralNode, nodeContainsOffset, nodeEnd, nodeStart, parseText, walkAst };
130
129
  //# sourceMappingURL=oxcUtils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"oxcUtils.mjs","names":[],"sources":["../../src/oxcUtils.ts"],"sourcesContent":["import { parseSync } from 'oxc-parser';\n\nexport type OxcNode = Record<string, unknown>;\n\n/**\n * Parse `text` into an Oxc program AST.\n * Returns null when the parse completely fails (e.g. unsupported syntax).\n * Oxc is generally resilient to partial/in-progress edits and still returns a\n * usable AST alongside error diagnostics, so this is rare.\n */\nexport const parseText = (text: string): OxcNode | null => {\n try {\n const { program } = parseSync('file.tsx', text);\n\n return program as unknown as OxcNode;\n } catch {\n return null;\n }\n};\n\n/**\n * Depth-first walk of an Oxc AST. Visitor is called for every node that has\n * a `type` property; returning `true` prunes that subtree.\n */\nexport const walkAst = (\n node: unknown,\n // biome-ignore lint/suspicious/noConfusingVoidType: visitors without a return value must stay assignable\n visitor: (node: OxcNode) => boolean | void\n): void => {\n if (!node || typeof node !== 'object') return;\n\n if (Array.isArray(node)) {\n for (const child of node) walkAst(child, visitor);\n\n return;\n }\n const oxcNode = node as OxcNode;\n\n if (typeof oxcNode['type'] !== 'string') return;\n\n if (visitor(oxcNode)) return; // pruned\n\n for (const key of Object.keys(oxcNode)) {\n if (key === 'start' || key === 'end' || key === 'type') continue;\n walkAst(oxcNode[key], visitor);\n }\n};\n\n/**\n * Build a child → parent reference map for upward traversal.\n * Only AST-node-valued properties are followed; primitives are ignored.\n */\nexport const buildParentMap = (root: OxcNode): Map<OxcNode, OxcNode> => {\n const parentMap = new Map<OxcNode, OxcNode>();\n\n const walk = (node: unknown, parent: OxcNode | null): void => {\n if (!node || typeof node !== 'object') return;\n\n if (Array.isArray(node)) {\n for (const child of node) walk(child, parent);\n\n return;\n }\n const oxcNode = node as OxcNode;\n\n if (typeof oxcNode['type'] !== 'string') return;\n\n if (parent) parentMap.set(oxcNode, parent);\n\n for (const key of Object.keys(oxcNode)) {\n if (key === 'start' || key === 'end' || key === 'type') continue;\n walk(oxcNode[key], oxcNode);\n }\n };\n\n walk(root, null);\n return parentMap;\n};\n\n/** Byte offset of the node's start. */\nexport const nodeStart = (node: OxcNode): number =>\n (node['start'] as number | undefined) ??\n (node['range'] as number[] | undefined)?.[0] ??\n 0;\n\n/** Byte offset of the node's end. */\nexport const nodeEnd = (node: OxcNode): number =>\n (node['end'] as number | undefined) ??\n (node['range'] as number[] | undefined)?.[1] ??\n 0;\n\n/** Returns `true` when `offset` falls inside the node's span (inclusive). */\nexport const nodeContainsOffset = (node: OxcNode, offset: number): boolean =>\n offset >= nodeStart(node) && offset <= nodeEnd(node);\n\n/**\n * Returns `true` for nodes that carry a static string value:\n * Literal / StringLiteral, and no-substitution TemplateLiteral.\n */\nexport const isStringLiteralNode = (node: OxcNode): boolean => {\n const nodeType = node['type'] as string;\n\n if (nodeType === 'Literal' || nodeType === 'StringLiteral') {\n return typeof node['value'] === 'string';\n }\n\n if (nodeType === 'TemplateLiteral') {\n return ((node['expressions'] as unknown[])?.length ?? 0) === 0;\n }\n\n return false;\n};\n\n/**\n * Extract the static string value from a literal node, or null when the node\n * is not a static string.\n */\nexport const getStaticStringValue = (\n node: OxcNode | undefined\n): string | null => {\n if (!node) return null;\n const nodeType = node['type'] as string;\n\n if (\n (nodeType === 'Literal' || nodeType === 'StringLiteral') &&\n typeof node['value'] === 'string'\n ) {\n return node['value'];\n }\n\n if (\n nodeType === 'TemplateLiteral' &&\n ((node['expressions'] as unknown[])?.length ?? 0) === 0\n ) {\n const templateElements = node['quasis'] as OxcNode[] | undefined;\n const valueNode = templateElements?.[0]?.['value'] as OxcNode | undefined;\n\n return (\n (valueNode?.['cooked'] as string) ??\n (valueNode?.['raw'] as string) ??\n null\n );\n }\n\n return null;\n};\n\n/** Extract the string value from the Nth argument of a call expression. */\nexport const getStringArgAt = (\n callNode: OxcNode,\n index: number\n): string | null => {\n const argumentsList = callNode['arguments'] as OxcNode[] | undefined;\n\n if (!argumentsList || argumentsList.length <= index) return null;\n\n return getStaticStringValue(argumentsList[index]);\n};\n\n/** Extract the string value from the first argument of a call expression. */\nexport const getFirstStringArg = (callExpressionNode: OxcNode): string | null =>\n getStringArgAt(callExpressionNode, 0);\n\n/**\n * Find the property node named `propertyName` inside an ObjectExpression.\n */\nexport const getObjectPropertyNode = (\n objectExpression: OxcNode | undefined,\n propertyName: string\n): OxcNode | null => {\n if (objectExpression?.['type'] !== 'ObjectExpression') return null;\n\n for (const property of (objectExpression['properties'] as OxcNode[]) ?? []) {\n const propertyType = property['type'] as string;\n\n if (propertyType !== 'Property' && propertyType !== 'ObjectProperty') {\n continue;\n }\n\n const keyNode = property['key'] as OxcNode | undefined;\n const keyName =\n keyNode?.['type'] === 'Identifier'\n ? (keyNode['name'] as string)\n : typeof keyNode?.['value'] === 'string'\n ? (keyNode['value'] as string)\n : null;\n\n if (keyName === propertyName) return property;\n }\n\n return null;\n};\n\n/**\n * Read a static string property from an ObjectExpression argument,\n * e.g. the `namespace` of `useI18n({ namespace: 'about' })`.\n */\nexport const getObjectPropertyString = (\n objectExpression: OxcNode | undefined,\n propertyName: string\n): string | null => {\n const property = getObjectPropertyNode(objectExpression, propertyName);\n\n if (!property) return null;\n\n return getStaticStringValue(property['value'] as OxcNode | undefined);\n};\n\n/** Get the identifier or string name of an object-property key node. */\nexport const getPropertyKeyName = (\n keyNode: OxcNode | undefined\n): string | null => {\n if (!keyNode) return null;\n const nodeType = keyNode['type'] as string;\n\n if (nodeType === 'Identifier') return keyNode['name'] as string;\n\n if (\n (nodeType === 'Literal' || nodeType === 'StringLiteral') &&\n typeof keyNode['value'] === 'string'\n ) {\n return keyNode['value'];\n }\n\n return null;\n};\n"],"mappings":";;;;;;;;;AAUA,MAAa,aAAa,SAAiC;CACzD,IAAI;EACF,MAAM,EAAE,YAAY,UAAU,YAAY,IAAI;EAE9C,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAMA,MAAa,WACX,MAEA,YACS;CACT,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;CAEvC,IAAI,MAAM,QAAQ,IAAI,GAAG;EACvB,KAAK,MAAM,SAAS,MAAM,QAAQ,OAAO,OAAO;EAEhD;CACF;CACA,MAAM,UAAU;CAEhB,IAAI,OAAO,QAAQ,YAAY,UAAU;CAEzC,IAAI,QAAQ,OAAO,GAAG;CAEtB,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GAAG;EACtC,IAAI,QAAQ,WAAW,QAAQ,SAAS,QAAQ,QAAQ;EACxD,QAAQ,QAAQ,MAAM,OAAO;CAC/B;AACF;;;;;AAMA,MAAa,kBAAkB,SAAyC;CACtE,MAAM,4BAAY,IAAI,IAAsB;CAE5C,MAAM,QAAQ,MAAe,WAAiC;EAC5D,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;EAEvC,IAAI,MAAM,QAAQ,IAAI,GAAG;GACvB,KAAK,MAAM,SAAS,MAAM,KAAK,OAAO,MAAM;GAE5C;EACF;EACA,MAAM,UAAU;EAEhB,IAAI,OAAO,QAAQ,YAAY,UAAU;EAEzC,IAAI,QAAQ,UAAU,IAAI,SAAS,MAAM;EAEzC,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GAAG;GACtC,IAAI,QAAQ,WAAW,QAAQ,SAAS,QAAQ,QAAQ;GACxD,KAAK,QAAQ,MAAM,OAAO;EAC5B;CACF;CAEA,KAAK,MAAM,IAAI;CACf,OAAO;AACT;;AAGA,MAAa,aAAa,SACvB,KAAK,YACL,KAAK,QAAQ,GAA4B,MAC1C;;AAGF,MAAa,WAAW,SACrB,KAAK,UACL,KAAK,QAAQ,GAA4B,MAC1C;;AAGF,MAAa,sBAAsB,MAAe,WAChD,UAAU,UAAU,IAAI,KAAK,UAAU,QAAQ,IAAI;;;;;AAMrD,MAAa,uBAAuB,SAA2B;CAC7D,MAAM,WAAW,KAAK;CAEtB,IAAI,aAAa,aAAa,aAAa,iBACzC,OAAO,OAAO,KAAK,aAAa;CAGlC,IAAI,aAAa,mBACf,QAAS,KAAK,cAAc,EAAgB,UAAU,OAAO;CAG/D,OAAO;AACT;;;;;AAMA,MAAa,wBACX,SACkB;CAClB,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,WAAW,KAAK;CAEtB,KACG,aAAa,aAAa,aAAa,oBACxC,OAAO,KAAK,aAAa,UAEzB,OAAO,KAAK;CAGd,IACE,aAAa,sBACX,KAAK,cAAc,EAAgB,UAAU,OAAO,GACtD;EAEA,MAAM,YADmB,KAAK,SACI,GAAG,EAAE,GAAG;EAE1C,OACG,YAAY,aACZ,YAAY,UACb;CAEJ;CAEA,OAAO;AACT;;AAGA,MAAa,kBACX,UACA,UACkB;CAClB,MAAM,gBAAgB,SAAS;CAE/B,IAAI,CAAC,iBAAiB,cAAc,UAAU,OAAO,OAAO;CAE5D,OAAO,qBAAqB,cAAc,MAAM;AAClD;;AAGA,MAAa,qBAAqB,uBAChC,eAAe,oBAAoB,CAAC;;;;AAKtC,MAAa,yBACX,kBACA,iBACmB;CACnB,IAAI,mBAAmB,YAAY,oBAAoB,OAAO;CAE9D,KAAK,MAAM,YAAa,iBAAiB,iBAA+B,CAAC,GAAG;EAC1E,MAAM,eAAe,SAAS;EAE9B,IAAI,iBAAiB,cAAc,iBAAiB,kBAClD;EAGF,MAAM,UAAU,SAAS;EAQzB,KANE,UAAU,YAAY,eACjB,QAAQ,UACT,OAAO,UAAU,aAAa,WAC3B,QAAQ,WACT,UAEQ,cAAc,OAAO;CACvC;CAEA,OAAO;AACT;;;;;AAMA,MAAa,2BACX,kBACA,iBACkB;CAClB,MAAM,WAAW,sBAAsB,kBAAkB,YAAY;CAErE,IAAI,CAAC,UAAU,OAAO;CAEtB,OAAO,qBAAqB,SAAS,QAA+B;AACtE;;AAGA,MAAa,sBACX,YACkB;CAClB,IAAI,CAAC,SAAS,OAAO;CACrB,MAAM,WAAW,QAAQ;CAEzB,IAAI,aAAa,cAAc,OAAO,QAAQ;CAE9C,KACG,aAAa,aAAa,aAAa,oBACxC,OAAO,QAAQ,aAAa,UAE5B,OAAO,QAAQ;CAGjB,OAAO;AACT"}
1
+ {"version":3,"file":"oxcUtils.mjs","names":[],"sources":["../../src/oxcUtils.ts"],"sourcesContent":["import { parseSync } from 'oxc-parser';\n\nexport type OxcNode = Record<string, unknown>;\n\n/**\n * Parse `text` into an Oxc program AST.\n * Returns null when the parse completely fails (e.g. unsupported syntax).\n * Oxc is generally resilient to partial/in-progress edits and still returns a\n * usable AST alongside error diagnostics, so this is rare.\n */\nexport const parseText = (text: string): OxcNode | null => {\n try {\n const { program } = parseSync('file.tsx', text);\n\n return program as unknown as OxcNode;\n } catch {\n return null;\n }\n};\n\n/**\n * Depth-first walk of an Oxc AST. Visitor is called for every node that has\n * a `type` property; returning `true` prunes that subtree.\n */\nexport const walkAst = (\n node: unknown,\n // oxlint-disable-next-line typescript/no-invalid-void-type -- visitors without a return value must stay assignable\n visitor: (node: OxcNode) => boolean | void\n): void => {\n if (!node || typeof node !== 'object') return;\n\n if (Array.isArray(node)) {\n for (const child of node) walkAst(child, visitor);\n\n return;\n }\n const oxcNode = node as OxcNode;\n\n if (typeof oxcNode['type'] !== 'string') return;\n\n if (visitor(oxcNode)) return; // pruned\n\n for (const key of Object.keys(oxcNode)) {\n if (key === 'start' || key === 'end' || key === 'type') continue;\n walkAst(oxcNode[key], visitor);\n }\n};\n\n/**\n * Build a child → parent reference map for upward traversal.\n * Only AST-node-valued properties are followed; primitives are ignored.\n */\nexport const buildParentMap = (root: OxcNode): Map<OxcNode, OxcNode> => {\n const parentMap = new Map<OxcNode, OxcNode>();\n\n const walk = (node: unknown, parent: OxcNode | null): void => {\n if (!node || typeof node !== 'object') return;\n\n if (Array.isArray(node)) {\n for (const child of node) walk(child, parent);\n\n return;\n }\n const oxcNode = node as OxcNode;\n\n if (typeof oxcNode['type'] !== 'string') return;\n\n if (parent) parentMap.set(oxcNode, parent);\n\n for (const key of Object.keys(oxcNode)) {\n if (key === 'start' || key === 'end' || key === 'type') continue;\n walk(oxcNode[key], oxcNode);\n }\n };\n\n walk(root, null);\n return parentMap;\n};\n\n/** Byte offset of the node's start. */\nexport const nodeStart = (node: OxcNode): number =>\n (node['start'] as number | undefined) ??\n (node['range'] as number[] | undefined)?.[0] ??\n 0;\n\n/** Byte offset of the node's end. */\nexport const nodeEnd = (node: OxcNode): number =>\n (node['end'] as number | undefined) ??\n (node['range'] as number[] | undefined)?.[1] ??\n 0;\n\n/** Returns `true` when `offset` falls inside the node's span (inclusive). */\nexport const nodeContainsOffset = (node: OxcNode, offset: number): boolean =>\n offset >= nodeStart(node) && offset <= nodeEnd(node);\n\n/**\n * Returns `true` for nodes that carry a static string value:\n * Literal / StringLiteral, and no-substitution TemplateLiteral.\n */\nexport const isStringLiteralNode = (node: OxcNode): boolean => {\n const nodeType = node['type'] as string;\n\n if (nodeType === 'Literal' || nodeType === 'StringLiteral') {\n return typeof node['value'] === 'string';\n }\n\n if (nodeType === 'TemplateLiteral') {\n return ((node['expressions'] as unknown[])?.length ?? 0) === 0;\n }\n\n return false;\n};\n\n/**\n * Extract the static string value from a literal node, or null when the node\n * is not a static string.\n */\nexport const getStaticStringValue = (\n node: OxcNode | undefined\n): string | null => {\n if (!node) return null;\n const nodeType = node['type'] as string;\n\n if (\n (nodeType === 'Literal' || nodeType === 'StringLiteral') &&\n typeof node['value'] === 'string'\n ) {\n return node['value'];\n }\n\n if (\n nodeType === 'TemplateLiteral' &&\n ((node['expressions'] as unknown[])?.length ?? 0) === 0\n ) {\n const templateElements = node['quasis'] as OxcNode[] | undefined;\n const valueNode = templateElements?.[0]?.['value'] as OxcNode | undefined;\n\n return (\n (valueNode?.['cooked'] as string) ??\n (valueNode?.['raw'] as string) ??\n null\n );\n }\n\n return null;\n};\n\n/** Extract the string value from the Nth argument of a call expression. */\nexport const getStringArgAt = (\n callNode: OxcNode,\n index: number\n): string | null => {\n const argumentsList = callNode['arguments'] as OxcNode[] | undefined;\n\n if (!argumentsList || argumentsList.length <= index) return null;\n\n return getStaticStringValue(argumentsList[index]);\n};\n\n/** Extract the string value from the first argument of a call expression. */\nexport const getFirstStringArg = (callExpressionNode: OxcNode): string | null =>\n getStringArgAt(callExpressionNode, 0);\n\n/**\n * Find the property node named `propertyName` inside an ObjectExpression.\n */\nexport const getObjectPropertyNode = (\n objectExpression: OxcNode | undefined,\n propertyName: string\n): OxcNode | null => {\n if (objectExpression?.['type'] !== 'ObjectExpression') return null;\n\n for (const property of (objectExpression['properties'] as OxcNode[]) ?? []) {\n const propertyType = property['type'] as string;\n\n if (propertyType !== 'Property' && propertyType !== 'ObjectProperty') {\n continue;\n }\n\n const keyNode = property['key'] as OxcNode | undefined;\n const keyName =\n keyNode?.['type'] === 'Identifier'\n ? (keyNode['name'] as string)\n : typeof keyNode?.['value'] === 'string'\n ? (keyNode['value'] as string)\n : null;\n\n if (keyName === propertyName) return property;\n }\n\n return null;\n};\n\n/**\n * Read a static string property from an ObjectExpression argument,\n * e.g. the `namespace` of `useI18n({ namespace: 'about' })`.\n */\nexport const getObjectPropertyString = (\n objectExpression: OxcNode | undefined,\n propertyName: string\n): string | null => {\n const property = getObjectPropertyNode(objectExpression, propertyName);\n\n if (!property) return null;\n\n return getStaticStringValue(property['value'] as OxcNode | undefined);\n};\n\n/** Get the identifier or string name of an object-property key node. */\nexport const getPropertyKeyName = (\n keyNode: OxcNode | undefined\n): string | null => {\n if (!keyNode) return null;\n const nodeType = keyNode['type'] as string;\n\n if (nodeType === 'Identifier') return keyNode['name'] as string;\n\n if (\n (nodeType === 'Literal' || nodeType === 'StringLiteral') &&\n typeof keyNode['value'] === 'string'\n ) {\n return keyNode['value'];\n }\n\n return null;\n};\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- //#region src/protocol.ts
1
+ // src/protocol.ts
2
2
  /**
3
3
  * Custom notifications the Intlayer language server sends to its client, on
4
4
  * top of the standard LSP surface. Kept in a dedicated module so the client
@@ -17,6 +17,5 @@
17
17
  */
18
18
  const DICTIONARIES_NOT_BUILT_NOTIFICATION = "intlayer/dictionariesNotBuilt";
19
19
 
20
- //#endregion
21
20
  export { DICTIONARIES_NOT_BUILT_NOTIFICATION };
22
21
  //# sourceMappingURL=protocol.mjs.map
@@ -1,6 +1,6 @@
1
1
  import { getLibraryRootDictionaryKey } from "./usageAnalyzer.mjs";
2
2
 
3
- //#region src/resolveDictionaryTarget.ts
3
+ // src/resolveDictionaryTarget.ts
4
4
  /**
5
5
  * Dictionary produced by a JSON source holding a whole namespace-less catalog
6
6
  * (`./locales/{{locale}}.json`), which i18next's default namespace resolves to.
@@ -96,6 +96,5 @@ const resolveDictionaryTarget = async (target, loadDictionaries) => {
96
96
  return fallback;
97
97
  };
98
98
 
99
- //#endregion
100
99
  export { findContentPath, getUsageTargetCandidates, resolveDictionaryTarget };
101
100
  //# sourceMappingURL=resolveDictionaryTarget.mjs.map
@@ -1,8 +1,8 @@
1
- import { buildParentMap, getObjectPropertyNode, getObjectPropertyString, getPropertyKeyName, getStaticStringValue, nodeEnd, nodeStart, parseText, walkAst } from "./oxcUtils.mjs";
2
1
  import { callers_exports } from "./callers/index.mjs";
3
2
  import { EDITOR_CALLERS } from "./callers/editorCallers.mjs";
3
+ import { buildParentMap, getObjectPropertyNode, getObjectPropertyString, getPropertyKeyName, getStaticStringValue, nodeEnd, nodeStart, parseText, walkAst } from "./oxcUtils.mjs";
4
4
 
5
- //#region src/usageAnalyzer.ts
5
+ // src/usageAnalyzer.ts
6
6
  /**
7
7
  * Property names that are framework accessors rather than content fields
8
8
  * (react `.value`, vue refs `.value`, editor `.raw`/`.use`): they are skipped
@@ -906,6 +906,5 @@ const findMessageUsageAtOffset = (text, offset) => {
906
906
  return bestMatch;
907
907
  };
908
908
 
909
- //#endregion
910
909
  export { collectCallerBindings, collectMessageUsages, collectNamespaceReferences, findMessageUsageAtOffset, getLibraryRootDictionaryKey, matchCallNamespace };
911
910
  //# sourceMappingURL=usageAnalyzer.mjs.map