@ripple-ts/language-server 0.3.103 → 0.3.105

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.
@@ -50,6 +50,8 @@ let path = require("path");
50
50
  path = __toESM(path, 1);
51
51
  let module$1 = require("module");
52
52
  let url = require("url");
53
+ let node_path = require("node:path");
54
+ node_path = __toESM(node_path, 1);
53
55
  let _tsrx_core = require("@tsrx/core");
54
56
  let node_module = require("node:module");
55
57
  let volar_service_css = require("volar-service-css");
@@ -713,6 +715,507 @@ var require_language_core = /* @__PURE__ */ __commonJSMin(((exports) => {
713
715
  }
714
716
  }));
715
717
 
718
+ //#endregion
719
+ //#region ../typescript-plugin/src/tsconfig-resolution.js
720
+ var import_language_core = require_language_core();
721
+ /**
722
+ * @typedef {object} TsconfigHost
723
+ * @property {(file_name: string) => boolean} fileExists
724
+ * @property {(file_name: string) => string | undefined} readFile
725
+ * @property {import('typescript').ParseConfigHost['readDirectory']} readDirectory
726
+ * @property {boolean | (() => boolean)} useCaseSensitiveFileNames
727
+ * @property {import('typescript').System['getModifiedTime']} [getModifiedTime]
728
+ */
729
+ /**
730
+ * @typedef {object} TsconfigLayer
731
+ * @property {string} path
732
+ * @property {string} dir
733
+ * @property {Record<string, unknown>} config
734
+ * @property {string | undefined} raw_source
735
+ * @property {import('typescript').Diagnostic[]} parse_diagnostics
736
+ */
737
+ /** @typedef {TsconfigLayer & { extends_values: unknown[] }} ParsedTsconfigLayer */
738
+ /**
739
+ * @typedef {object} TsconfigExtendsFailure
740
+ * @property {string} config_path
741
+ * @property {unknown} extends_value
742
+ * @property {string | undefined} resolved_path
743
+ * @property {import('typescript').Diagnostic[]} diagnostics
744
+ */
745
+ /**
746
+ * @typedef {object} ResolvedTsconfigLayers
747
+ * @property {TsconfigLayer[]} layers
748
+ * @property {string[]} dependencies
749
+ * @property {import('typescript').Diagnostic[]} diagnostics
750
+ * @property {TsconfigExtendsFailure[]} extends_failures
751
+ */
752
+ /**
753
+ * @template TValue
754
+ * @typedef {{state: 'absent'} | {state: 'found', value: TValue}} ConfigValueResult
755
+ */
756
+ /** @typedef {{config_path: string, config_dir: string}} TsconfigValueOrigin */
757
+ const no_inputs_found_diagnostic_code = 18003;
758
+ const circularity_diagnostic_code = 18e3;
759
+ /**
760
+ * Load a tsconfig and its explicit inheritance graph from lowest to highest
761
+ * precedence. Parsed files are cached, but traversal intentionally does not
762
+ * deduplicate layers because a shared base must be reapplied in every branch.
763
+ *
764
+ * @param {typeof import('typescript')} ts
765
+ * @param {TsconfigHost} host
766
+ * @param {string} config_file_name
767
+ * @returns {ResolvedTsconfigLayers}
768
+ */
769
+ function load_tsconfig_layers(ts, host, config_file_name) {
770
+ /** @type {Map<string, ParsedTsconfigLayer>} */
771
+ const parsed_file_cache = /* @__PURE__ */ new Map();
772
+ /** @type {TsconfigLayer[]} */
773
+ const layers = [];
774
+ /** @type {string[]} */
775
+ const dependencies = [];
776
+ const dependency_keys = /* @__PURE__ */ new Set();
777
+ /** @type {import('typescript').Diagnostic[]} */
778
+ const diagnostics = [];
779
+ /** @type {TsconfigExtendsFailure[]} */
780
+ const extends_failures = [];
781
+ const diagnostic_keys = /* @__PURE__ */ new Set();
782
+ const active_stack = /* @__PURE__ */ new Set();
783
+ const use_case_sensitive_file_names = typeof host.useCaseSensitiveFileNames === "function" ? host.useCaseSensitiveFileNames() : host.useCaseSensitiveFileNames;
784
+ /** @type {import('typescript').ParseConfigHost} */
785
+ const parse_host = {
786
+ fileExists: host.fileExists,
787
+ readFile: host.readFile,
788
+ readDirectory: host.readDirectory,
789
+ useCaseSensitiveFileNames: use_case_sensitive_file_names
790
+ };
791
+ /** @param {string} file_name */
792
+ function normalize_path(file_name) {
793
+ return node_path.default.normalize(node_path.default.resolve(file_name));
794
+ }
795
+ /** @param {string} file_name */
796
+ function get_path_key(file_name) {
797
+ const normalized_path = normalize_path(file_name);
798
+ return use_case_sensitive_file_names ? normalized_path : normalized_path.toLowerCase();
799
+ }
800
+ /** @param {import('typescript').Diagnostic[]} next_diagnostics */
801
+ function add_diagnostics(next_diagnostics) {
802
+ for (const diagnostic of next_diagnostics) {
803
+ const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
804
+ const key = `${diagnostic.code}\0${diagnostic.file?.fileName ?? ""}\0${diagnostic.start ?? ""}\0${message}`;
805
+ if (!diagnostic_keys.has(key)) {
806
+ diagnostic_keys.add(key);
807
+ diagnostics.push(diagnostic);
808
+ }
809
+ }
810
+ }
811
+ /** @param {string} file_name */
812
+ function add_dependency(file_name) {
813
+ const normalized_path = normalize_path(file_name);
814
+ const key = get_path_key(normalized_path);
815
+ if (!dependency_keys.has(key)) {
816
+ dependency_keys.add(key);
817
+ dependencies.push(normalized_path);
818
+ }
819
+ }
820
+ /**
821
+ * TypeScript does not expose a failed lookup path for extensionless relative
822
+ * extends entries. Preserve the conventional JSON candidate so creating it
823
+ * later invalidates both the mtime cache and the language-server project.
824
+ * @param {ParsedTsconfigLayer} parsed
825
+ * @param {unknown} extends_value
826
+ */
827
+ function get_unresolved_relative_dependency(parsed, extends_value) {
828
+ if (typeof extends_value !== "string" || !node_path.default.isAbsolute(extends_value) && !extends_value.startsWith(".")) return;
829
+ const candidate_path = node_path.default.resolve(parsed.dir, extends_value);
830
+ return node_path.default.extname(candidate_path) === "" ? `${candidate_path}.json` : candidate_path;
831
+ }
832
+ /** @param {string} file_name */
833
+ function parse_file(file_name) {
834
+ const normalized_path = normalize_path(file_name);
835
+ const key = get_path_key(normalized_path);
836
+ const cached = parsed_file_cache.get(key);
837
+ if (cached) return cached;
838
+ const raw_source = host.readFile(normalized_path);
839
+ const source_file = ts.readJsonConfigFile(normalized_path, host.readFile);
840
+ /** @type {import('typescript').Diagnostic[]} */
841
+ const parse_diagnostics = [...source_file.parseDiagnostics];
842
+ const converted_config = ts.convertToObject(source_file, parse_diagnostics);
843
+ const config = converted_config !== null && typeof converted_config === "object" && !Array.isArray(converted_config) ? converted_config : {};
844
+ const extends_value = config.extends;
845
+ const extends_values = Array.isArray(extends_value) ? extends_value : extends_value !== void 0 ? [extends_value] : [];
846
+ const parsed = {
847
+ path: normalized_path,
848
+ dir: node_path.default.dirname(normalized_path),
849
+ config,
850
+ raw_source,
851
+ parse_diagnostics,
852
+ extends_values
853
+ };
854
+ parsed_file_cache.set(key, parsed);
855
+ add_diagnostics(parse_diagnostics);
856
+ return parsed;
857
+ }
858
+ /**
859
+ * Resolve one immediate extends entry with TypeScript's own config resolver.
860
+ * A synthetic config isolates the edge; inherited files remain available to
861
+ * TypeScript so relative, absolute, package, optional-suffix, and cycle
862
+ * semantics stay aligned with parseJsonSourceFileConfigFileContent.
863
+ *
864
+ * @param {ParsedTsconfigLayer} parsed
865
+ * @param {unknown} extends_value
866
+ */
867
+ function resolve_extends_path(parsed, extends_value) {
868
+ const synthetic_source = JSON.stringify({
869
+ extends: extends_value,
870
+ files: []
871
+ });
872
+ const source_file = ts.readJsonConfigFile(parsed.path, () => synthetic_source);
873
+ const edge_diagnostics = ts.parseJsonSourceFileConfigFileContent(source_file, parse_host, parsed.dir, {}, parsed.path).errors.filter((diagnostic) => diagnostic.code !== no_inputs_found_diagnostic_code);
874
+ add_diagnostics(edge_diagnostics);
875
+ const resolved_path = source_file.extendedSourceFiles?.[0];
876
+ const dependency_path = resolved_path ?? get_unresolved_relative_dependency(parsed, extends_value);
877
+ if (dependency_path !== void 0) add_dependency(dependency_path);
878
+ const has_cycle = edge_diagnostics.some((diagnostic) => diagnostic.code === circularity_diagnostic_code);
879
+ const is_unresolved = resolved_path === void 0 || !host.fileExists(resolved_path);
880
+ if (is_unresolved || has_cycle) extends_failures.push({
881
+ config_path: parsed.path,
882
+ extends_value,
883
+ resolved_path: dependency_path,
884
+ diagnostics: edge_diagnostics
885
+ });
886
+ return is_unresolved ? void 0 : resolved_path;
887
+ }
888
+ /** @param {string} file_name */
889
+ function visit(file_name) {
890
+ const normalized_path = normalize_path(file_name);
891
+ const key = get_path_key(normalized_path);
892
+ if (active_stack.has(key)) return;
893
+ active_stack.add(key);
894
+ add_dependency(normalized_path);
895
+ const parsed = parse_file(normalized_path);
896
+ for (const extends_value of parsed.extends_values) {
897
+ const extended_path = resolve_extends_path(parsed, extends_value);
898
+ if (extended_path !== void 0) visit(extended_path);
899
+ }
900
+ layers.push({
901
+ path: parsed.path,
902
+ dir: parsed.dir,
903
+ config: parsed.config,
904
+ raw_source: parsed.raw_source,
905
+ parse_diagnostics: parsed.parse_diagnostics
906
+ });
907
+ active_stack.delete(key);
908
+ }
909
+ visit(config_file_name);
910
+ return {
911
+ layers,
912
+ dependencies,
913
+ diagnostics,
914
+ extends_failures
915
+ };
916
+ }
917
+ /**
918
+ * Read a nested config value only when every segment is an own property.
919
+ *
920
+ * @param {unknown} config
921
+ * @param {readonly PropertyKey[]} path_parts
922
+ * @returns {ConfigValueResult<unknown>}
923
+ */
924
+ function get_own_config_value(config, path_parts) {
925
+ let current = config;
926
+ for (const path_part of path_parts) {
927
+ if (current === null || typeof current !== "object" && typeof current !== "function" || !Object.prototype.hasOwnProperty.call(current, path_part)) return { state: "absent" };
928
+ current = current[path_part];
929
+ }
930
+ return {
931
+ state: "found",
932
+ value: current
933
+ };
934
+ }
935
+ /**
936
+ * Resolve a caller-defined value across layers. The last result whose state is
937
+ * not absent wins and is annotated with the config that declared it.
938
+ *
939
+ * @template {{state: string}} TResult
940
+ * @param {readonly TsconfigLayer[]} layers
941
+ * @param {(layer: TsconfigLayer) => TResult} read_layer
942
+ * @returns {{state: 'absent'} | (TResult & TsconfigValueOrigin)}
943
+ */
944
+ function resolve_inherited_config_value(layers, read_layer) {
945
+ /** @type {{state: 'absent'} | (TResult & TsconfigValueOrigin)} */
946
+ let resolved = { state: "absent" };
947
+ for (const layer of layers) {
948
+ const value = read_layer(layer);
949
+ if (value.state !== "absent") resolved = {
950
+ ...value,
951
+ config_path: layer.path,
952
+ config_dir: layer.dir
953
+ };
954
+ }
955
+ return resolved;
956
+ }
957
+
958
+ //#endregion
959
+ //#region ../typescript-plugin/src/consumer-compiler.js
960
+ /** @typedef {{actual_type: string, actual_value: string}} InvalidConfigValueDetails */
961
+ /**
962
+ * @typedef {
963
+ * | {state: 'absent'}
964
+ * | {state: 'declared', value: string}
965
+ * | ({state: 'invalid', target: 'tsrx' | 'compiler'} & InvalidConfigValueDetails)
966
+ * } CompilerDeclaration
967
+ */
968
+ /**
969
+ * @typedef {object} CompilerResolutionOptions
970
+ * @property {typeof import('typescript')} [ts]
971
+ * @property {string} [configFileName]
972
+ * @property {import('./tsconfig-resolution.js').TsconfigHost} [configHost]
973
+ * @property {Set<string>} [dependencies]
974
+ */
975
+ /**
976
+ * @typedef {object} ConfigHostResolutionCache
977
+ * @property {Map<string, string | null>} nearest_config_paths
978
+ * @property {Map<string, CachedTsconfigLayers>} resolved_config_layers
979
+ */
980
+ /**
981
+ * @typedef {object} CachedTsconfigLayers
982
+ * @property {import('./tsconfig-resolution.js').ResolvedTsconfigLayers} value
983
+ * @property {Map<string, number | undefined> | undefined} dependency_modified_times
984
+ */
985
+ const { log: log$10, logError: logError$5, logWarning: logWarning$1 } = createLogging("[Ripple Language]");
986
+ const bare_package_specifier_pattern = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*(?:\/[A-Za-z0-9][A-Za-z0-9._~-]*)*$/;
987
+ const tsrx_key_pattern = /["']tsrx["']\s*:/;
988
+ /** @type {WeakMap<object, ConfigHostResolutionCache>} */
989
+ let config_host_to_resolution_cache = /* @__PURE__ */ new WeakMap();
990
+ /** @type {Map<string, string | null>} */
991
+ const declared_compiler_path_map = /* @__PURE__ */ new Map();
992
+ /**
993
+ * @param {unknown} value
994
+ * @returns {{ actual_type: string, actual_value: string }}
995
+ */
996
+ function describe_config_value(value) {
997
+ return {
998
+ actual_type: value === null ? "null" : Array.isArray(value) ? "array" : typeof value,
999
+ actual_value: JSON.stringify(value) ?? String(value)
1000
+ };
1001
+ }
1002
+ /**
1003
+ * Keep filesystem-derived config caches isolated per TypeScript/Volar host.
1004
+ * Different hosts can expose different snapshots for the same absolute path.
1005
+ * @param {import('./tsconfig-resolution.js').TsconfigHost} host
1006
+ */
1007
+ function get_config_host_cache(host) {
1008
+ let cache = config_host_to_resolution_cache.get(host);
1009
+ if (!cache) {
1010
+ cache = {
1011
+ nearest_config_paths: /* @__PURE__ */ new Map(),
1012
+ resolved_config_layers: /* @__PURE__ */ new Map()
1013
+ };
1014
+ config_host_to_resolution_cache.set(host, cache);
1015
+ }
1016
+ return cache;
1017
+ }
1018
+ /**
1019
+ * @param {string} file_name
1020
+ * @param {import('./tsconfig-resolution.js').TsconfigHost} host
1021
+ */
1022
+ function get_config_cache_key(file_name, host) {
1023
+ const normalized_path = path.default.normalize(path.default.resolve(file_name));
1024
+ return (typeof host.useCaseSensitiveFileNames === "function" ? host.useCaseSensitiveFileNames() : host.useCaseSensitiveFileNames) ? normalized_path : normalized_path.toLowerCase();
1025
+ }
1026
+ /**
1027
+ * @param {unknown} config
1028
+ * @returns {CompilerDeclaration}
1029
+ */
1030
+ function get_compiler_declaration(config) {
1031
+ const tsrx_result = get_own_config_value(config, ["tsrx"]);
1032
+ if (tsrx_result.state === "absent") return { state: "absent" };
1033
+ const tsrx_value = tsrx_result.value;
1034
+ if (tsrx_value === null || typeof tsrx_value !== "object" || Array.isArray(tsrx_value)) return {
1035
+ state: "invalid",
1036
+ target: "tsrx",
1037
+ ...describe_config_value(tsrx_value)
1038
+ };
1039
+ const compiler_result = get_own_config_value(tsrx_value, ["compiler"]);
1040
+ if (compiler_result.state === "absent") return { state: "absent" };
1041
+ const compiler = compiler_result.value;
1042
+ if (typeof compiler === "string" && compiler.trim() !== "") return {
1043
+ state: "declared",
1044
+ value: compiler.trim()
1045
+ };
1046
+ return {
1047
+ state: "invalid",
1048
+ target: "compiler",
1049
+ ...describe_config_value(compiler)
1050
+ };
1051
+ }
1052
+ /**
1053
+ * Find the nearest tsconfig.json to use as the root of inheritance resolution.
1054
+ * @param {string} start_dir
1055
+ * @param {import('./tsconfig-resolution.js').TsconfigHost} host
1056
+ * @param {ConfigHostResolutionCache} cache
1057
+ * @returns {string | null}
1058
+ */
1059
+ function get_nearest_root_tsconfig(start_dir, host, cache) {
1060
+ let current_dir = start_dir;
1061
+ /** @type {string[]} */
1062
+ const visited_dir_keys = [];
1063
+ while (current_dir) {
1064
+ const current_dir_key = get_config_cache_key(current_dir, host);
1065
+ if (cache.nearest_config_paths.has(current_dir_key)) {
1066
+ const cached_tsconfig = cache.nearest_config_paths.get(current_dir_key) ?? null;
1067
+ for (const visited_dir_key of visited_dir_keys) cache.nearest_config_paths.set(visited_dir_key, cached_tsconfig);
1068
+ return cached_tsconfig;
1069
+ }
1070
+ visited_dir_keys.push(current_dir_key);
1071
+ const tsconfig_path = path.default.join(current_dir, "tsconfig.json");
1072
+ if (host.fileExists(tsconfig_path)) {
1073
+ for (const visited_dir_key of visited_dir_keys) cache.nearest_config_paths.set(visited_dir_key, tsconfig_path);
1074
+ return tsconfig_path;
1075
+ }
1076
+ const parent_dir = path.default.dirname(current_dir);
1077
+ if (parent_dir === current_dir) break;
1078
+ current_dir = parent_dir;
1079
+ }
1080
+ for (const visited_dir_key of visited_dir_keys) cache.nearest_config_paths.set(visited_dir_key, null);
1081
+ return null;
1082
+ }
1083
+ /**
1084
+ * @param {typeof import('typescript')} typescript
1085
+ * @param {import('./tsconfig-resolution.js').TsconfigHost} host
1086
+ * @param {string} root_config_path
1087
+ * @param {ConfigHostResolutionCache} cache
1088
+ */
1089
+ function get_tsconfig_layers(typescript$2, host, root_config_path, cache) {
1090
+ const cache_key = get_config_cache_key(root_config_path, host);
1091
+ const cached = cache.resolved_config_layers.get(cache_key);
1092
+ if (cached && (cached.dependency_modified_times === void 0 || [...cached.dependency_modified_times].every(([dependency, modified_time]) => host.getModifiedTime?.(dependency)?.valueOf() === modified_time))) return cached.value;
1093
+ const value = load_tsconfig_layers(typescript$2, host, root_config_path);
1094
+ const dependency_modified_times = host.getModifiedTime ? new Map(value.dependencies.map((dependency) => [dependency, host.getModifiedTime?.(dependency)?.valueOf()])) : void 0;
1095
+ cache.resolved_config_layers.set(cache_key, {
1096
+ value,
1097
+ dependency_modified_times
1098
+ });
1099
+ return value;
1100
+ }
1101
+ /**
1102
+ * A declaration in the config that owns a failed extends entry, or in a child
1103
+ * config above it, has higher precedence than anything the missing base could
1104
+ * have declared and therefore makes that failure irrelevant to this value.
1105
+ * @param {readonly import('./tsconfig-resolution.js').TsconfigLayer[]} layers
1106
+ * @param {import('./tsconfig-resolution.js').TsconfigExtendsFailure} failure
1107
+ * @param {string} declaration_config_path
1108
+ */
1109
+ function is_extends_failure_overridden(layers, failure, declaration_config_path) {
1110
+ const failure_owner_index = layers.findLastIndex((layer) => layer.path === failure.config_path);
1111
+ const declaration_index = layers.findLastIndex((layer) => layer.path === declaration_config_path);
1112
+ return failure_owner_index >= 0 && declaration_index >= failure_owner_index;
1113
+ }
1114
+ /**
1115
+ * Resolve without a module-resolution cache. This is the recovery path when
1116
+ * Node retains a negative package lookup after a dependency is installed.
1117
+ * `noDtsResolution` ensures the runtime entry wins over a package's types.
1118
+ * @param {typeof import('typescript')} typescript
1119
+ * @param {import('./tsconfig-resolution.js').TsconfigHost} host
1120
+ * @param {string} config_path
1121
+ * @param {string} specifier
1122
+ */
1123
+ function resolve_uncached_declared_compiler(typescript$3, host, config_path, specifier) {
1124
+ return typescript$3.resolveModuleName(specifier, config_path, {
1125
+ module: typescript$3.ModuleKind.Node16,
1126
+ moduleResolution: typescript$3.ModuleResolutionKind.Node16,
1127
+ noDtsResolution: true
1128
+ }, host, void 0, void 0, typescript$3.ModuleKind.CommonJS).resolvedModule?.resolvedFileName;
1129
+ }
1130
+ /**
1131
+ * Resolve the compiler explicitly selected by a consumer tsconfig. Invalid
1132
+ * specifiers are stable and cached, while missing packages are retried so
1133
+ * tsserver can recover after the dependency is installed.
1134
+ * @param {typeof import('typescript')} typescript
1135
+ * @param {import('./tsconfig-resolution.js').TsconfigHost} host
1136
+ * @param {string} config_path
1137
+ * @param {string} specifier
1138
+ * @returns {string | null}
1139
+ */
1140
+ function resolve_declared_compiler(typescript$4, host, config_path, specifier) {
1141
+ const cache_key = `${config_path}\0${specifier}`;
1142
+ if (declared_compiler_path_map.has(cache_key)) return declared_compiler_path_map.get(cache_key) ?? null;
1143
+ if (!bare_package_specifier_pattern.test(specifier)) {
1144
+ declared_compiler_path_map.set(cache_key, null);
1145
+ logError$5("Declared TSRX compiler must be a bare package specifier:", specifier, `in ${config_path}`);
1146
+ return null;
1147
+ }
1148
+ try {
1149
+ const compiler_path = (0, module$1.createRequire)(config_path).resolve(specifier);
1150
+ declared_compiler_path_map.set(cache_key, compiler_path);
1151
+ log$10("Found declared tsrx compiler at:", compiler_path, "from tsconfig:", config_path);
1152
+ return compiler_path;
1153
+ } catch {
1154
+ const compiler_path = resolve_uncached_declared_compiler(typescript$4, host, config_path, specifier);
1155
+ if (compiler_path !== void 0) {
1156
+ declared_compiler_path_map.set(cache_key, compiler_path);
1157
+ log$10("Found declared tsrx compiler after uncached retry at:", compiler_path, "from tsconfig:", config_path);
1158
+ return compiler_path;
1159
+ }
1160
+ logError$5(`Unable to resolve declared TSRX compiler "${specifier}" from tsconfig`, config_path);
1161
+ return null;
1162
+ }
1163
+ }
1164
+ /**
1165
+ * Return undefined when there is no consumer declaration, null for a declared
1166
+ * hard stop, or the resolved compiler path for a valid declaration.
1167
+ * @param {string} normalized_file_name
1168
+ * @param {CompilerResolutionOptions} [options]
1169
+ * @returns {string | null | undefined}
1170
+ */
1171
+ function resolve_consumer_compiler_for_file(normalized_file_name, options = {}) {
1172
+ const typescript$5 = options.ts ?? typescript.default;
1173
+ const config_host = options.configHost ?? typescript$5.sys;
1174
+ const host_cache = get_config_host_cache(config_host);
1175
+ const root_config_path = options.configFileName ?? get_nearest_root_tsconfig(path.default.dirname(normalized_file_name), config_host, host_cache);
1176
+ if (root_config_path === null) return;
1177
+ const resolved_layers = get_tsconfig_layers(typescript$5, config_host, root_config_path, host_cache);
1178
+ for (const dependency of resolved_layers.dependencies) options.dependencies?.add(dependency);
1179
+ const unreadable_layers = resolved_layers.layers.filter((layer) => layer.raw_source === void 0);
1180
+ if (unreadable_layers.length > 0) {
1181
+ for (const layer of unreadable_layers) logError$5("Unable to read tsconfig layer:", layer.path);
1182
+ return null;
1183
+ }
1184
+ const malformed_layers = resolved_layers.layers.filter((layer) => layer.parse_diagnostics.length > 0);
1185
+ if (malformed_layers.length > 0) {
1186
+ const has_tsrx_intent = resolved_layers.layers.some((layer) => {
1187
+ if (get_own_config_value(layer.config, ["tsrx"]).state === "found") return true;
1188
+ if (layer.parse_diagnostics.length === 0 || layer.raw_source === void 0) return false;
1189
+ return tsrx_key_pattern.test(layer.raw_source);
1190
+ });
1191
+ const log_parse_error = has_tsrx_intent ? logError$5 : logWarning$1;
1192
+ for (const layer of malformed_layers) for (const diagnostic of layer.parse_diagnostics) log_parse_error("Unable to parse tsconfig layer:", layer.path, typescript$5.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
1193
+ return has_tsrx_intent ? null : void 0;
1194
+ }
1195
+ const declaration = resolve_inherited_config_value(resolved_layers.layers, (layer) => get_compiler_declaration(layer.config));
1196
+ if (declaration.state === "invalid") {
1197
+ logError$5(`Invalid TSRX ${declaration.target} declaration:`, declaration.actual_type, declaration.actual_value, `in ${declaration.config_path}`);
1198
+ return null;
1199
+ }
1200
+ const effective_extends_failures = resolved_layers.extends_failures.filter((failure) => declaration.state !== "declared" || !is_extends_failure_overridden(resolved_layers.layers, failure, declaration.config_path));
1201
+ if (effective_extends_failures.length > 0) {
1202
+ for (const failure of effective_extends_failures) {
1203
+ if (failure.diagnostics.length === 0) {
1204
+ logError$5("Unable to resolve tsconfig extends entry:", JSON.stringify(failure.extends_value), `in ${failure.config_path}`);
1205
+ continue;
1206
+ }
1207
+ for (const diagnostic of failure.diagnostics) logError$5("Unable to resolve tsconfig extends entry:", JSON.stringify(failure.extends_value), `in ${failure.config_path}`, typescript$5.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
1208
+ }
1209
+ return null;
1210
+ }
1211
+ if (declaration.state === "declared") return resolve_declared_compiler(typescript$5, config_host, declaration.config_path, declaration.value);
1212
+ }
1213
+ /** Drop all filesystem-derived consumer compiler resolution state. */
1214
+ function reset_consumer_compiler_resolution_caches() {
1215
+ config_host_to_resolution_cache = /* @__PURE__ */ new WeakMap();
1216
+ declared_compiler_path_map.clear();
1217
+ }
1218
+
716
1219
  //#endregion
717
1220
  //#region ../typescript-plugin/src/language.js
718
1221
  /** @import * as AST from 'estree'; */
@@ -725,9 +1228,9 @@ var require_language_core = /* @__PURE__ */ __commonJSMin(((exports) => {
725
1228
  /** @typedef {import('@volar/language-core').VirtualCode} VirtualCode */
726
1229
  /** @typedef {string | { fsPath: string }} ScriptId */
727
1230
  /** @typedef {typeof import('@volar/typescript')} _VolarTypeScriptAugmentation */
1231
+ /** @typedef {import('./consumer-compiler.js').CompilerResolutionOptions} CompilerResolutionOptions */
728
1232
  /** @typedef {import('@volar/language-core').LanguagePlugin<ScriptId, VirtualCode>} RippleLanguagePlugin */
729
1233
  /** @typedef {InstanceType<typeof import('./language.js')["TSRXVirtualCode"]>} TSRXVirtualCodeInstance */
730
- var import_language_core = require_language_core();
731
1234
  const require$2 = (0, module$1.createRequire)(require("url").pathToFileURL(__filename).href);
732
1235
  const root_dirname = path.default.dirname((0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
733
1236
  const { log: log$9, logWarning, logError: logError$4 } = createLogging("[Ripple Language]");
@@ -812,10 +1315,18 @@ function is_ripple_file(file_name) {
812
1315
  return RIPPLE_EXTENSIONS$1.some((extension) => file_name.endsWith(extension));
813
1316
  }
814
1317
  /**
1318
+ * @param {CompilerResolutionOptions} [options]
815
1319
  * @returns {RippleLanguagePlugin}
816
1320
  */
817
- function getRippleLanguagePlugin() {
1321
+ function getRippleLanguagePlugin(options = {}) {
818
1322
  log$9("Creating Ripple language plugin...");
1323
+ const typescript$1 = options.ts ?? typescript.default;
1324
+ /** @type {CompilerResolutionOptions} */
1325
+ const compiler_resolution_options = {
1326
+ ...options,
1327
+ ts: typescript$1,
1328
+ configHost: options.configHost ?? typescript$1.sys
1329
+ };
819
1330
  return {
820
1331
  getLanguageId(fileNameOrUri) {
821
1332
  const file_name = typeof fileNameOrUri === "string" ? fileNameOrUri : fileNameOrUri.fsPath.replace(/\\/g, "/");
@@ -827,7 +1338,7 @@ function getRippleLanguagePlugin() {
827
1338
  createVirtualCode(fileNameOrUri, languageId, snapshot) {
828
1339
  if (languageId === "ripple") {
829
1340
  const file_name = normalizeFileNameOrUri(fileNameOrUri);
830
- const ripple = get_tsrx_compiler(file_name);
1341
+ const ripple = get_tsrx_compiler(file_name, compiler_resolution_options);
831
1342
  if (!ripple) {
832
1343
  logError$4(`Ripple compiler not found for file: ${file_name}`);
833
1344
  return;
@@ -1370,9 +1881,9 @@ const resolveConfig = (config) => {
1370
1881
  };
1371
1882
  /** @type {Map<string, string | null>} */
1372
1883
  const path2RipplePathMap = /* @__PURE__ */ new Map();
1373
- /** @type {Map<string, string>} */
1884
+ /** @type {Map<string, { mtimeMs: number, size: number, content: string }>} */
1374
1885
  const pathToTypesCache = /* @__PURE__ */ new Map();
1375
- /** @type {Map<string, RegExpMatchArray>} */
1886
+ /** @type {Map<string, { text: string, matches: Map<string, RegExpMatchArray> }>} */
1376
1887
  const typeNameMatchCache = /* @__PURE__ */ new Map();
1377
1888
  /** @type {Map<string, { name: string | null, dependencies: Set<string> } | null>} */
1378
1889
  const pathToPackageManifestCache = /* @__PURE__ */ new Map();
@@ -1440,18 +1951,24 @@ function package_manifest_matches_compiler(package_manifest, compiler_name, pack
1440
1951
  }
1441
1952
  /**
1442
1953
  * @param {string} normalized_file_name
1954
+ * @param {CompilerResolutionOptions} [options]
1443
1955
  * @returns {TSRXCompilerModule | undefined}
1444
1956
  */
1445
- function get_tsrx_compiler(normalized_file_name) {
1446
- const compiler_path = get_compiler_entry_for_file(normalized_file_name);
1447
- if (compiler_path) {
1448
- const compiler_module = require$2(compiler_path);
1449
- if (typeof compiler_module?.compile_to_volar_mappings !== "function" && typeof compiler_module?.compileToVolarMappings === "function") return {
1450
- ...compiler_module,
1451
- compile_to_volar_mappings: compiler_module.compileToVolarMappings
1452
- };
1453
- return compiler_module;
1454
- }
1957
+ function get_tsrx_compiler(normalized_file_name, options) {
1958
+ const compiler_path = get_compiler_entry_for_file(normalized_file_name, options);
1959
+ if (compiler_path) return normalize_tsrx_compiler_module(require$2(compiler_path));
1960
+ }
1961
+ /**
1962
+ * Normalize the compiler contract used by Octane and consumer-declared modules.
1963
+ * @param {TSRXCompilerModule & { compileToVolarMappings?: TSRXCompilerModule['compile_to_volar_mappings'] }} compiler_module
1964
+ * @returns {TSRXCompilerModule}
1965
+ */
1966
+ function normalize_tsrx_compiler_module(compiler_module) {
1967
+ if (typeof compiler_module?.compile_to_volar_mappings !== "function" && typeof compiler_module?.compileToVolarMappings === "function") return {
1968
+ ...compiler_module,
1969
+ compile_to_volar_mappings: compiler_module.compileToVolarMappings
1970
+ };
1971
+ return compiler_module;
1455
1972
  }
1456
1973
  /**
1457
1974
  * @param {string} normalized_file_name
@@ -1495,10 +2012,13 @@ function find_workspace_compiler_entry_for_file(normalized_file_name, exists_syn
1495
2012
  }
1496
2013
  /**
1497
2014
  * @param {string} normalized_file_name
2015
+ * @param {CompilerResolutionOptions} [options]
1498
2016
  * @returns {string | undefined}
1499
2017
  */
1500
- function get_compiler_entry_for_file(normalized_file_name) {
2018
+ function get_compiler_entry_for_file(normalized_file_name, options) {
1501
2019
  const ext = path.default.extname(normalized_file_name);
2020
+ const consumer_compiler_path = resolve_consumer_compiler_for_file(normalized_file_name, options);
2021
+ if (consumer_compiler_path !== void 0) return consumer_compiler_path ?? void 0;
1502
2022
  const package_manifest = get_nearest_package_manifest(path.default.dirname(normalized_file_name));
1503
2023
  const workspace_compiler_path = find_workspace_compiler_entry_for_file(normalized_file_name);
1504
2024
  if (workspace_compiler_path) return workspace_compiler_path;
@@ -1529,16 +2049,18 @@ function get_compiler_entry_for_file(normalized_file_name) {
1529
2049
  }
1530
2050
  /**
1531
2051
  * Resolve the tsrx compiler package that owns a `.tsrx` file (e.g. `'@tsrx/ripple'`,
1532
- * `'@tsrx/react'`), based on the nearest `package.json` dependencies. All targets
1533
- * share the `.tsrx` extension, so this is how the editor layer tells them apart —
1534
- * it reuses the exact same resolution as compilation, so tooling and the compiler
1535
- * always agree on the platform. Returns `undefined` when no compiler resolves.
2052
+ * `'@tsrx/react'`) from the exact compiler entry selected for compilation. All
2053
+ * targets share the `.tsrx` extension, so this is how the editor layer tells them
2054
+ * apart. Returns `undefined` for an unresolved or unknown third-party compiler.
1536
2055
  * @param {string} file_name
2056
+ * @param {CompilerResolutionOptions} [options]
1537
2057
  * @returns {string | undefined}
1538
2058
  */
1539
- function get_tsrx_compiler_name_for_file(file_name) {
1540
- const entry = get_compiler_entry_for_file(file_name.replace(/\\/g, "/"));
2059
+ function get_tsrx_compiler_name_for_file(file_name, options) {
2060
+ const entry = get_compiler_entry_for_file(file_name.replace(/\\/g, "/"), options);
1541
2061
  if (!entry) return;
2062
+ const package_name = get_nearest_package_manifest(path.default.dirname(entry))?.name;
2063
+ if (COMPILER_CANDIDATES.some(([compiler_name]) => compiler_name === package_name)) return package_name ?? void 0;
1542
2064
  const normalized_entry = entry.replace(/\\/g, "/");
1543
2065
  for (const [compiler_name, compiler_dir_parts, , , entry_parts = DEFAULT_COMPILER_ENTRY_PARTS] of COMPILER_CANDIDATES) {
1544
2066
  const suffix = "/" + [...compiler_dir_parts, ...entry_parts].join("/");
@@ -1549,53 +2071,116 @@ function get_tsrx_compiler_name_for_file(file_name) {
1549
2071
  * Whether a `.tsrx` file is compiled by the Ripple target (as opposed to
1550
2072
  * React/Solid/Preact/Vue). Used to gate Ripple-runtime-only editor suggestions.
1551
2073
  * @param {string} file_name
2074
+ * @param {CompilerResolutionOptions} [options]
1552
2075
  * @returns {boolean}
1553
2076
  */
1554
- function is_ripple_platform_file(file_name) {
1555
- return get_tsrx_compiler_name_for_file(file_name) === "@tsrx/ripple";
2077
+ function is_ripple_platform_file(file_name, options) {
2078
+ return get_tsrx_compiler_name_for_file(file_name, options) === "@tsrx/ripple";
2079
+ }
2080
+ /**
2081
+ * Create a stable key for filesystem caches. Windows paths are case-insensitive
2082
+ * to TypeScript, while file URIs conventionally lowercase their drive letter.
2083
+ * @param {string} file_name
2084
+ */
2085
+ function getPathCacheKey(file_name) {
2086
+ const is_windows_path = process.platform === "win32" || /^[a-z]:[\\/]/i.test(file_name) || file_name.startsWith("\\\\");
2087
+ const normalized = (is_windows_path ? path.default.win32 : path.default).normalize(file_name);
2088
+ return is_windows_path ? normalized.toLowerCase() : normalized;
1556
2089
  }
1557
2090
  /**
1558
2091
  * @param {string} typesFilePath
1559
2092
  * @returns {string | undefined}
1560
2093
  */
1561
2094
  function getCachedTypeDefinitionFile(typesFilePath) {
1562
- const cached = pathToTypesCache.get(typesFilePath);
1563
- if (cached) return cached;
1564
- if (!fs.default.existsSync(typesFilePath)) {
2095
+ const cache_key = getPathCacheKey(typesFilePath);
2096
+ let stat;
2097
+ try {
2098
+ stat = fs.default.statSync(typesFilePath);
2099
+ } catch {
1565
2100
  logWarning(`Types file does not exist at path: ${typesFilePath}`);
1566
2101
  return;
1567
2102
  }
2103
+ const cached = pathToTypesCache.get(cache_key);
2104
+ if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) return cached.content;
1568
2105
  log$9(`Found ripple types at: ${typesFilePath}`);
1569
2106
  const fileContent = fs.default.readFileSync(typesFilePath, "utf8");
1570
2107
  if (!fileContent) {
1571
2108
  logWarning(`Failed to read content of types file at: ${typesFilePath}`);
1572
2109
  return;
1573
2110
  }
1574
- pathToTypesCache.set(typesFilePath, fileContent);
2111
+ pathToTypesCache.set(cache_key, {
2112
+ mtimeMs: stat.mtimeMs,
2113
+ size: stat.size,
2114
+ content: fileContent
2115
+ });
1575
2116
  return fileContent;
1576
2117
  }
1577
2118
  /**
1578
2119
  * @param {string} typeName
1579
2120
  * @param {string} text
2121
+ * @param {string} [sourceKey]
1580
2122
  * @returns {RegExpMatchArray | undefined}
1581
2123
  */
1582
- function getCachedTypeMatches(typeName, text) {
1583
- const cached = typeNameMatchCache.get(typeName);
2124
+ function getCachedTypeMatches(typeName, text, sourceKey = text) {
2125
+ const cache_key = sourceKey === text ? text : getPathCacheKey(sourceKey);
2126
+ let source_cache = typeNameMatchCache.get(cache_key);
2127
+ if (!source_cache || source_cache.text !== text) {
2128
+ source_cache = {
2129
+ text,
2130
+ matches: /* @__PURE__ */ new Map()
2131
+ };
2132
+ typeNameMatchCache.set(cache_key, source_cache);
2133
+ }
2134
+ const cached = source_cache.matches.get(typeName);
1584
2135
  if (cached) return cached;
1585
2136
  const searchPattern = new RegExp(`(?:export\\s+(?:declare\\s+)?|declare\\s+)(class|function)\\s+${typeName}`);
1586
2137
  const match = text.match(searchPattern);
1587
2138
  if (match && match.index !== void 0) {
1588
- typeNameMatchCache.set(typeName, match);
2139
+ source_cache.matches.set(typeName, match);
1589
2140
  return match;
1590
2141
  }
1591
2142
  }
1592
2143
  /**
1593
2144
  * @param {string} normalized_file_name
2145
+ * @param {CompilerResolutionOptions} [options]
1594
2146
  * @returns {string | undefined}
1595
2147
  */
1596
- function get_compiler_dir_for_file(normalized_file_name) {
1597
- const entry = get_compiler_entry_for_file(normalized_file_name);
1598
- if (entry) return path.default.dirname(path.default.dirname(entry));
2148
+ function get_compiler_dir_for_file(normalized_file_name, options) {
2149
+ const entry = get_compiler_entry_for_file(normalized_file_name, options);
2150
+ if (!entry) return;
2151
+ let current_dir = path.default.dirname(entry);
2152
+ while (current_dir) {
2153
+ if (fs.default.existsSync(path.default.join(current_dir, "package.json"))) return current_dir;
2154
+ if (path.default.basename(current_dir) === "node_modules") return;
2155
+ const parent_dir = path.default.dirname(current_dir);
2156
+ if (parent_dir === current_dir) return;
2157
+ current_dir = parent_dir;
2158
+ }
2159
+ }
2160
+ /**
2161
+ * Drop compiler-selection state. Loaded ESM compiler graphs cannot be evicted
2162
+ * safely in-process; language-server package watchers restart the process.
2163
+ */
2164
+ function invalidateCompilerResolutionCaches() {
2165
+ path2RipplePathMap.clear();
2166
+ pathToPackageManifestCache.clear();
2167
+ reset_consumer_compiler_resolution_caches();
2168
+ loggedCompilationFailures.clear();
2169
+ }
2170
+ /**
2171
+ * Drop cached definition-file content and matches. A path invalidates only one
2172
+ * file; omitting it clears all definition state.
2173
+ * @param {string} [typesFilePath]
2174
+ */
2175
+ function invalidateTypeDefinitionCaches(typesFilePath) {
2176
+ if (typesFilePath) {
2177
+ const cache_key = getPathCacheKey(typesFilePath);
2178
+ pathToTypesCache.delete(cache_key);
2179
+ typeNameMatchCache.delete(cache_key);
2180
+ return;
2181
+ }
2182
+ pathToTypesCache.clear();
2183
+ typeNameMatchCache.clear();
1599
2184
  }
1600
2185
 
1601
2186
  //#endregion
@@ -1715,15 +2300,30 @@ function is_ripple_document(document_uri) {
1715
2300
  return RIPPLE_EXTENSIONS.some((extension) => document_uri.endsWith(extension));
1716
2301
  }
1717
2302
  /**
2303
+ * Reuse the TypeScript project selected by Volar when language-service plugins
2304
+ * perform compiler-dependent lookups outside virtual-code generation.
2305
+ * @param {LanguageServiceContext} context
2306
+ * @returns {import('@tsrx/typescript-plugin/src/consumer-compiler.js').CompilerResolutionOptions | undefined}
2307
+ */
2308
+ function get_compiler_resolution_options(context) {
2309
+ const typescript_project = context.project.typescript;
2310
+ if (!typescript_project) return;
2311
+ return {
2312
+ configFileName: typescript_project.configFileName,
2313
+ configHost: typescript_project.sys
2314
+ };
2315
+ }
2316
+ /**
1718
2317
  * Whether a `.tsrx` document is compiled by the Ripple target (vs React/Solid/
1719
2318
  * Preact/Vue). All targets share the `.tsrx` extension, so this resolves the
1720
- * platform from the nearest `package.json` — used to gate Ripple-runtime-only
2319
+ * platform from the active TypeScript project — used to gate Ripple-runtime-only
1721
2320
  * editor suggestions.
1722
2321
  * @param {string} document_uri
2322
+ * @param {import('@tsrx/typescript-plugin/src/consumer-compiler.js').CompilerResolutionOptions} [options]
1723
2323
  * @returns {boolean}
1724
2324
  */
1725
- function is_ripple_platform_document(document_uri) {
1726
- return is_ripple_platform_file(vscode_uri.URI.parse(document_uri).fsPath);
2325
+ function is_ripple_platform_document(document_uri, options) {
2326
+ return is_ripple_platform_file(vscode_uri.URI.parse(document_uri).fsPath, options);
1727
2327
  }
1728
2328
 
1729
2329
  //#endregion
@@ -4639,7 +5239,7 @@ var require_main$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
4639
5239
  exports.createMessageConnection = exports.createServerSocketTransport = exports.createClientSocketTransport = exports.createServerPipeTransport = exports.createClientPipeTransport = exports.generateRandomPipeName = exports.StreamMessageWriter = exports.StreamMessageReader = exports.SocketMessageWriter = exports.SocketMessageReader = exports.PortMessageWriter = exports.PortMessageReader = exports.IPCMessageWriter = exports.IPCMessageReader = void 0;
4640
5240
  const ril_1 = require_ril();
4641
5241
  ril_1.default.install();
4642
- const path$3 = require("path");
5242
+ const path$4 = require("path");
4643
5243
  const os = require("os");
4644
5244
  const crypto_1 = require("crypto");
4645
5245
  const net_1 = require("net");
@@ -4763,8 +5363,8 @@ var require_main$4 = /* @__PURE__ */ __commonJSMin(((exports) => {
4763
5363
  const randomSuffix = (0, crypto_1.randomBytes)(21).toString("hex");
4764
5364
  if (process.platform === "win32") return `\\\\.\\pipe\\vscode-jsonrpc-${randomSuffix}-sock`;
4765
5365
  let result;
4766
- if (XDG_RUNTIME_DIR) result = path$3.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
4767
- else result = path$3.join(os.tmpdir(), `vscode-${randomSuffix}.sock`);
5366
+ if (XDG_RUNTIME_DIR) result = path$4.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
5367
+ else result = path$4.join(os.tmpdir(), `vscode-${randomSuffix}.sock`);
4768
5368
  const limit = safeIpcPathLengths.get(process.platform);
4769
5369
  if (limit !== void 0 && result.length > limit) (0, ril_1.default)().console.warn(`WARNING: IPC handle "${result}" is longer than ${limit} characters.`);
4770
5370
  return result;
@@ -11181,7 +11781,7 @@ var require_files = /* @__PURE__ */ __commonJSMin(((exports) => {
11181
11781
  Object.defineProperty(exports, "__esModule", { value: true });
11182
11782
  exports.resolveModulePath = exports.FileSystem = exports.resolveGlobalYarnPath = exports.resolveGlobalNodePath = exports.resolve = exports.uriToFilePath = void 0;
11183
11783
  const url$1 = require("url");
11184
- const path$2 = require("path");
11784
+ const path$3 = require("path");
11185
11785
  const fs$2 = require("fs");
11186
11786
  const child_process_1 = require("child_process");
11187
11787
  /**
@@ -11198,7 +11798,7 @@ var require_files = /* @__PURE__ */ __commonJSMin(((exports) => {
11198
11798
  let second = segments[1];
11199
11799
  if (first.length === 0 && second.length > 1 && second[1] === ":") segments.shift();
11200
11800
  }
11201
- return path$2.normalize(segments.join("/"));
11801
+ return path$3.normalize(segments.join("/"));
11202
11802
  }
11203
11803
  exports.uriToFilePath = uriToFilePath;
11204
11804
  function isWindows() {
@@ -11228,7 +11828,7 @@ var require_files = /* @__PURE__ */ __commonJSMin(((exports) => {
11228
11828
  let newEnv = Object.create(null);
11229
11829
  Object.keys(env).forEach((key) => newEnv[key] = env[key]);
11230
11830
  if (nodePath && fs$2.existsSync(nodePath)) {
11231
- if (newEnv[nodePathKey]) newEnv[nodePathKey] = nodePath + path$2.delimiter + newEnv[nodePathKey];
11831
+ if (newEnv[nodePathKey]) newEnv[nodePathKey] = nodePath + path$3.delimiter + newEnv[nodePathKey];
11232
11832
  else newEnv[nodePathKey] = nodePath;
11233
11833
  if (tracer) tracer(`NODE_PATH value is: ${newEnv[nodePathKey]}`);
11234
11834
  }
@@ -11297,8 +11897,8 @@ var require_files = /* @__PURE__ */ __commonJSMin(((exports) => {
11297
11897
  }
11298
11898
  let prefix = stdout.trim();
11299
11899
  if (tracer) tracer(`'npm config get prefix' value is: ${prefix}`);
11300
- if (prefix.length > 0) if (isWindows()) return path$2.join(prefix, "node_modules");
11301
- else return path$2.join(prefix, "lib", "node_modules");
11900
+ if (prefix.length > 0) if (isWindows()) return path$3.join(prefix, "node_modules");
11901
+ else return path$3.join(prefix, "lib", "node_modules");
11302
11902
  return;
11303
11903
  } catch (err) {
11304
11904
  return;
@@ -11333,7 +11933,7 @@ var require_files = /* @__PURE__ */ __commonJSMin(((exports) => {
11333
11933
  let lines = stdout.trim().split(/\r?\n/);
11334
11934
  for (let line of lines) try {
11335
11935
  let yarn = JSON.parse(line);
11336
- if (yarn.type === "log") return path$2.join(yarn.data, "node_modules");
11936
+ if (yarn.type === "log") return path$3.join(yarn.data, "node_modules");
11337
11937
  } catch (e) {}
11338
11938
  return;
11339
11939
  } catch (err) {
@@ -11354,14 +11954,14 @@ var require_files = /* @__PURE__ */ __commonJSMin(((exports) => {
11354
11954
  }
11355
11955
  FileSystem.isCaseSensitive = isCaseSensitive;
11356
11956
  function isParent(parent, child) {
11357
- if (isCaseSensitive()) return path$2.normalize(child).indexOf(path$2.normalize(parent)) === 0;
11358
- else return path$2.normalize(child).toLowerCase().indexOf(path$2.normalize(parent).toLowerCase()) === 0;
11957
+ if (isCaseSensitive()) return path$3.normalize(child).indexOf(path$3.normalize(parent)) === 0;
11958
+ else return path$3.normalize(child).toLowerCase().indexOf(path$3.normalize(parent).toLowerCase()) === 0;
11359
11959
  }
11360
11960
  FileSystem.isParent = isParent;
11361
11961
  })(FileSystem || (exports.FileSystem = FileSystem = {}));
11362
11962
  function resolveModulePath(workspaceRoot, moduleName, nodePath, tracer) {
11363
11963
  if (nodePath) {
11364
- if (!path$2.isAbsolute(nodePath)) nodePath = path$2.join(workspaceRoot, nodePath);
11964
+ if (!path$3.isAbsolute(nodePath)) nodePath = path$3.join(workspaceRoot, nodePath);
11365
11965
  return resolve(moduleName, nodePath, nodePath, tracer).then((value) => {
11366
11966
  if (FileSystem.isParent(nodePath, value)) return value;
11367
11967
  else return Promise.reject(/* @__PURE__ */ new Error(`Failed to load ${moduleName} from node path location.`));
@@ -21019,8 +21619,6 @@ function get_start_offset_from_error(error) {
21019
21619
  //#region src/definitionPlugin.js
21020
21620
  init_main();
21021
21621
  const { log: log$7 } = createLogging("[Ripple Definition Plugin]");
21022
- /** @type {string | undefined} */
21023
- let ripple_dir;
21024
21622
  /**
21025
21623
  * @returns {LanguageServicePlugin}
21026
21624
  */
@@ -21029,6 +21627,7 @@ function createDefinitionPlugin() {
21029
21627
  name: "ripple-definition",
21030
21628
  capabilities: { definitionProvider: true },
21031
21629
  create(context) {
21630
+ const compiler_resolution_options = get_compiler_resolution_options(context);
21032
21631
  return { async provideDefinition(document, position, token) {
21033
21632
  /** @type {LocationLink[]} */
21034
21633
  let tsDefinitions = [];
@@ -21051,7 +21650,7 @@ function createDefinitionPlugin() {
21051
21650
  const { name: typeName, path: typePath } = customMapping.data.customData.definition.typeReplace;
21052
21651
  log$7(`Found replace definition for ${typeName}`);
21053
21652
  const filePath = sourceUri.fsPath || sourceUri.path;
21054
- ripple_dir = ripple_dir ?? get_compiler_dir_for_file(normalizeFileNameOrUri(filePath));
21653
+ const ripple_dir = get_compiler_dir_for_file(normalizeFileNameOrUri(filePath), compiler_resolution_options);
21055
21654
  if (!ripple_dir) {
21056
21655
  log$7(`Could not determine Ripple source directory for file: ${filePath}`);
21057
21656
  return;
@@ -21059,7 +21658,7 @@ function createDefinitionPlugin() {
21059
21658
  const typesFilePath = path.default.join(ripple_dir, ...typePath.split("/"));
21060
21659
  const fileContent = getCachedTypeDefinitionFile(typesFilePath);
21061
21660
  if (!fileContent) return;
21062
- const match = getCachedTypeMatches(typeName, fileContent);
21661
+ const match = getCachedTypeMatches(typeName, fileContent, typesFilePath);
21063
21662
  if (match && match.index !== void 0) {
21064
21663
  const classStart = match.index + match[0].indexOf(typeName);
21065
21664
  const classEnd = classStart + typeName.length;
@@ -21691,6 +22290,7 @@ function createCompletionPlugin() {
21691
22290
  resolveProvider: false
21692
22291
  } },
21693
22292
  create(context) {
22293
+ const compiler_resolution_options = get_compiler_resolution_options(context);
21694
22294
  return {
21695
22295
  isAdditionalCompletion: true,
21696
22296
  async provideCompletionItems(document, position, completionContext, _token) {
@@ -21724,7 +22324,7 @@ function createCompletionPlugin() {
21724
22324
  });
21725
22325
  const fullText = document.getText();
21726
22326
  const cursorOffset = document.offsetAt(position);
21727
- const is_ripple = is_ripple_platform_document(document.uri);
22327
+ const is_ripple = is_ripple_platform_document(document.uri, compiler_resolution_options);
21728
22328
  if (isInsideImport(fullText, cursorOffset)) {
21729
22329
  if (is_ripple) items.push(...RIPPLE_IMPORTS);
21730
22330
  return {
@@ -22571,6 +23171,134 @@ function createTypeScriptServices(ts) {
22571
23171
  return create(ts);
22572
23172
  }
22573
23173
 
23174
+ //#endregion
23175
+ //#region src/workspaceState.js
23176
+ const WORKSPACE_FILE_PATTERNS = [
23177
+ "**/*.tsrx",
23178
+ "**/*.ts",
23179
+ "**/*.tsx",
23180
+ "**/*.cts",
23181
+ "**/*.mts",
23182
+ "**/*.js",
23183
+ "**/*.jsx",
23184
+ "**/*.cjs",
23185
+ "**/*.mjs",
23186
+ "**/*.json",
23187
+ "**/pnpm-lock.yaml",
23188
+ "**/pnpm-workspace.yaml",
23189
+ "**/yarn.lock",
23190
+ "**/bun.lock",
23191
+ "**/bun.lockb"
23192
+ ];
23193
+ const PACKAGE_STATE_FILE_NAMES = new Set([
23194
+ "package.json",
23195
+ "package-lock.json",
23196
+ "pnpm-lock.yaml",
23197
+ "pnpm-workspace.yaml",
23198
+ "yarn.lock",
23199
+ "bun.lock",
23200
+ "bun.lockb"
23201
+ ]);
23202
+ /**
23203
+ * @param {string} file_name
23204
+ */
23205
+ function isTypeScriptConfigFile(file_name) {
23206
+ const base_name = node_path.default.basename(file_name).toLowerCase();
23207
+ return base_name.endsWith(".json") && (base_name.includes("tsconfig") || base_name.includes("jsconfig"));
23208
+ }
23209
+ /**
23210
+ * @param {string} file_name
23211
+ */
23212
+ function isTypeDefinitionFile(file_name) {
23213
+ return /\.d\.[cm]?ts$/i.test(file_name);
23214
+ }
23215
+ /**
23216
+ * @param {string} file_name
23217
+ */
23218
+ function isPackageStateFile(file_name) {
23219
+ return PACKAGE_STATE_FILE_NAMES.has(node_path.default.basename(file_name).toLowerCase());
23220
+ }
23221
+ /**
23222
+ * @param {string} file_name
23223
+ */
23224
+ function normalizeFileName(file_name) {
23225
+ const is_windows_path = process.platform === "win32" || /^[a-z]:[\\/]/i.test(file_name) || file_name.startsWith("\\\\");
23226
+ const path_api = is_windows_path ? node_path.default.win32 : node_path.default;
23227
+ const normalized = path_api.normalize(path_api.resolve(file_name));
23228
+ return is_windows_path ? normalized.toLowerCase() : normalized;
23229
+ }
23230
+ /**
23231
+ * Record every config file that contributed to a parsed TypeScript project.
23232
+ * This set is intentionally additive across project reloads because Volar
23233
+ * recreates projects lazily. TypeScript allows both `extends` and
23234
+ * project-reference configs to use names other than tsconfig.json, so filename
23235
+ * matching alone is not sufficient.
23236
+ * @param {Set<string>} config_files
23237
+ * @param {{
23238
+ * configFileName?: string,
23239
+ * compilerOptions?: import('typescript').CompilerOptions,
23240
+ * projectReferences?: readonly import('typescript').ProjectReference[],
23241
+ * configDependencies?: Iterable<string>,
23242
+ * }} project
23243
+ */
23244
+ function trackTypeScriptConfigDependencies(config_files, project) {
23245
+ if (project.configFileName) config_files.add(normalizeFileName(project.configFileName));
23246
+ const config_file = project.compilerOptions?.configFile;
23247
+ for (const extended_file of config_file?.extendedSourceFiles ?? []) config_files.add(normalizeFileName(extended_file));
23248
+ for (const reference of project.projectReferences ?? []) config_files.add(normalizeFileName(reference.path));
23249
+ for (const dependency of project.configDependencies ?? []) config_files.add(normalizeFileName(dependency));
23250
+ }
23251
+ /**
23252
+ * Classify watched changes before mutating project state. Package changes need
23253
+ * a process restart because Node does not expose a supported way to evict a
23254
+ * loaded ESM compiler and its transitive module graph.
23255
+ * @param {import('@volar/language-server').FileEvent[]} changes
23256
+ * @param {ReadonlySet<string>} [tracked_config_files]
23257
+ */
23258
+ function classifyWorkspaceChanges(changes, tracked_config_files = /* @__PURE__ */ new Set()) {
23259
+ let reloadProjects = false;
23260
+ let restartLanguageServer = false;
23261
+ /** @type {Set<string>} */
23262
+ const changedTypeDefinitions = /* @__PURE__ */ new Set();
23263
+ for (const change of changes) {
23264
+ const uri = vscode_uri.URI.parse(change.uri);
23265
+ const file_name = uri.scheme === "file" ? uri.fsPath : uri.path;
23266
+ if (isTypeScriptConfigFile(file_name) || tracked_config_files.has(normalizeFileName(file_name))) reloadProjects = true;
23267
+ if (isPackageStateFile(file_name)) restartLanguageServer = true;
23268
+ else if (isTypeDefinitionFile(file_name)) changedTypeDefinitions.add(file_name);
23269
+ }
23270
+ return {
23271
+ restartLanguageServer,
23272
+ reloadProjects: reloadProjects && !restartLanguageServer,
23273
+ changedTypeDefinitions: restartLanguageServer ? [] : [...changedTypeDefinitions]
23274
+ };
23275
+ }
23276
+ /**
23277
+ * @param {import('@volar/language-server').FileEvent[]} changes
23278
+ * @param {{
23279
+ * restartLanguageServer: () => void,
23280
+ * invalidateCompilerResolutionCaches?: () => void,
23281
+ * reloadProjects: () => void,
23282
+ * requestRefresh: (clearDiagnostics: boolean) => unknown,
23283
+ * invalidateTypeDefinitions: (file_name?: string) => void,
23284
+ * }} hooks
23285
+ * @param {ReadonlySet<string>} [tracked_config_files]
23286
+ */
23287
+ function handleWorkspaceChanges(changes, hooks, tracked_config_files) {
23288
+ const effects = classifyWorkspaceChanges(changes, tracked_config_files);
23289
+ if (effects.restartLanguageServer) {
23290
+ hooks.restartLanguageServer();
23291
+ return effects;
23292
+ }
23293
+ for (const file_name of effects.changedTypeDefinitions) hooks.invalidateTypeDefinitions(file_name);
23294
+ if (effects.reloadProjects) {
23295
+ hooks.invalidateCompilerResolutionCaches?.();
23296
+ hooks.reloadProjects();
23297
+ hooks.requestRefresh(true);
23298
+ }
23299
+ return effects;
23300
+ }
23301
+
22574
23302
  //#endregion
22575
23303
  //#region src/server.js
22576
23304
  /** @import {CompilerOptions} from 'typescript' */
@@ -22603,10 +23331,20 @@ function createRippleLanguageServer() {
22603
23331
  const connection = (0, import_node.createConnection)();
22604
23332
  const server = (0, import_node.createServer)(connection);
22605
23333
  connection.listen();
22606
- const rippleLanguagePlugin = getRippleLanguagePlugin();
22607
- log("Language plugin instance created");
22608
23334
  /** @type {WeakSet<Function>} */
22609
23335
  const wrappedFunctions = /* @__PURE__ */ new WeakSet();
23336
+ /** @type {Set<string>} */
23337
+ const trackedTypeScriptConfigFiles = /* @__PURE__ */ new Set();
23338
+ /** @type {Set<Set<string>>} */
23339
+ const compilerResolutionDependencySets = /* @__PURE__ */ new Set();
23340
+ let restartScheduled = false;
23341
+ /** Restart the process so Node drops the complete ESM compiler graph. */
23342
+ function restartLanguageServer() {
23343
+ if (restartScheduled) return;
23344
+ restartScheduled = true;
23345
+ log("Restarting after package state changed.");
23346
+ setTimeout(() => process.exit(0), 50);
23347
+ }
22610
23348
  /**
22611
23349
  * Ensure TypeScript hosts always see compiler options with Ripple defaults.
22612
23350
  * @param {unknown} target
@@ -22639,12 +23377,25 @@ function createRippleLanguageServer() {
22639
23377
  log("Initializing Ripple language server...");
22640
23378
  log("Initialization options:", JSON.stringify(params.initializationOptions, null, 2));
22641
23379
  const ts = require("typescript");
22642
- const initResult = server.initialize(params, (0, import_node.createTypeScriptProject)(ts, void 0, ({ projectHost }) => {
23380
+ const initResult = server.initialize(params, (0, import_node.createTypeScriptProject)(ts, void 0, ({ configFileName, projectHost, sys }) => {
22643
23381
  wrapCompilerOptionsProvider(projectHost, "getCompilationSettings");
23382
+ const compilerResolutionDependencies = /* @__PURE__ */ new Set();
23383
+ const languagePlugin = getRippleLanguagePlugin({
23384
+ ts,
23385
+ configFileName,
23386
+ configHost: sys,
23387
+ dependencies: compilerResolutionDependencies
23388
+ });
23389
+ compilerResolutionDependencySets.add(compilerResolutionDependencies);
22644
23390
  return {
22645
- languagePlugins: [rippleLanguagePlugin],
23391
+ languagePlugins: [languagePlugin],
22646
23392
  setup({ project }) {
22647
23393
  wrapCompilerOptionsProvider(project?.typescript?.languageServiceHost, "getCompilationSettings");
23394
+ trackTypeScriptConfigDependencies(trackedTypeScriptConfigFiles, {
23395
+ configFileName,
23396
+ compilerOptions: projectHost.getCompilationSettings(),
23397
+ projectReferences: projectHost.getProjectReferences?.()
23398
+ });
22648
23399
  }
22649
23400
  };
22650
23401
  }), [
@@ -22669,21 +23420,21 @@ function createRippleLanguageServer() {
22669
23420
  connection.onInitialized(async () => {
22670
23421
  log("Server initialized.");
22671
23422
  server.initialized();
23423
+ server.fileWatcher.onDidChangeWatchedFiles(({ changes }) => {
23424
+ for (const configDependencies of compilerResolutionDependencySets) trackTypeScriptConfigDependencies(trackedTypeScriptConfigFiles, { configDependencies });
23425
+ if (handleWorkspaceChanges(changes, {
23426
+ restartLanguageServer,
23427
+ invalidateCompilerResolutionCaches,
23428
+ invalidateTypeDefinitions: invalidateTypeDefinitionCaches,
23429
+ reloadProjects: () => {
23430
+ server.project.reload();
23431
+ },
23432
+ requestRefresh: (clearDiagnostics) => server.languageFeatures.requestRefresh(clearDiagnostics)
23433
+ }, trackedTypeScriptConfigFiles).reloadProjects) log("Reloaded TypeScript projects after workspace configuration changed.");
23434
+ });
22672
23435
  try {
22673
- await server.fileWatcher.watchFiles([
22674
- "**/*.ts",
22675
- "**/*.tsx",
22676
- "**/*.cts",
22677
- "**/*.mts",
22678
- "**/*.js",
22679
- "**/*.jsx",
22680
- "**/*.cjs",
22681
- "**/*.mjs",
22682
- "**/*.d.ts",
22683
- "**/tsconfig.json",
22684
- "**/jsconfig.json"
22685
- ]);
22686
- log("File watchers registered for TypeScript/JavaScript files.");
23436
+ await server.fileWatcher.watchFiles(WORKSPACE_FILE_PATTERNS);
23437
+ log("Workspace file watchers registered.");
22687
23438
  } catch (err) {
22688
23439
  logError("Failed to register file watchers:", err);
22689
23440
  }
@@ -22713,4 +23464,4 @@ Object.defineProperty(exports, 'createRippleLanguageServer', {
22713
23464
  return createRippleLanguageServer;
22714
23465
  }
22715
23466
  });
22716
- //# sourceMappingURL=server-BDTOWnFL.js.map
23467
+ //# sourceMappingURL=server-B91H_hpq.js.map