From fb86a3f9693f43ee7bfacf3329e1897dd8372449 Mon Sep 17 00:00:00 2001 From: Egor Aristov Date: Wed, 15 Oct 2025 19:20:23 +0300 Subject: [PATCH] remove unused packages --- kitcom/internal/tsgo/module/cache.go | 29 - kitcom/internal/tsgo/module/resolver.go | 2010 ------------------ kitcom/internal/tsgo/module/resolver_test.go | 398 ---- kitcom/internal/tsgo/module/types.go | 147 -- kitcom/internal/tsgo/module/util.go | 155 -- kitcom/internal/tsgo/nodebuilder/types.go | 80 - 6 files changed, 2819 deletions(-) delete mode 100644 kitcom/internal/tsgo/module/cache.go delete mode 100644 kitcom/internal/tsgo/module/resolver.go delete mode 100644 kitcom/internal/tsgo/module/resolver_test.go delete mode 100644 kitcom/internal/tsgo/module/types.go delete mode 100644 kitcom/internal/tsgo/module/util.go delete mode 100644 kitcom/internal/tsgo/nodebuilder/types.go diff --git a/kitcom/internal/tsgo/module/cache.go b/kitcom/internal/tsgo/module/cache.go deleted file mode 100644 index 326f60e..0000000 --- a/kitcom/internal/tsgo/module/cache.go +++ /dev/null @@ -1,29 +0,0 @@ -package module - -import ( - "sync" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/packagejson" -) - -type ModeAwareCache[T any] map[ModeAwareCacheKey]T - -type caches struct { - packageJsonInfoCache *packagejson.InfoCache - - // Cached representation for `core.CompilerOptions.paths`. - // Doesn't handle other path patterns like in `typesVersions`. - parsedPatternsForPathsOnce sync.Once - parsedPatternsForPaths *ParsedPatterns -} - -func newCaches( - currentDirectory string, - useCaseSensitiveFileNames bool, - options *core.CompilerOptions, -) caches { - return caches{ - packageJsonInfoCache: packagejson.NewInfoCache(currentDirectory, useCaseSensitiveFileNames), - } -} diff --git a/kitcom/internal/tsgo/module/resolver.go b/kitcom/internal/tsgo/module/resolver.go deleted file mode 100644 index 4229da6..0000000 --- a/kitcom/internal/tsgo/module/resolver.go +++ /dev/null @@ -1,2010 +0,0 @@ -package module - -import ( - "fmt" - "slices" - "strings" - "sync" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/packagejson" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/semver" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type resolved struct { - path string - extension string - packageId PackageId - originalPath string - resolvedUsingTsExtension bool -} - -func (r *resolved) shouldContinueSearching() bool { - return r == nil -} - -func (r *resolved) isResolved() bool { - return r != nil && r.path != "" -} - -func continueSearching() *resolved { - return nil -} - -func unresolved() *resolved { - return &resolved{} -} - -type resolutionKindSpecificLoader = func(extensions extensions, candidate string, onlyRecordFailures bool) *resolved - -type tracer struct { - traces []string -} - -func (t *tracer) write(msg string) { - if t != nil { - t.traces = append(t.traces, msg) - } -} - -func (t *tracer) getTraces() []string { - if t != nil { - return t.traces - } - return nil -} - -type resolutionState struct { - resolver *Resolver - tracer *tracer - - // request fields - name string - containingDirectory string - isConfigLookup bool - features NodeResolutionFeatures - esmMode bool - conditions []string - extensions extensions - compilerOptions *core.CompilerOptions - - // state fields - candidateIsFromPackageJsonField bool - resolvedPackageDirectory bool - failedLookupLocations []string - affectingLocations []string - diagnostics []*ast.Diagnostic - - // Similar to whats on resolver but only done if compilerOptions are for project reference redirect - // Cached representation for `core.CompilerOptions.paths`. - // Doesn't handle other path patterns like in `typesVersions`. - parsedPatternsForPathsOnce sync.Once - parsedPatternsForPaths *ParsedPatterns -} - -func newResolutionState( - name string, - containingDirectory string, - isTypeReferenceDirective bool, - resolutionMode core.ResolutionMode, - compilerOptions *core.CompilerOptions, - redirectedReference ResolvedProjectReference, - resolver *Resolver, - traceBuilder *tracer, -) *resolutionState { - state := &resolutionState{ - name: name, - containingDirectory: containingDirectory, - compilerOptions: GetCompilerOptionsWithRedirect(compilerOptions, redirectedReference), - resolver: resolver, - tracer: traceBuilder, - } - - if isTypeReferenceDirective { - state.extensions = extensionsDeclaration - } else if compilerOptions.NoDtsResolution == core.TSTrue { - state.extensions = extensionsImplementationFiles - } else { - state.extensions = extensionsTypeScript | extensionsJavaScript | extensionsDeclaration - } - - if !isTypeReferenceDirective && compilerOptions.GetResolveJsonModule() { - state.extensions |= extensionsJson - } - - switch compilerOptions.GetModuleResolutionKind() { - case core.ModuleResolutionKindNode16: - state.features = NodeResolutionFeaturesNode16Default - state.esmMode = resolutionMode == core.ModuleKindESNext - state.conditions = GetConditions(compilerOptions, resolutionMode) - case core.ModuleResolutionKindNodeNext: - state.features = NodeResolutionFeaturesNodeNextDefault - state.esmMode = resolutionMode == core.ModuleKindESNext - state.conditions = GetConditions(compilerOptions, resolutionMode) - case core.ModuleResolutionKindBundler: - state.features = getNodeResolutionFeatures(compilerOptions) - state.conditions = GetConditions(compilerOptions, resolutionMode) - } - return state -} - -func GetCompilerOptionsWithRedirect(compilerOptions *core.CompilerOptions, redirectedReference ResolvedProjectReference) *core.CompilerOptions { - if redirectedReference == nil { - return compilerOptions - } - if optionsFromRedirect := redirectedReference.CompilerOptions(); optionsFromRedirect != nil { - return optionsFromRedirect - } - return compilerOptions -} - -type Resolver struct { - caches - host ResolutionHost - compilerOptions *core.CompilerOptions - typingsLocation string - projectName string - // reportDiagnostic: DiagnosticReporter -} - -func NewResolver( - host ResolutionHost, - options *core.CompilerOptions, - typingsLocation string, - projectName string, -) *Resolver { - return &Resolver{ - host: host, - caches: newCaches(host.GetCurrentDirectory(), host.FS().UseCaseSensitiveFileNames(), options), - compilerOptions: options, - typingsLocation: typingsLocation, - projectName: projectName, - } -} - -func (r *Resolver) newTraceBuilder() *tracer { - if r.compilerOptions.TraceResolution == core.TSTrue { - return &tracer{} - } - return nil -} - -func (r *Resolver) GetPackageScopeForPath(directory string) *packagejson.InfoCacheEntry { - return (&resolutionState{compilerOptions: r.compilerOptions, resolver: r}).getPackageScopeForPath(directory) -} - -func (r *Resolver) GetPackageJsonScopeIfApplicable(path string) *packagejson.InfoCacheEntry { - if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionMts, tspath.ExtensionCts, tspath.ExtensionMjs, tspath.ExtensionCjs}) { - return nil - } - - moduleResolutionKind := r.compilerOptions.GetModuleResolutionKind() - if core.ModuleResolutionKindNode16 <= moduleResolutionKind && moduleResolutionKind <= core.ModuleResolutionKindNodeNext || strings.Contains(path, "/node_modules/") { - return r.GetPackageScopeForPath(tspath.GetDirectoryPath(path)) - } - - return nil -} - -func (r *tracer) traceResolutionUsingProjectReference(redirectedReference ResolvedProjectReference) { - if redirectedReference != nil && redirectedReference.CompilerOptions() != nil { - r.write(diagnostics.Using_compiler_options_of_project_reference_redirect_0.Format(redirectedReference.ConfigName())) - } -} - -func (r *Resolver) ResolveTypeReferenceDirective( - typeReferenceDirectiveName string, - containingFile string, - resolutionMode core.ResolutionMode, - redirectedReference ResolvedProjectReference, -) (*ResolvedTypeReferenceDirective, []string) { - traceBuilder := r.newTraceBuilder() - - compilerOptions := GetCompilerOptionsWithRedirect(r.compilerOptions, redirectedReference) - containingDirectory := tspath.GetDirectoryPath(containingFile) - - typeRoots, fromConfig := compilerOptions.GetEffectiveTypeRoots(r.host.GetCurrentDirectory()) - if traceBuilder != nil { - traceBuilder.write(diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2.Format(typeReferenceDirectiveName, containingFile, strings.Join(typeRoots, ","))) - traceBuilder.traceResolutionUsingProjectReference(redirectedReference) - } - - state := newResolutionState(typeReferenceDirectiveName, containingDirectory, true /*isTypeReferenceDirective*/, resolutionMode, compilerOptions, redirectedReference, r, traceBuilder) - result := state.resolveTypeReferenceDirective(typeRoots, fromConfig, strings.HasSuffix(containingFile, InferredTypesContainingFile)) - - if traceBuilder != nil { - traceBuilder.traceTypeReferenceDirectiveResult(typeReferenceDirectiveName, result) - } - return result, traceBuilder.getTraces() -} - -func (r *Resolver) ResolveModuleName(moduleName string, containingFile string, resolutionMode core.ResolutionMode, redirectedReference ResolvedProjectReference) (*ResolvedModule, []string) { - traceBuilder := r.newTraceBuilder() - compilerOptions := GetCompilerOptionsWithRedirect(r.compilerOptions, redirectedReference) - if traceBuilder != nil { - traceBuilder.write(diagnostics.Resolving_module_0_from_1.Format(moduleName, containingFile)) - traceBuilder.traceResolutionUsingProjectReference(redirectedReference) - } - containingDirectory := tspath.GetDirectoryPath(containingFile) - - moduleResolution := compilerOptions.ModuleResolution - if moduleResolution == core.ModuleResolutionKindUnknown { - moduleResolution = compilerOptions.GetModuleResolutionKind() - if traceBuilder != nil { - traceBuilder.write(diagnostics.Module_resolution_kind_is_not_specified_using_0.Format(moduleResolution.String())) - } - } else { - if traceBuilder != nil { - traceBuilder.write(diagnostics.Explicitly_specified_module_resolution_kind_Colon_0.Format(moduleResolution.String())) - } - } - - var result *ResolvedModule - switch moduleResolution { - case core.ModuleResolutionKindNode16, core.ModuleResolutionKindNodeNext, core.ModuleResolutionKindBundler: - state := newResolutionState(moduleName, containingDirectory, false /*isTypeReferenceDirective*/, resolutionMode, compilerOptions, redirectedReference, r, traceBuilder) - result = state.resolveNodeLike() - default: - panic(fmt.Sprintf("Unexpected moduleResolution: %d", moduleResolution)) - } - - if traceBuilder != nil { - if result.IsResolved() { - if result.PackageId.Name != "" { - traceBuilder.write(diagnostics.Module_name_0_was_successfully_resolved_to_1_with_Package_ID_2.Format(moduleName, result.ResolvedFileName, result.PackageId.String())) - } else { - traceBuilder.write(diagnostics.Module_name_0_was_successfully_resolved_to_1.Format(moduleName, result.ResolvedFileName)) - } - } else { - traceBuilder.write(diagnostics.Module_name_0_was_not_resolved.Format(moduleName)) - } - } - - return r.tryResolveFromTypingsLocation(moduleName, containingDirectory, result, traceBuilder), traceBuilder.getTraces() -} - -func (r *Resolver) tryResolveFromTypingsLocation(moduleName string, containingDirectory string, originalResult *ResolvedModule, traceBuilder *tracer) *ResolvedModule { - if r.typingsLocation == "" || - tspath.IsExternalModuleNameRelative(moduleName) || - (originalResult.ResolvedFileName != "" && tspath.ExtensionIsOneOf(originalResult.Extension, tspath.SupportedTSExtensionsWithJsonFlat)) { - return originalResult - } - - state := newResolutionState( - moduleName, - containingDirectory, - false, /*isTypeReferenceDirective*/ - core.ModuleKindNone, // resolutionMode, - r.compilerOptions, - nil, // redirectedReference, - r, - traceBuilder, - ) - if traceBuilder != nil { - traceBuilder.write(diagnostics.Auto_discovery_for_typings_is_enabled_in_project_0_Running_extra_resolution_pass_for_module_1_using_cache_location_2.Format(r.projectName, moduleName, r.typingsLocation)) - } - globalResolved := state.loadModuleFromImmediateNodeModulesDirectory(extensionsDeclaration, r.typingsLocation, false) - if globalResolved == nil { - return originalResult - } - result := state.createResolvedModule(globalResolved, true) - result.FailedLookupLocations = append(originalResult.FailedLookupLocations, result.FailedLookupLocations...) - result.AffectingLocations = append(originalResult.AffectingLocations, result.AffectingLocations...) - result.ResolutionDiagnostics = append(originalResult.ResolutionDiagnostics, result.ResolutionDiagnostics...) - return result -} - -func (r *Resolver) resolveConfig(moduleName string, containingFile string) *ResolvedModule { - containingDirectory := tspath.GetDirectoryPath(containingFile) - state := newResolutionState(moduleName, containingDirectory, false /*isTypeReferenceDirective*/, core.ModuleKindCommonJS, r.compilerOptions, nil, r, nil) - state.isConfigLookup = true - state.extensions = extensionsJson - return state.resolveNodeLike() -} - -func (r *tracer) traceTypeReferenceDirectiveResult(typeReferenceDirectiveName string, result *ResolvedTypeReferenceDirective) { - if !result.IsResolved() { - r.write(diagnostics.Type_reference_directive_0_was_not_resolved.Format(typeReferenceDirectiveName)) - } else if result.PackageId.Name != "" { - r.write(diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_with_Package_ID_2_primary_Colon_3.Format( - typeReferenceDirectiveName, - result.ResolvedFileName, - result.PackageId.String(), - result.Primary, - )) - } else { - r.write(diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2.Format( - typeReferenceDirectiveName, - result.ResolvedFileName, - result.Primary, - )) - } -} - -func (r *resolutionState) resolveTypeReferenceDirective(typeRoots []string, fromConfig bool, fromInferredTypesContainingFile bool) *ResolvedTypeReferenceDirective { - // Primary lookup - if len(typeRoots) > 0 { - if r.tracer != nil { - r.tracer.write(diagnostics.Resolving_with_primary_search_path_0.Format(strings.Join(typeRoots, ", "))) - } - for _, typeRoot := range typeRoots { - candidate := r.getCandidateFromTypeRoot(typeRoot) - directoryExists := r.resolver.host.FS().DirectoryExists(candidate) - if !directoryExists && r.tracer != nil { - r.tracer.write(diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it.Format(typeRoot)) - } - if fromConfig { - // Custom typeRoots resolve as file or directory just like we do modules - if resolvedFromFile := r.loadModuleFromFile(extensionsDeclaration, candidate, !directoryExists); !resolvedFromFile.shouldContinueSearching() { - packageDirectory := ParseNodeModuleFromPath(resolvedFromFile.path, false) - if packageDirectory != "" { - resolvedFromFile.packageId = r.getPackageId(resolvedFromFile.path, r.getPackageJsonInfo(packageDirectory, false /*onlyRecordFailures*/)) - } - return r.createResolvedTypeReferenceDirective(resolvedFromFile, true /*primary*/) - } - } - if resolvedFromDirectory := r.loadNodeModuleFromDirectory(extensionsDeclaration, candidate, !directoryExists, true /*considerPackageJson*/); !resolvedFromDirectory.shouldContinueSearching() { - return r.createResolvedTypeReferenceDirective(resolvedFromDirectory, true /*primary*/) - } - } - } else if r.tracer != nil { - r.tracer.write(diagnostics.Root_directory_cannot_be_determined_skipping_primary_search_paths.Format()) - } - - // Secondary lookup - var resolved *resolved - if !fromConfig || !fromInferredTypesContainingFile { - if r.tracer != nil { - r.tracer.write(diagnostics.Looking_up_in_node_modules_folder_initial_location_0.Format(r.containingDirectory)) - } - if !tspath.IsExternalModuleNameRelative(r.name) { - resolved = r.loadModuleFromNearestNodeModulesDirectory(false /*typesScopeOnly*/) - } else { - candidate := normalizePathForCJSResolution(r.containingDirectory, r.name) - resolved = r.nodeLoadModuleByRelativeName(extensionsDeclaration, candidate, false /*onlyRecordFailures*/, true /*considerPackageJson*/) - } - } else if r.tracer != nil { - r.tracer.write(diagnostics.Resolving_type_reference_directive_for_program_that_specifies_custom_typeRoots_skipping_lookup_in_node_modules_folder.Format()) - } - return r.createResolvedTypeReferenceDirective(resolved, false /*primary*/) -} - -func (r *resolutionState) getCandidateFromTypeRoot(typeRoot string) string { - nameForLookup := r.name - if strings.HasSuffix(typeRoot, "/node_modules/@types") || strings.HasSuffix(typeRoot, "/node_modules/@types/") { - nameForLookup = r.mangleScopedPackageName(r.name) - } - return tspath.CombinePaths(typeRoot, nameForLookup) -} - -func (r *resolutionState) mangleScopedPackageName(name string) string { - mangled := MangleScopedPackageName(name) - if r.tracer != nil && mangled != name { - r.tracer.write(diagnostics.Scoped_package_detected_looking_in_0.Format(mangled)) - } - return mangled -} - -func (r *resolutionState) getPackageScopeForPath(directory string) *packagejson.InfoCacheEntry { - result, _ := tspath.ForEachAncestorDirectory( - directory, - func(directory string) (*packagejson.InfoCacheEntry, bool) { - // !!! stop at global cache - if result := r.getPackageJsonInfo(directory, false /*onlyRecordFailures*/); result != nil { - return result, true - } - return nil, false - }, - ) - return result -} - -func (r *resolutionState) resolveNodeLike() *ResolvedModule { - if r.tracer != nil { - conditions := strings.Join(core.Map(r.conditions, func(c string) string { return `'` + c + `'` }), ", ") - if r.esmMode { - r.tracer.write(diagnostics.Resolving_in_0_mode_with_conditions_1.Format("ESM", conditions)) - } else { - r.tracer.write(diagnostics.Resolving_in_0_mode_with_conditions_1.Format("CJS", conditions)) - } - } - result := r.resolveNodeLikeWorker() - if r.resolvedPackageDirectory && - !r.isConfigLookup && - r.features&NodeResolutionFeaturesExports != 0 && - r.extensions&(extensionsTypeScript|extensionsDeclaration) != 0 && - !tspath.IsExternalModuleNameRelative(r.name) && - result.IsResolved() && - result.IsExternalLibraryImport && - !extensionIsOk(extensionsTypeScript|extensionsDeclaration, result.Extension) && - slices.Contains(r.conditions, "import") { - if r.tracer != nil { - r.tracer.write(diagnostics.Resolution_of_non_relative_name_failed_trying_with_modern_Node_resolution_features_disabled_to_see_if_npm_library_needs_configuration_update.Format()) - } - r.features = r.features & ^NodeResolutionFeaturesExports - r.extensions = r.extensions & (extensionsTypeScript | extensionsDeclaration) - diagnosticsCount := len(r.diagnostics) - if diagnosticResult := r.resolveNodeLikeWorker(); diagnosticResult.IsResolved() && diagnosticResult.IsExternalLibraryImport { - result.AlternateResult = diagnosticResult.ResolvedFileName - } - r.diagnostics = r.diagnostics[:diagnosticsCount] - } - return result -} - -func (r *resolutionState) resolveNodeLikeWorker() *ResolvedModule { - if resolved := r.tryLoadModuleUsingOptionalResolutionSettings(); !resolved.shouldContinueSearching() { - return r.createResolvedModuleHandlingSymlink(resolved) - } - - if !tspath.IsExternalModuleNameRelative(r.name) { - if r.features&NodeResolutionFeaturesImports != 0 && strings.HasPrefix(r.name, "#") { - if resolved := r.loadModuleFromImports(); !resolved.shouldContinueSearching() { - return r.createResolvedModuleHandlingSymlink(resolved) - } - } - if r.features&NodeResolutionFeaturesSelfName != 0 { - if resolved := r.loadModuleFromSelfNameReference(); !resolved.shouldContinueSearching() { - return r.createResolvedModuleHandlingSymlink(resolved) - } - } - if strings.Contains(r.name, ":") { - if r.tracer != nil { - r.tracer.write(diagnostics.Skipping_module_0_that_looks_like_an_absolute_URI_target_file_types_Colon_1.Format(r.name, r.extensions.String())) - } - return r.createResolvedModule(nil, false) - } - if r.tracer != nil { - r.tracer.write(diagnostics.Loading_module_0_from_node_modules_folder_target_file_types_Colon_1.Format(r.name, r.extensions.String())) - } - if resolved := r.loadModuleFromNearestNodeModulesDirectory(false /*typesScopeOnly*/); !resolved.shouldContinueSearching() { - return r.createResolvedModuleHandlingSymlink(resolved) - } - if r.extensions&extensionsDeclaration != 0 { - // !!! - // if resolved := r.resolveFromTypeRoot(); !resolved.shouldContinueSearching() { - // return r.createResolvedModuleHandlingSymlink(resolved) - // } - } - } else { - candidate := normalizePathForCJSResolution(r.containingDirectory, r.name) - resolved := r.nodeLoadModuleByRelativeName(r.extensions, candidate, false, true) - return r.createResolvedModule( - resolved, - resolved != nil && strings.Contains(resolved.path, "/node_modules/"), - ) - } - return r.createResolvedModule(nil, false) -} - -func (r *resolutionState) loadModuleFromSelfNameReference() *resolved { - directoryPath := tspath.GetNormalizedAbsolutePath(r.containingDirectory, r.resolver.host.GetCurrentDirectory()) - scope := r.getPackageScopeForPath(directoryPath) - if !scope.Exists() || scope.Contents.Exports.IsFalsy() { - // !!! falsy check seems wrong? - return continueSearching() - } - name, ok := scope.Contents.Name.GetValue() - if !ok { - return continueSearching() - } - parts := tspath.GetPathComponents(r.name, "") - nameParts := tspath.GetPathComponents(name, "") - if len(parts) < len(nameParts) || !slices.Equal(nameParts, parts[:len(nameParts)]) { - return continueSearching() - } - trailingParts := parts[len(nameParts):] - var subpath string - if len(trailingParts) > 0 { - subpath = tspath.CombinePaths(".", trailingParts...) - } else { - subpath = "." - } - // Maybe TODO: splitting extensions into two priorities should be unnecessary, except - // https://github.com/microsoft/TypeScript/issues/50762 makes the behavior different. - // As long as that bug exists, we need to do two passes here in self-name loading - // in order to be consistent with (non-self) library-name loading in - // `loadModuleFromNearestNodeModulesDirectoryWorker`, which uses two passes in order - // to prioritize `@types` packages higher up the directory tree over untyped - // implementation packages. See the selfNameModuleAugmentation.ts test for why this - // matters. - // - // However, there's an exception. If the user has `allowJs` and `declaration`, we need - // to ensure that self-name imports of their own package can resolve back to their - // input JS files via `tryLoadInputFileForPath` at a higher priority than their output - // declaration files, so we need to do a single pass with all extensions for that case. - if r.compilerOptions.GetAllowJS() && !strings.Contains(r.containingDirectory, "/node_modules/") { - return r.loadModuleFromExports(scope, r.extensions, subpath) - } - priorityExtensions := r.extensions & (extensionsTypeScript | extensionsDeclaration) - secondaryExtensions := r.extensions & ^(extensionsTypeScript | extensionsDeclaration) - if resolved := r.loadModuleFromExports(scope, priorityExtensions, subpath); !resolved.shouldContinueSearching() { - return resolved - } - return r.loadModuleFromExports(scope, secondaryExtensions, subpath) -} - -func (r *resolutionState) loadModuleFromImports() *resolved { - if r.name == "#" || strings.HasPrefix(r.name, "#/") { - if r.tracer != nil { - r.tracer.write(diagnostics.Invalid_import_specifier_0_has_no_possible_resolutions.Format(r.name)) - } - return continueSearching() - } - directoryPath := tspath.GetNormalizedAbsolutePath(r.containingDirectory, r.resolver.host.GetCurrentDirectory()) - scope := r.getPackageScopeForPath(directoryPath) - if !scope.Exists() { - if r.tracer != nil { - r.tracer.write(diagnostics.Directory_0_has_no_containing_package_json_scope_Imports_will_not_resolve.Format(directoryPath)) - } - return continueSearching() - } - if scope.Contents.Imports.Type != packagejson.JSONValueTypeObject { - // !!! Old compiler only checks for undefined, but then assumes `imports` is an object if present. - // Maybe should have a new diagnostic for imports of an invalid type. Also, array should be handled? - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_scope_0_has_no_imports_defined.Format(scope.PackageDirectory)) - } - return continueSearching() - } - - if result := r.loadModuleFromExportsOrImports(r.extensions, r.name, scope.Contents.Imports.AsObject(), scope /*isImports*/, true); !result.shouldContinueSearching() { - return result - } - - if r.tracer != nil { - r.tracer.write(diagnostics.Import_specifier_0_does_not_exist_in_package_json_scope_at_path_1.Format(r.name, scope.PackageDirectory)) - } - return continueSearching() -} - -func (r *resolutionState) loadModuleFromExports(packageInfo *packagejson.InfoCacheEntry, ext extensions, subpath string) *resolved { - // !!! This is ported exactly, but the falsy check seems wrong - if !packageInfo.Exists() || packageInfo.Contents.Exports.IsFalsy() { - return continueSearching() - } - - if subpath == "." { - var mainExport packagejson.ExportsOrImports - switch packageInfo.Contents.Exports.Type { - case packagejson.JSONValueTypeString, packagejson.JSONValueTypeArray: - mainExport = packageInfo.Contents.Exports - case packagejson.JSONValueTypeObject: - if packageInfo.Contents.Exports.IsConditions() { - mainExport = packageInfo.Contents.Exports - } else if dot, ok := packageInfo.Contents.Exports.AsObject().Get("."); ok { - mainExport = dot - } - } - if mainExport.Type != packagejson.JSONValueTypeNotPresent { - return r.loadModuleFromTargetExportOrImport(ext, subpath, packageInfo, false /*isImports*/, mainExport, "", false /*isPattern*/, ".") - } - } else if packageInfo.Contents.Exports.Type == packagejson.JSONValueTypeObject && packageInfo.Contents.Exports.IsSubpaths() { - if result := r.loadModuleFromExportsOrImports(ext, subpath, packageInfo.Contents.Exports.AsObject(), packageInfo, false /*isImports*/); !result.shouldContinueSearching() { - return result - } - } - - if r.tracer != nil { - r.tracer.write(diagnostics.Export_specifier_0_does_not_exist_in_package_json_scope_at_path_1.Format(subpath, packageInfo.PackageDirectory)) - } - return continueSearching() -} - -func (r *resolutionState) loadModuleFromExportsOrImports( - extensions extensions, - moduleName string, - lookupTable *collections.OrderedMap[string, packagejson.ExportsOrImports], - scope *packagejson.InfoCacheEntry, - isImports bool, -) *resolved { - if !strings.HasSuffix(moduleName, "/") && !strings.Contains(moduleName, "*") { - if target, ok := lookupTable.Get(moduleName); ok { - return r.loadModuleFromTargetExportOrImport(extensions, moduleName, scope, isImports, target, "", false /*isPattern*/, moduleName) - } - } - - expandingKeys := make([]string, 0, lookupTable.Size()) - for key := range lookupTable.Keys() { - if strings.Count(key, "*") == 1 || strings.HasSuffix(key, "/") { - expandingKeys = append(expandingKeys, key) - } - } - slices.SortFunc(expandingKeys, ComparePatternKeys) - - for _, potentialTarget := range expandingKeys { - if r.features&NodeResolutionFeaturesExportsPatternTrailers != 0 && matchesPatternWithTrailer(potentialTarget, moduleName) { - target, _ := lookupTable.Get(potentialTarget) - starPos := strings.Index(potentialTarget, "*") - subpath := moduleName[len(potentialTarget[:starPos]) : len(moduleName)-(len(potentialTarget)-1-starPos)] - return r.loadModuleFromTargetExportOrImport(extensions, moduleName, scope, isImports, target, subpath, true, potentialTarget) - } else if strings.HasSuffix(potentialTarget, "*") && strings.HasPrefix(moduleName, potentialTarget[:len(potentialTarget)-1]) { - target, _ := lookupTable.Get(potentialTarget) - subpath := moduleName[len(potentialTarget)-1:] - return r.loadModuleFromTargetExportOrImport(extensions, moduleName, scope, isImports, target, subpath, true, potentialTarget) - } else if strings.HasPrefix(moduleName, potentialTarget) { - target, _ := lookupTable.Get(potentialTarget) - subpath := moduleName[len(potentialTarget):] - return r.loadModuleFromTargetExportOrImport(extensions, moduleName, scope, isImports, target, subpath, false, potentialTarget) - } - } - - return continueSearching() -} - -func (r *resolutionState) loadModuleFromTargetExportOrImport(extensions extensions, moduleName string, scope *packagejson.InfoCacheEntry, isImports bool, target packagejson.ExportsOrImports, subpath string, isPattern bool, key string) *resolved { - switch target.Type { - case packagejson.JSONValueTypeString: - targetString, _ := target.Value.(string) - if !isPattern && len(subpath) > 0 && !strings.HasSuffix(targetString, "/") { - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_scope_0_has_invalid_type_for_target_of_specifier_1.Format(scope.PackageDirectory, moduleName)) - } - return continueSearching() - } - if !strings.HasPrefix(targetString, "./") { - if isImports && !strings.HasPrefix(targetString, "../") && !strings.HasPrefix(targetString, "/") && !tspath.IsRootedDiskPath(targetString) { - combinedLookup := targetString + subpath - if isPattern { - combinedLookup = strings.ReplaceAll(targetString, "*", subpath) - } - if r.tracer != nil { - r.tracer.write(diagnostics.Using_0_subpath_1_with_target_2.Format("imports", key, combinedLookup)) - r.tracer.write(diagnostics.Resolving_module_0_from_1.Format(combinedLookup, scope.PackageDirectory+"/")) - } - name, containingDirectory := r.name, r.containingDirectory - r.name, r.containingDirectory = combinedLookup, scope.PackageDirectory+"/" - defer func() { - r.name, r.containingDirectory = name, containingDirectory - }() - if result := r.resolveNodeLike(); result.IsResolved() { - return &resolved{ - path: result.ResolvedFileName, - extension: result.Extension, - packageId: result.PackageId, - originalPath: result.OriginalPath, - resolvedUsingTsExtension: result.ResolvedUsingTsExtension, - } - } - return continueSearching() - } - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_scope_0_has_invalid_type_for_target_of_specifier_1.Format(scope.PackageDirectory, moduleName)) - } - return continueSearching() - } - var parts []string - if tspath.PathIsRelative(targetString) { - parts = tspath.GetPathComponents(targetString, "")[1:] - } else { - parts = tspath.GetPathComponents(targetString, "") - } - partsAfterFirst := parts[1:] - if slices.Contains(partsAfterFirst, "..") || slices.Contains(partsAfterFirst, ".") || slices.Contains(partsAfterFirst, "node_modules") { - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_scope_0_has_invalid_type_for_target_of_specifier_1.Format(scope.PackageDirectory, moduleName)) - } - return continueSearching() - } - resolvedTarget := tspath.CombinePaths(scope.PackageDirectory, targetString) - // TODO: Assert that `resolvedTarget` is actually within the package directory? That's what the spec says.... but I'm not sure we need - // to be in the business of validating everyone's import and export map correctness. - subpathParts := tspath.GetPathComponents(subpath, "") - if slices.Contains(subpathParts, "..") || slices.Contains(subpathParts, ".") || slices.Contains(subpathParts, "node_modules") { - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_scope_0_has_invalid_type_for_target_of_specifier_1.Format(scope.PackageDirectory, moduleName)) - } - return continueSearching() - } - - if r.tracer != nil { - var messageTarget string - if isPattern { - messageTarget = strings.ReplaceAll(targetString, "*", subpath) - } else { - messageTarget = targetString + subpath - } - r.tracer.write(diagnostics.Using_0_subpath_1_with_target_2.Format(core.IfElse(isImports, "imports", "exports"), key, messageTarget)) - } - var finalPath string - if isPattern { - finalPath = tspath.GetNormalizedAbsolutePath(strings.ReplaceAll(resolvedTarget, "*", subpath), r.resolver.host.GetCurrentDirectory()) - } else { - finalPath = tspath.GetNormalizedAbsolutePath(resolvedTarget+subpath, r.resolver.host.GetCurrentDirectory()) - } - if inputLink := r.tryLoadInputFileForPath(finalPath, subpath, tspath.CombinePaths(scope.PackageDirectory, "package.json"), isImports); !inputLink.shouldContinueSearching() { - return inputLink - } - if result := r.loadFileNameFromPackageJSONField(extensions, finalPath, targetString, false /*onlyRecordFailures*/); !result.shouldContinueSearching() { - result.packageId = r.getPackageId(result.path, scope) - return result - } - return continueSearching() - - case packagejson.JSONValueTypeObject: - if r.tracer != nil { - r.tracer.write(diagnostics.Entering_conditional_exports.Format()) - } - for condition := range target.AsObject().Keys() { - if r.conditionMatches(condition) { - if r.tracer != nil { - r.tracer.write(diagnostics.Matched_0_condition_1.Format(core.IfElse(isImports, "imports", "exports"), condition)) - } - subTarget, _ := target.AsObject().Get(condition) - if result := r.loadModuleFromTargetExportOrImport(extensions, moduleName, scope, isImports, subTarget, subpath, isPattern, key); !result.shouldContinueSearching() { - if r.tracer != nil { - r.tracer.write(diagnostics.Resolved_under_condition_0.Format(condition)) - } - if r.tracer != nil { - r.tracer.write(diagnostics.Exiting_conditional_exports.Format()) - } - return result - } else if r.tracer != nil { - r.tracer.write(diagnostics.Failed_to_resolve_under_condition_0.Format(condition)) - } - } else { - if r.tracer != nil { - r.tracer.write(diagnostics.Saw_non_matching_condition_0.Format(condition)) - } - } - } - if r.tracer != nil { - r.tracer.write(diagnostics.Exiting_conditional_exports.Format()) - } - return continueSearching() - case packagejson.JSONValueTypeArray: - if len(target.AsArray()) == 0 { - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_scope_0_has_invalid_type_for_target_of_specifier_1.Format(scope.PackageDirectory, moduleName)) - } - return continueSearching() - } - for _, elem := range target.AsArray() { - if result := r.loadModuleFromTargetExportOrImport(extensions, moduleName, scope, isImports, elem, subpath, isPattern, key); !result.shouldContinueSearching() { - return result - } - } - - case packagejson.JSONValueTypeNull: - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_scope_0_explicitly_maps_specifier_1_to_null.Format(scope.PackageDirectory, moduleName)) - } - return continueSearching() - } - - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_scope_0_has_invalid_type_for_target_of_specifier_1.Format(scope.PackageDirectory, moduleName)) - } - return continueSearching() -} - -func (r *resolutionState) tryLoadInputFileForPath(finalPath string, entry string, packagePath string, isImports bool) *resolved { - // Replace any references to outputs for files in the program with the input files to support package self-names used with outDir - if !r.isConfigLookup && - (r.compilerOptions.DeclarationDir != "" || r.compilerOptions.OutDir != "") && - !strings.Contains(finalPath, "/node_modules/") && - (r.compilerOptions.ConfigFilePath == "" || tspath.ContainsPath( - tspath.GetDirectoryPath(packagePath), - r.compilerOptions.ConfigFilePath, - tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: r.resolver.host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: r.resolver.host.GetCurrentDirectory(), - }, - )) { - - // Note: this differs from Strada's tryLoadInputFileForPath in that it - // does not attempt to perform "guesses", instead requring a clear root indicator. - - var rootDir string - if r.compilerOptions.RootDir != "" { - // A `rootDir` compiler option strongly indicates the root location - rootDir = r.compilerOptions.RootDir - } else if r.compilerOptions.Composite.IsTrue() && r.compilerOptions.ConfigFilePath != "" { - // A `composite` project is using project references and has it's common src dir set to `.`, so it shouldn't need to check any other locations - rootDir = r.compilerOptions.ConfigFilePath - } else { - diagnostic := ast.NewDiagnostic( - nil, - core.TextRange{}, - core.IfElse(isImports, - diagnostics.The_project_root_is_ambiguous_but_is_required_to_resolve_import_map_entry_0_in_file_1_Supply_the_rootDir_compiler_option_to_disambiguate, - diagnostics.The_project_root_is_ambiguous_but_is_required_to_resolve_export_map_entry_0_in_file_1_Supply_the_rootDir_compiler_option_to_disambiguate, - ), - core.IfElse(entry == "", ".", entry), // replace empty string with `.` - the reverse of the operation done when entries are built - so main entrypoint errors don't look weird - packagePath, - ) - r.diagnostics = append(r.diagnostics, diagnostic) - return unresolved() - } - - candidateDirectories := r.getOutputDirectoriesForBaseDirectory(rootDir) - for _, candidateDir := range candidateDirectories { - if tspath.ContainsPath(candidateDir, finalPath, tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: r.resolver.host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: r.resolver.host.GetCurrentDirectory(), - }) { - // The matched export is looking up something in either the out declaration or js dir, now map the written path back into the source dir and source extension - pathFragment := finalPath[len(candidateDir)+1:] // +1 to also remove directory separator - possibleInputBase := tspath.CombinePaths(rootDir, pathFragment) - jsAndDtsExtensions := []string{tspath.ExtensionMjs, tspath.ExtensionCjs, tspath.ExtensionJs, tspath.ExtensionJson, tspath.ExtensionDmts, tspath.ExtensionDcts, tspath.ExtensionDts} - for _, ext := range jsAndDtsExtensions { - if tspath.FileExtensionIs(possibleInputBase, ext) { - inputExts := r.getPossibleOriginalInputExtensionForExtension(possibleInputBase) - for _, possibleExt := range inputExts { - if !extensionIsOk(r.extensions, possibleExt) { - continue - } - possibleInputWithInputExtension := tspath.ChangeExtension(possibleInputBase, possibleExt) - if r.resolver.host.FS().FileExists(possibleInputWithInputExtension) { - resolved := r.loadFileNameFromPackageJSONField(r.extensions, possibleInputWithInputExtension, "", false) - if !resolved.shouldContinueSearching() { - return resolved - } - } - } - } - } - } - } - } - return continueSearching() -} - -func (r *resolutionState) getOutputDirectoriesForBaseDirectory(commonSourceDirGuess string) []string { - // Config file output paths are processed to be relative to the host's current directory, while - // otherwise the paths are resolved relative to the common source dir the compiler puts together - currentDir := core.IfElse(r.compilerOptions.ConfigFilePath != "", r.resolver.host.GetCurrentDirectory(), commonSourceDirGuess) - var candidateDirectories []string - if r.compilerOptions.DeclarationDir != "" { - candidateDirectories = append(candidateDirectories, tspath.GetNormalizedAbsolutePath(tspath.CombinePaths(currentDir, r.compilerOptions.DeclarationDir), r.resolver.host.GetCurrentDirectory())) - } - if r.compilerOptions.OutDir != "" && r.compilerOptions.OutDir != r.compilerOptions.DeclarationDir { - candidateDirectories = append(candidateDirectories, tspath.GetNormalizedAbsolutePath(tspath.CombinePaths(currentDir, r.compilerOptions.OutDir), r.resolver.host.GetCurrentDirectory())) - } - return candidateDirectories -} - -func (r *resolutionState) getPossibleOriginalInputExtensionForExtension(path string) []string { - if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDmts, tspath.ExtensionMjs, tspath.ExtensionMts}) { - return []string{tspath.ExtensionMts, tspath.ExtensionMjs} - } - if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDcts, tspath.ExtensionCjs, tspath.ExtensionCts}) { - return []string{tspath.ExtensionCts, tspath.ExtensionCjs} - } - if tspath.FileExtensionIs(path, ".d.json.ts") { - return []string{tspath.ExtensionJson} - } - return []string{tspath.ExtensionTsx, tspath.ExtensionTs, tspath.ExtensionJsx, tspath.ExtensionJs} -} - -func (r *resolutionState) loadModuleFromNearestNodeModulesDirectory(typesScopeOnly bool) *resolved { - mode := core.ResolutionModeCommonJS - if r.esmMode || r.conditionMatches("import") { - mode = core.ResolutionModeESM - } - // Do (up to) two passes through node_modules: - // 1. For each ancestor node_modules directory, try to find: - // i. TS/DTS files in the implementation package - // ii. DTS files in the @types package - // 2. For each ancestor node_modules directory, try to find: - // i. JS files in the implementation package - priorityExtensions := r.extensions & (extensionsTypeScript | extensionsDeclaration) - secondaryExtensions := r.extensions & ^(extensionsTypeScript | extensionsDeclaration) - // (1) - if priorityExtensions != 0 { - if r.tracer != nil { - r.tracer.write(diagnostics.Searching_all_ancestor_node_modules_directories_for_preferred_extensions_Colon_0.Format(priorityExtensions.String())) - } - if result := r.loadModuleFromNearestNodeModulesDirectoryWorker(priorityExtensions, mode, typesScopeOnly); !result.shouldContinueSearching() { - return result - } - } - // (2) - if secondaryExtensions != 0 && !typesScopeOnly { - if r.tracer != nil { - r.tracer.write(diagnostics.Searching_all_ancestor_node_modules_directories_for_fallback_extensions_Colon_0.Format(secondaryExtensions.String())) - } - return r.loadModuleFromNearestNodeModulesDirectoryWorker(secondaryExtensions, mode, typesScopeOnly) - } - return continueSearching() -} - -func (r *resolutionState) loadModuleFromNearestNodeModulesDirectoryWorker(ext extensions, mode core.ResolutionMode, typesScopeOnly bool) *resolved { - result, _ := tspath.ForEachAncestorDirectory( - r.containingDirectory, - func(directory string) (result *resolved, stop bool) { - // !!! stop at global cache - if tspath.GetBaseFileName(directory) != "node_modules" { - result := r.loadModuleFromImmediateNodeModulesDirectory(ext, directory, typesScopeOnly) - return result, !result.shouldContinueSearching() - } - return continueSearching(), false - }, - ) - return result -} - -func (r *resolutionState) loadModuleFromImmediateNodeModulesDirectory(extensions extensions, directory string, typesScopeOnly bool) *resolved { - nodeModulesFolder := tspath.CombinePaths(directory, "node_modules") - nodeModulesFolderExists := r.resolver.host.FS().DirectoryExists(nodeModulesFolder) - if !nodeModulesFolderExists && r.tracer != nil { - r.tracer.write(diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it.Format(nodeModulesFolder)) - } - - if !typesScopeOnly { - if packageResult := r.loadModuleFromSpecificNodeModulesDirectory(extensions, r.name, nodeModulesFolder, nodeModulesFolderExists); !packageResult.shouldContinueSearching() { - return packageResult - } - } - - if extensions&extensionsDeclaration != 0 { - nodeModulesAtTypes := tspath.CombinePaths(nodeModulesFolder, "@types") - nodeModulesAtTypesExists := nodeModulesFolderExists && r.resolver.host.FS().DirectoryExists(nodeModulesAtTypes) - if !nodeModulesAtTypesExists && r.tracer != nil { - r.tracer.write(diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it.Format(nodeModulesAtTypes)) - } - return r.loadModuleFromSpecificNodeModulesDirectory(extensionsDeclaration, r.mangleScopedPackageName(r.name), nodeModulesAtTypes, nodeModulesAtTypesExists) - } - - return continueSearching() -} - -func (r *resolutionState) loadModuleFromSpecificNodeModulesDirectory(ext extensions, moduleName string, nodeModulesDirectory string, nodeModulesDirectoryExists bool) *resolved { - candidate := tspath.NormalizePath(tspath.CombinePaths(nodeModulesDirectory, moduleName)) - packageName, rest := ParsePackageName(moduleName) - packageDirectory := tspath.CombinePaths(nodeModulesDirectory, packageName) - - var rootPackageInfo *packagejson.InfoCacheEntry - // First look for a nested package.json, as in `node_modules/foo/bar/package.json` - packageInfo := r.getPackageJsonInfo(candidate, !nodeModulesDirectoryExists) - // But only if we're not respecting export maps (if we are, we might redirect around this location) - if rest != "" && packageInfo.Exists() { - if r.features&NodeResolutionFeaturesExports != 0 { - rootPackageInfo = r.getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists) - } - if !rootPackageInfo.Exists() || rootPackageInfo.Contents.Exports.Type == packagejson.JSONValueTypeNotPresent { - if fromFile := r.loadModuleFromFile(ext, candidate, !nodeModulesDirectoryExists); !fromFile.shouldContinueSearching() { - return fromFile - } - - if fromDirectory := r.loadNodeModuleFromDirectoryWorker(ext, candidate, !nodeModulesDirectoryExists, packageInfo); !fromDirectory.shouldContinueSearching() { - fromDirectory.packageId = r.getPackageId(packageDirectory, packageInfo) - return fromDirectory - } - } - } - - loader := func(extensions extensions, candidate string, onlyRecordFailures bool) *resolved { - if rest != "" || !r.esmMode { - if fromFile := r.loadModuleFromFile(extensions, candidate, onlyRecordFailures); !fromFile.shouldContinueSearching() { - fromFile.packageId = r.getPackageId(packageDirectory, packageInfo) - return fromFile - } - } - if fromDirectory := r.loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, packageInfo); !fromDirectory.shouldContinueSearching() { - fromDirectory.packageId = r.getPackageId(packageDirectory, packageInfo) - return fromDirectory - } - // !!! this is ported exactly, but checking for null seems wrong? - if rest == "" && packageInfo.Exists() && - (packageInfo.Contents.Exports.Type == packagejson.JSONValueTypeNotPresent || packageInfo.Contents.Exports.Type == packagejson.JSONValueTypeNull) && - r.esmMode { - // EsmMode disables index lookup in `loadNodeModuleFromDirectoryWorker` generally, however non-relative package resolutions still assume - // a default `index.js` entrypoint if no `main` or `exports` are present - if indexResult := r.loadModuleFromFile(extensions, tspath.CombinePaths(candidate, "index.js"), onlyRecordFailures); !indexResult.shouldContinueSearching() { - indexResult.packageId = r.getPackageId(packageDirectory, packageInfo) - return indexResult - } - } - return continueSearching() - } - - if rest != "" { - packageInfo = rootPackageInfo - if packageInfo == nil { - // Previous `packageInfo` may have been from a nested package.json; ensure we have the one from the package root now. - packageInfo = r.getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists) - } - } - if packageInfo != nil { - r.resolvedPackageDirectory = true - if r.features&NodeResolutionFeaturesExports != 0 && - packageInfo.Exists() && - packageInfo.Contents.Exports.Type != packagejson.JSONValueTypeNotPresent { - // package exports are higher priority than file/directory/typesVersions lookups and (and, if there's exports present, blocks them) - return r.loadModuleFromExports(packageInfo, ext, tspath.CombinePaths(".", rest)) - } - if rest != "" { - versionPaths := packageInfo.Contents.GetVersionPaths(r.getTraceFunc()) - if versionPaths.Exists() { - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2.Format(versionPaths.Version, core.Version(), rest)) - } - packageDirectoryExists := nodeModulesDirectoryExists && r.resolver.host.FS().DirectoryExists(packageDirectory) - pathPatterns := TryParsePatterns(versionPaths.GetPaths()) - if fromPaths := r.tryLoadModuleUsingPaths(ext, rest, packageDirectory, versionPaths.GetPaths(), pathPatterns, loader, !packageDirectoryExists); !fromPaths.shouldContinueSearching() { - return fromPaths - } - } - } - } - return loader(ext, candidate, !nodeModulesDirectoryExists) -} - -func (r *resolutionState) createResolvedModuleHandlingSymlink(resolved *resolved) *ResolvedModule { - isExternalLibraryImport := resolved != nil && strings.Contains(resolved.path, "/node_modules/") - if r.compilerOptions.PreserveSymlinks != core.TSTrue && - isExternalLibraryImport && - resolved.originalPath == "" && - !tspath.IsExternalModuleNameRelative(r.name) { - originalPath, resolvedFileName := r.getOriginalAndResolvedFileName(resolved.path) - if originalPath != "" { - resolved.path = resolvedFileName - resolved.originalPath = originalPath - } - } - return r.createResolvedModule(resolved, isExternalLibraryImport) -} - -func (r *resolutionState) createResolvedModule(resolved *resolved, isExternalLibraryImport bool) *ResolvedModule { - var resolvedModule ResolvedModule - resolvedModule.LookupLocations = LookupLocations{ - FailedLookupLocations: r.failedLookupLocations, - AffectingLocations: r.affectingLocations, - ResolutionDiagnostics: r.diagnostics, - } - - if resolved != nil { - resolvedModule.ResolvedFileName = resolved.path - resolvedModule.OriginalPath = resolved.originalPath - resolvedModule.IsExternalLibraryImport = isExternalLibraryImport - resolvedModule.ResolvedUsingTsExtension = resolved.resolvedUsingTsExtension - resolvedModule.Extension = resolved.extension - resolvedModule.PackageId = resolved.packageId - } - return &resolvedModule -} - -func (r *resolutionState) createResolvedTypeReferenceDirective(resolved *resolved, primary bool) *ResolvedTypeReferenceDirective { - var resolvedTypeReferenceDirective ResolvedTypeReferenceDirective - resolvedTypeReferenceDirective.LookupLocations = LookupLocations{ - FailedLookupLocations: r.failedLookupLocations, - AffectingLocations: r.affectingLocations, - ResolutionDiagnostics: r.diagnostics, - } - - if resolved.isResolved() { - if !tspath.ExtensionIsTs(resolved.extension) { - panic("expected a TypeScript file extension") - } - resolvedTypeReferenceDirective.ResolvedFileName = resolved.path - resolvedTypeReferenceDirective.Primary = primary - resolvedTypeReferenceDirective.PackageId = resolved.packageId - resolvedTypeReferenceDirective.IsExternalLibraryImport = strings.Contains(resolved.path, "/node_modules/") - - if r.compilerOptions.PreserveSymlinks != core.TSTrue { - originalPath, resolvedFileName := r.getOriginalAndResolvedFileName(resolved.path) - if originalPath != "" { - resolvedTypeReferenceDirective.ResolvedFileName = resolvedFileName - resolvedTypeReferenceDirective.OriginalPath = originalPath - } - } - } - return &resolvedTypeReferenceDirective -} - -func (r *resolutionState) getOriginalAndResolvedFileName(fileName string) (string, string) { - resolvedFileName := r.realPath(fileName) - comparePathsOptions := tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: r.resolver.host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: r.resolver.host.GetCurrentDirectory(), - } - if tspath.ComparePaths(fileName, resolvedFileName, comparePathsOptions) == 0 { - // If the fileName and realpath are differing only in casing, prefer fileName - // so that we can issue correct errors for casing under forceConsistentCasingInFileNames - return "", fileName - } - return fileName, resolvedFileName -} - -func (r *resolutionState) tryLoadModuleUsingOptionalResolutionSettings() *resolved { - if resolved := r.tryLoadModuleUsingPathsIfEligible(); !resolved.shouldContinueSearching() { - return resolved - } - - if !tspath.IsExternalModuleNameRelative(r.name) { - // No more tryLoadModuleUsingBaseUrl. - return continueSearching() - } else { - return r.tryLoadModuleUsingRootDirs() - } -} - -func (r *resolutionState) getParsedPatternsForPaths() *ParsedPatterns { - if r.compilerOptions == r.resolver.compilerOptions { - return r.resolver.getParsedPatternsForPaths() - } - r.parsedPatternsForPathsOnce.Do(func() { - r.parsedPatternsForPaths = TryParsePatterns(r.compilerOptions.Paths) - }) - return r.parsedPatternsForPaths -} - -func (r *resolutionState) tryLoadModuleUsingPathsIfEligible() *resolved { - if r.compilerOptions.Paths.Size() > 0 && !tspath.PathIsRelative(r.name) { - if r.tracer != nil { - r.tracer.write(diagnostics.X_paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0.Format(r.name)) - } - } else { - return continueSearching() - } - baseDirectory := r.compilerOptions.GetPathsBasePath(r.resolver.host.GetCurrentDirectory()) - pathPatterns := r.getParsedPatternsForPaths() - return r.tryLoadModuleUsingPaths( - r.extensions, - r.name, - baseDirectory, - r.compilerOptions.Paths, - pathPatterns, - func(extensions extensions, candidate string, onlyRecordFailures bool) *resolved { - return r.nodeLoadModuleByRelativeName(extensions, candidate, onlyRecordFailures, true /*considerPackageJson*/) - }, - false, /*onlyRecordFailures*/ - ) -} - -func (r *resolutionState) tryLoadModuleUsingPaths(extensions extensions, moduleName string, containingDirectory string, paths *collections.OrderedMap[string, []string], pathPatterns *ParsedPatterns, loader resolutionKindSpecificLoader, onlyRecordFailures bool) *resolved { - if matchedPattern := MatchPatternOrExact(pathPatterns, moduleName); matchedPattern.IsValid() { - matchedStar := matchedPattern.MatchedText(moduleName) - if r.tracer != nil { - r.tracer.write(diagnostics.Module_name_0_matched_pattern_1.Format(moduleName, matchedPattern.Text)) - } - for _, subst := range paths.GetOrZero(matchedPattern.Text) { - path := strings.Replace(subst, "*", matchedStar, 1) - candidate := tspath.NormalizePath(tspath.CombinePaths(containingDirectory, path)) - if r.tracer != nil { - r.tracer.write(diagnostics.Trying_substitution_0_candidate_module_location_Colon_1.Format(subst, path)) - } - // A path mapping may have an extension - if extension := tspath.TryGetExtensionFromPath(subst); extension != "" { - if path, ok := r.tryFile(candidate, onlyRecordFailures /*onlyRecordFailures*/); ok { - return &resolved{ - path: path, - extension: extension, - } - } - } - if resolved := loader(extensions, candidate, onlyRecordFailures || !r.resolver.host.FS().DirectoryExists(tspath.GetDirectoryPath(candidate))); !resolved.shouldContinueSearching() { - return resolved - } - } - } - return continueSearching() -} - -func (r *resolutionState) tryLoadModuleUsingRootDirs() *resolved { - if len(r.compilerOptions.RootDirs) == 0 { - return continueSearching() - } - - if r.tracer != nil { - r.tracer.write(diagnostics.X_rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0.Format(r.name)) - } - - candidate := tspath.NormalizePath(tspath.CombinePaths(r.containingDirectory, r.name)) - - var matchedRootDir string - var matchedNormalizedPrefix string - for _, rootDir := range r.compilerOptions.RootDirs { - // rootDirs are expected to be absolute - // in case of tsconfig.json this will happen automatically - compiler will expand relative names - // using location of tsconfig.json as base location - normalizedRoot := tspath.NormalizePath(rootDir) - if !strings.HasSuffix(normalizedRoot, "/") { - normalizedRoot += "/" - } - isLongestMatchingPrefix := strings.HasPrefix(candidate, normalizedRoot) && - (matchedNormalizedPrefix == "" || len(matchedNormalizedPrefix) < len(normalizedRoot)) - - if r.tracer != nil { - r.tracer.write(diagnostics.Checking_if_0_is_the_longest_matching_prefix_for_1_2.Format(normalizedRoot, candidate, isLongestMatchingPrefix)) - } - - if isLongestMatchingPrefix { - matchedNormalizedPrefix = normalizedRoot - matchedRootDir = rootDir - } - } - - if matchedNormalizedPrefix != "" { - if r.tracer != nil { - r.tracer.write(diagnostics.Longest_matching_prefix_for_0_is_1.Format(candidate, matchedNormalizedPrefix)) - } - suffix := candidate[len(matchedNormalizedPrefix):] - - // first - try to load from a initial location - if r.tracer != nil { - r.tracer.write(diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2.Format(suffix, matchedNormalizedPrefix, candidate)) - } - loader := func(extensions extensions, candidate string, onlyRecordFailures bool) *resolved { - return r.nodeLoadModuleByRelativeName(extensions, candidate, onlyRecordFailures, true /*considerPackageJson*/) - } - if resolvedFileName := loader(r.extensions, candidate, !r.resolver.host.FS().DirectoryExists(r.containingDirectory)); !resolvedFileName.shouldContinueSearching() { - return resolvedFileName - } - - if r.tracer != nil { - r.tracer.write(diagnostics.Trying_other_entries_in_rootDirs.Format()) - } - // then try to resolve using remaining entries in rootDirs - for _, rootDir := range r.compilerOptions.RootDirs { - if rootDir == matchedRootDir { - // skip the initially matched entry - continue - } - candidate := tspath.CombinePaths(tspath.NormalizePath(rootDir), suffix) - if r.tracer != nil { - r.tracer.write(diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2.Format(suffix, rootDir, candidate)) - } - baseDirectory := tspath.GetDirectoryPath(candidate) - if resolvedFileName := loader(r.extensions, candidate, !r.resolver.host.FS().DirectoryExists(baseDirectory)); !resolvedFileName.shouldContinueSearching() { - return resolvedFileName - } - } - if r.tracer != nil { - r.tracer.write(diagnostics.Module_resolution_using_rootDirs_has_failed.Format()) - } - } - return continueSearching() -} - -func (r *resolutionState) nodeLoadModuleByRelativeName(extensions extensions, candidate string, onlyRecordFailures bool, considerPackageJson bool) *resolved { - if r.tracer != nil { - r.tracer.write(diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0_target_file_types_Colon_1.Format(candidate, extensions.String())) - } - if !tspath.HasTrailingDirectorySeparator(candidate) { - if !onlyRecordFailures { - parentOfCandidate := tspath.GetDirectoryPath(candidate) - if !r.resolver.host.FS().DirectoryExists(parentOfCandidate) { - if r.tracer != nil { - r.tracer.write(diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it.Format(parentOfCandidate)) - } - onlyRecordFailures = true - } - } - resolvedFromFile := r.loadModuleFromFile(extensions, candidate, onlyRecordFailures) - if resolvedFromFile != nil { - if considerPackageJson { - if packageDirectory := ParseNodeModuleFromPath(resolvedFromFile.path /*isFolder*/, false); packageDirectory != "" { - resolvedFromFile.packageId = r.getPackageId(resolvedFromFile.path, r.getPackageJsonInfo(packageDirectory /*onlyRecordFailures*/, false)) - } - } - return resolvedFromFile - } - } - if !onlyRecordFailures { - candidateExists := r.resolver.host.FS().DirectoryExists(candidate) - if !candidateExists { - if r.tracer != nil { - r.tracer.write(diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it.Format(candidate)) - } - onlyRecordFailures = true - } - } - // esm mode relative imports shouldn't do any directory lookups (either inside `package.json` - // files or implicit `index.js`es). This is a notable departure from cjs norms, where `./foo/pkg` - // could have been redirected by `./foo/pkg/package.json` to an arbitrary location! - if !r.esmMode { - return r.loadNodeModuleFromDirectory(extensions, candidate, onlyRecordFailures, considerPackageJson) - } - return continueSearching() -} - -func (r *resolutionState) loadModuleFromFile(extensions extensions, candidate string, onlyRecordFailures bool) *resolved { - // ./foo.js -> ./foo.ts - resolvedByReplacingExtension := r.loadModuleFromFileNoImplicitExtensions(extensions, candidate, onlyRecordFailures) - if resolvedByReplacingExtension != nil { - return resolvedByReplacingExtension - } - - // ./foo -> ./foo.ts - if !r.esmMode { - return r.tryAddingExtensions(candidate, extensions, "", onlyRecordFailures) - } - - return continueSearching() -} - -func (r *resolutionState) loadModuleFromFileNoImplicitExtensions(extensions extensions, candidate string, onlyRecordFailures bool) *resolved { - base := tspath.GetBaseFileName(candidate) - if !strings.Contains(base, ".") { - return continueSearching() // extensionless import, no lookups performed, since we don't support extensionless files - } - extensionless := tspath.RemoveFileExtension(candidate) - if extensionless == candidate { - // Once TS native extensions are handled, handle arbitrary extensions for declaration file mapping - extensionless = candidate[:strings.LastIndex(candidate, ".")] - } - - extension := candidate[len(extensionless):] - if r.tracer != nil { - r.tracer.write(diagnostics.File_name_0_has_a_1_extension_stripping_it.Format(candidate, extension)) - } - return r.tryAddingExtensions(extensionless, extensions, extension, onlyRecordFailures) -} - -func (r *resolutionState) tryAddingExtensions(extensionless string, extensions extensions, originalExtension string, onlyRecordFailures bool) *resolved { - if !onlyRecordFailures { - directory := tspath.GetDirectoryPath(extensionless) - onlyRecordFailures = directory != "" && !r.resolver.host.FS().DirectoryExists(directory) - } - - switch originalExtension { - case tspath.ExtensionMjs, tspath.ExtensionMts, tspath.ExtensionDmts: - if extensions&extensionsTypeScript != 0 { - if resolved := r.tryExtension(tspath.ExtensionMts, extensionless, originalExtension == tspath.ExtensionMts || originalExtension == tspath.ExtensionDmts, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if extensions&extensionsDeclaration != 0 { - if resolved := r.tryExtension(tspath.ExtensionDmts, extensionless, originalExtension == tspath.ExtensionMts || originalExtension == tspath.ExtensionDmts, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if extensions&extensionsJavaScript != 0 { - if resolved := r.tryExtension(tspath.ExtensionMjs, extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - return continueSearching() - case tspath.ExtensionCjs, tspath.ExtensionCts, tspath.ExtensionDcts: - if extensions&extensionsTypeScript != 0 { - if resolved := r.tryExtension(tspath.ExtensionCts, extensionless, originalExtension == tspath.ExtensionCts || originalExtension == tspath.ExtensionDcts, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if extensions&extensionsDeclaration != 0 { - if resolved := r.tryExtension(tspath.ExtensionDcts, extensionless, originalExtension == tspath.ExtensionCts || originalExtension == tspath.ExtensionDcts, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if extensions&extensionsJavaScript != 0 { - if resolved := r.tryExtension(tspath.ExtensionCjs, extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - return continueSearching() - case tspath.ExtensionJson: - if extensions&extensionsDeclaration != 0 { - if resolved := r.tryExtension(".d.json.ts", extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if extensions&extensionsJson != 0 { - if resolved := r.tryExtension(tspath.ExtensionJson, extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - return continueSearching() - case tspath.ExtensionTsx, tspath.ExtensionJsx: - // basically idendical to the ts/js case below, but prefers matching tsx and jsx files exactly before falling back to the ts or js file path - // (historically, we disallow having both a a.ts and a.tsx file in the same compilation, since their outputs clash) - // TODO: We should probably error if `"./a.tsx"` resolved to `"./a.ts"`, right? - if extensions&extensionsTypeScript != 0 { - if resolved := r.tryExtension(tspath.ExtensionTsx, extensionless, originalExtension == tspath.ExtensionTsx, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - if resolved := r.tryExtension(tspath.ExtensionTs, extensionless, originalExtension == tspath.ExtensionTsx, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if extensions&extensionsDeclaration != 0 { - if resolved := r.tryExtension(tspath.ExtensionDts, extensionless, originalExtension == tspath.ExtensionTsx, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if extensions&extensionsJavaScript != 0 { - if resolved := r.tryExtension(tspath.ExtensionJsx, extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - if resolved := r.tryExtension(tspath.ExtensionJs, extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - return continueSearching() - case tspath.ExtensionTs, tspath.ExtensionDts, tspath.ExtensionJs, "": - if extensions&extensionsTypeScript != 0 { - if resolved := r.tryExtension(tspath.ExtensionTs, extensionless, originalExtension == tspath.ExtensionTs || originalExtension == tspath.ExtensionDts, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - if resolved := r.tryExtension(tspath.ExtensionTsx, extensionless, originalExtension == tspath.ExtensionTs || originalExtension == tspath.ExtensionDts, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if extensions&extensionsDeclaration != 0 { - if resolved := r.tryExtension(tspath.ExtensionDts, extensionless, originalExtension == tspath.ExtensionTs || originalExtension == tspath.ExtensionDts, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if extensions&extensionsJavaScript != 0 { - if resolved := r.tryExtension(tspath.ExtensionJs, extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - if resolved := r.tryExtension(tspath.ExtensionJsx, extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - if r.isConfigLookup { - if resolved := r.tryExtension(tspath.ExtensionJson, extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - return continueSearching() - default: - if extensions&extensionsDeclaration != 0 && !tspath.IsDeclarationFileName(extensionless+originalExtension) { - if resolved := r.tryExtension(".d"+originalExtension+".ts", extensionless, false, onlyRecordFailures); !resolved.shouldContinueSearching() { - return resolved - } - } - return continueSearching() - } -} - -func (r *resolutionState) tryExtension(extension string, extensionless string, resolvedUsingTsExtension bool, onlyRecordFailures bool) *resolved { - fileName := extensionless + extension - if path, ok := r.tryFile(fileName, onlyRecordFailures); ok { - return &resolved{ - path: path, - extension: extension, - resolvedUsingTsExtension: !r.candidateIsFromPackageJsonField && resolvedUsingTsExtension, - } - } - return continueSearching() -} - -func (r *resolutionState) tryFile(fileName string, onlyRecordFailures bool) (string, bool) { - if len(r.compilerOptions.ModuleSuffixes) == 0 { - return fileName, r.tryFileLookup(fileName, onlyRecordFailures) - } - - ext := tspath.TryGetExtensionFromPath(fileName) - fileNameNoExtension := tspath.RemoveExtension(fileName, ext) - for _, suffix := range r.compilerOptions.ModuleSuffixes { - path := fileNameNoExtension + suffix + ext - if r.tryFileLookup(path, onlyRecordFailures) { - return path, true - } - } - return fileName, false -} - -func (r *resolutionState) tryFileLookup(fileName string, onlyRecordFailures bool) bool { - if !onlyRecordFailures { - if r.resolver.host.FS().FileExists(fileName) { - if r.tracer != nil { - r.tracer.write(diagnostics.File_0_exists_use_it_as_a_name_resolution_result.Format(fileName)) - } - return true - } else if r.tracer != nil { - r.tracer.write(diagnostics.File_0_does_not_exist.Format(fileName)) - } - } - r.failedLookupLocations = append(r.failedLookupLocations, fileName) - return false -} - -func (r *resolutionState) loadNodeModuleFromDirectory(extensions extensions, candidate string, onlyRecordFailures bool, considerPackageJson bool) *resolved { - var packageInfo *packagejson.InfoCacheEntry - if considerPackageJson { - packageInfo = r.getPackageJsonInfo(candidate, onlyRecordFailures) - } - - return r.loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, packageInfo) -} - -func (r *resolutionState) loadNodeModuleFromDirectoryWorker(ext extensions, candidate string, onlyRecordFailures bool, packageInfo *packagejson.InfoCacheEntry) *resolved { - var ( - packageFile string - onlyRecordFailuresForPackageFile bool - versionPaths packagejson.VersionPaths - ) - if packageInfo.Exists() { - versionPaths = packageInfo.Contents.GetVersionPaths(r.getTraceFunc()) - if tspath.ComparePaths(candidate, packageInfo.PackageDirectory, tspath.ComparePathsOptions{UseCaseSensitiveFileNames: r.resolver.host.FS().UseCaseSensitiveFileNames()}) == 0 { - if file, ok := r.getPackageFile(ext, packageInfo); ok { - packageFile = file - onlyRecordFailuresForPackageFile = !r.resolver.host.FS().DirectoryExists(tspath.GetDirectoryPath(file)) - } - } - } - - loader := func(extensions extensions, candidate string, onlyRecordFailures bool) *resolved { - if fromFile := r.loadFileNameFromPackageJSONField(extensions, candidate, packageFile, onlyRecordFailures); !fromFile.shouldContinueSearching() { - return fromFile - } - - // Even if `extensions == extensionsDeclaration`, we can still look up a .ts file as a result of package.json "types" - // !!! should we not set this before the filename lookup above? - expandedExtensions := extensions - if extensions == extensionsDeclaration { - expandedExtensions = extensionsTypeScript | extensionsDeclaration - } - - // Disable `esmMode` for the resolution of the package path for CJS-mode packages (so the `main` field can omit extensions) - saveESMMode := r.esmMode - saveCandidateIsFromPackageJsonField := r.candidateIsFromPackageJsonField - r.candidateIsFromPackageJsonField = true - if packageInfo.Exists() && packageInfo.Contents.Type.Value != "module" { - r.esmMode = false - } - result := r.nodeLoadModuleByRelativeName(expandedExtensions, candidate, onlyRecordFailures, false /*considerPackageJson*/) - r.esmMode = saveESMMode - r.candidateIsFromPackageJsonField = saveCandidateIsFromPackageJsonField - return result - } - - var indexPath string - if r.isConfigLookup { - indexPath = tspath.CombinePaths(candidate, "tsconfig") - } else { - indexPath = tspath.CombinePaths(candidate, "index") - } - - if versionPaths.Exists() && (packageFile == "" || tspath.ContainsPath(candidate, packageFile, tspath.ComparePathsOptions{})) { - var moduleName string - if packageFile != "" { - moduleName = tspath.GetRelativePathFromDirectory(candidate, packageFile, tspath.ComparePathsOptions{}) - } else { - moduleName = tspath.GetRelativePathFromDirectory(candidate, indexPath, tspath.ComparePathsOptions{}) - } - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2.Format(versionPaths.Version, core.Version(), moduleName)) - } - pathPatterns := TryParsePatterns(versionPaths.GetPaths()) - if result := r.tryLoadModuleUsingPaths(ext, moduleName, candidate, versionPaths.GetPaths(), pathPatterns, loader, onlyRecordFailuresForPackageFile); !result.shouldContinueSearching() { - if result.packageId.Name != "" { - // !!! are these asserts really necessary? - panic("expected packageId to be empty") - } - return result - } - } - - if packageFile != "" { - if packageFileResult := loader(ext, packageFile, onlyRecordFailuresForPackageFile); !packageFileResult.shouldContinueSearching() { - if packageFileResult.packageId.Name != "" { - // !!! are these asserts really necessary? - panic("expected packageId to be empty") - } - return packageFileResult - } - } - - // ESM mode resolutions don't do package 'index' lookups - if !r.esmMode { - return r.loadModuleFromFile(ext, indexPath, onlyRecordFailures || !r.resolver.host.FS().DirectoryExists(candidate)) - } - return continueSearching() -} - -// This function is only ever called with paths written in package.json files - never -// module specifiers written in source files - and so it always allows the -// candidate to end with a TS extension (but will also try substituting a JS extension for a TS extension). -func (r *resolutionState) loadFileNameFromPackageJSONField(extensions extensions, candidate string, packageJSONValue string, onlyRecordFailures bool) *resolved { - if extensions&extensionsTypeScript != 0 && tspath.HasImplementationTSFileExtension(candidate) || extensions&extensionsDeclaration != 0 && tspath.IsDeclarationFileName(candidate) { - if path, ok := r.tryFile(candidate, onlyRecordFailures); ok { - extension := tspath.TryExtractTSExtension(path) - return &resolved{ - path: path, - extension: extension, - resolvedUsingTsExtension: packageJSONValue != "" && !strings.HasSuffix(packageJSONValue, extension), - } - } - return continueSearching() - } - - if r.isConfigLookup && extensions&extensionsJson != 0 && tspath.FileExtensionIs(candidate, tspath.ExtensionJson) { - if path, ok := r.tryFile(candidate, onlyRecordFailures); ok { - return &resolved{ - path: path, - extension: tspath.ExtensionJson, - } - } - } - - return r.loadModuleFromFileNoImplicitExtensions(extensions, candidate, onlyRecordFailures) -} - -func (r *resolutionState) getPackageFile(extensions extensions, packageInfo *packagejson.InfoCacheEntry) (string, bool) { - if !packageInfo.Exists() { - return "", false - } - if r.isConfigLookup { - return r.getPackageJSONPathField("tsconfig", &packageInfo.Contents.TSConfig, packageInfo.PackageDirectory) - } - if extensions&extensionsDeclaration != 0 { - if packageFile, ok := r.getPackageJSONPathField("typings", &packageInfo.Contents.Typings, packageInfo.PackageDirectory); ok { - return packageFile, ok - } - if packageFile, ok := r.getPackageJSONPathField("types", &packageInfo.Contents.Types, packageInfo.PackageDirectory); ok { - return packageFile, ok - } - } - if extensions&(extensionsImplementationFiles|extensionsDeclaration) != 0 { - return r.getPackageJSONPathField("main", &packageInfo.Contents.Main, packageInfo.PackageDirectory) - } - return "", false -} - -func (r *resolutionState) getPackageJsonInfo(packageDirectory string, onlyRecordFailures bool) *packagejson.InfoCacheEntry { - packageJsonPath := tspath.CombinePaths(packageDirectory, "package.json") - if onlyRecordFailures { - r.failedLookupLocations = append(r.failedLookupLocations, packageJsonPath) - return nil - } - - if existing := r.resolver.packageJsonInfoCache.Get(packageJsonPath); existing != nil { - if existing.Contents != nil { - if r.tracer != nil { - r.tracer.write(diagnostics.File_0_exists_according_to_earlier_cached_lookups.Format(packageJsonPath)) - } - r.affectingLocations = append(r.affectingLocations, packageJsonPath) - if existing.PackageDirectory == packageDirectory { - return existing - } - // https://github.com/microsoft/TypeScript/pull/50740 - return &packagejson.InfoCacheEntry{ - PackageDirectory: packageDirectory, - DirectoryExists: true, - Contents: existing.Contents, - } - } else { - if existing.DirectoryExists && r.tracer != nil { - r.tracer.write(diagnostics.File_0_does_not_exist_according_to_earlier_cached_lookups.Format(packageJsonPath)) - } - r.failedLookupLocations = append(r.failedLookupLocations, packageJsonPath) - return nil - } - } - - directoryExists := r.resolver.host.FS().DirectoryExists(packageDirectory) - if directoryExists && r.resolver.host.FS().FileExists(packageJsonPath) { - // Ignore error - contents, _ := r.resolver.host.FS().ReadFile(packageJsonPath) - packageJsonContent, _ := packagejson.Parse([]byte(contents)) - if r.tracer != nil { - r.tracer.write(diagnostics.Found_package_json_at_0.Format(packageJsonPath)) - } - result := &packagejson.InfoCacheEntry{ - PackageDirectory: packageDirectory, - DirectoryExists: true, - Contents: &packagejson.PackageJson{ - Fields: packageJsonContent, - }, - } - result = r.resolver.packageJsonInfoCache.Set(packageJsonPath, result) - r.affectingLocations = append(r.affectingLocations, packageJsonPath) - return result - } else { - if directoryExists && r.tracer != nil { - r.tracer.write(diagnostics.File_0_does_not_exist.Format(packageJsonPath)) - } - _ = r.resolver.packageJsonInfoCache.Set(packageJsonPath, &packagejson.InfoCacheEntry{ - PackageDirectory: packageDirectory, - DirectoryExists: directoryExists, - }) - r.failedLookupLocations = append(r.failedLookupLocations, packageJsonPath) - } - return nil -} - -func (r *resolutionState) getPackageId(resolvedFileName string, packageInfo *packagejson.InfoCacheEntry) PackageId { - if packageInfo.Exists() { - packageJsonContent := packageInfo.Contents - if name, ok := packageJsonContent.Name.GetValue(); ok { - if version, ok := packageJsonContent.Version.GetValue(); ok { - var subModuleName string - if len(resolvedFileName) > len(packageInfo.PackageDirectory) { - subModuleName = resolvedFileName[len(packageInfo.PackageDirectory)+1:] - } - return PackageId{ - Name: name, - Version: version, - SubModuleName: subModuleName, - PeerDependencies: r.readPackageJsonPeerDependencies(packageInfo), - } - } - } - } - return PackageId{} -} - -func (r *resolutionState) readPackageJsonPeerDependencies(packageJsonInfo *packagejson.InfoCacheEntry) string { - peerDependencies := packageJsonInfo.Contents.PeerDependencies - ok := r.validatePackageJSONField("peerDependencies", &peerDependencies) - if !ok || len(peerDependencies.Value) == 0 { - return "" - } - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_has_a_peerDependencies_field.Message()) - } - packageDirectory := r.realPath(packageJsonInfo.PackageDirectory) - nodeModulesIndex := strings.LastIndex(packageDirectory, "/node_modules") - if nodeModulesIndex == -1 { - return "" - } - nodeModules := packageDirectory[:nodeModulesIndex+len("/node_modules")] + "/" - builder := strings.Builder{} - for name := range peerDependencies.Value { - peerPackageJson := r.getPackageJsonInfo(nodeModules+name /*onlyRecordFailures*/, false) - if peerPackageJson != nil { - version := peerPackageJson.Contents.Version.Value - builder.WriteString("+") - builder.WriteString(name) - builder.WriteString("@") - builder.WriteString(version) - if r.tracer != nil { - r.tracer.write(diagnostics.Found_peerDependency_0_with_1_version.Format(name, version)) - } - } else if r.tracer != nil { - r.tracer.write(diagnostics.Failed_to_find_peerDependency_0.Format(name)) - } - } - return builder.String() -} - -func (r *resolutionState) realPath(path string) string { - rp := tspath.NormalizePath(r.resolver.host.FS().Realpath(path)) - if r.tracer != nil { - r.tracer.write(diagnostics.Resolving_real_path_for_0_result_1.Format(path, rp)) - } - return rp -} - -func (r *resolutionState) validatePackageJSONField(fieldName string, field packagejson.TypeValidatedField) bool { - if field.IsPresent() { - if field.IsValid() { - return true - } - if r.tracer != nil { - r.tracer.write(diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2.Format(fieldName, field.ExpectedJSONType(), field.ActualJSONType())) - } - } - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_does_not_have_a_0_field.Format(fieldName)) - } - return false -} - -func (r *resolutionState) getPackageJSONPathField(fieldName string, field *packagejson.Expected[string], directory string) (string, bool) { - if !r.validatePackageJSONField(fieldName, field) { - return "", false - } - if field.Value == "" { - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_had_a_falsy_0_field.Format(fieldName)) - } - return "", false - } - path := tspath.NormalizePath(tspath.CombinePaths(directory, field.Value)) - if r.tracer != nil { - r.tracer.write(diagnostics.X_package_json_has_0_field_1_that_references_2.Format(fieldName, field.Value, path)) - } - return path, true -} - -func (r *resolutionState) conditionMatches(condition string) bool { - if condition == "default" || slices.Contains(r.conditions, condition) { - return true - } - if !slices.Contains(r.conditions, "types") { - return false // only apply versioned types conditions if the types condition is applied - } - if !strings.HasPrefix(condition, "types@") { - return false - } - if versionRange, ok := semver.TryParseVersionRange(condition[len("types@"):]); ok { - return versionRange.Test(&typeScriptVersion) - } - return false -} - -func (r *resolutionState) getTraceFunc() func(string) { - if r.tracer != nil { - return r.tracer.write - } - return nil -} - -func GetConditions(options *core.CompilerOptions, resolutionMode core.ResolutionMode) []string { - moduleResolution := options.GetModuleResolutionKind() - if resolutionMode == core.ModuleKindNone && moduleResolution == core.ModuleResolutionKindBundler { - resolutionMode = core.ModuleKindESNext - } - conditions := make([]string, 0, 3+len(options.CustomConditions)) - if resolutionMode == core.ModuleKindESNext { - conditions = append(conditions, "import") - } else { - conditions = append(conditions, "require") - } - - if options.NoDtsResolution != core.TSTrue { - conditions = append(conditions, "types") - } - if moduleResolution != core.ModuleResolutionKindBundler { - conditions = append(conditions, "node") - } - conditions = core.Concatenate(conditions, options.CustomConditions) - return conditions -} - -func getNodeResolutionFeatures(options *core.CompilerOptions) NodeResolutionFeatures { - features := NodeResolutionFeaturesNone - - switch options.GetModuleResolutionKind() { - case core.ModuleResolutionKindNode16: - features = NodeResolutionFeaturesNode16Default - case core.ModuleResolutionKindNodeNext: - features = NodeResolutionFeaturesNodeNextDefault - case core.ModuleResolutionKindBundler: - features = NodeResolutionFeaturesBundlerDefault - } - if options.ResolvePackageJsonExports == core.TSTrue { - features |= NodeResolutionFeaturesExports - } else if options.ResolvePackageJsonExports == core.TSFalse { - features &^= NodeResolutionFeaturesExports - } - if options.ResolvePackageJsonImports == core.TSTrue { - features |= NodeResolutionFeaturesImports - } else if options.ResolvePackageJsonImports == core.TSFalse { - features &^= NodeResolutionFeaturesImports - } - return features -} - -func moveToNextDirectorySeparatorIfAvailable(path string, prevSeparatorIndex int, isFolder bool) int { - offset := prevSeparatorIndex + 1 - nextSeparatorIndex := strings.Index(path[offset:], "/") - if nextSeparatorIndex == -1 { - if isFolder { - return len(path) - } - return prevSeparatorIndex - } - return nextSeparatorIndex + offset -} - -type ParsedPatterns struct { - matchableStringSet collections.Set[string] - patterns []core.Pattern -} - -func (r *Resolver) getParsedPatternsForPaths() *ParsedPatterns { - r.parsedPatternsForPathsOnce.Do(func() { - r.parsedPatternsForPaths = TryParsePatterns(r.compilerOptions.Paths) - }) - return r.parsedPatternsForPaths -} - -func TryParsePatterns(pathMappings *collections.OrderedMap[string, []string]) *ParsedPatterns { - paths := pathMappings.Keys() - - numPatterns := 0 - for path := range paths { - if pattern := core.TryParsePattern(path); pattern.IsValid() && pattern.StarIndex == -1 { - numPatterns++ - } - } - numMatchables := pathMappings.Size() - numPatterns - - var patterns []core.Pattern - var matchableStringSet collections.Set[string] - if numPatterns != 0 { - patterns = make([]core.Pattern, 0, numPatterns) - } - if numMatchables != 0 { - matchableStringSet = *collections.NewSetWithSizeHint[string](numMatchables) - } - - for path := range paths { - if pattern := core.TryParsePattern(path); pattern.IsValid() { - if pattern.StarIndex == -1 { - matchableStringSet.Add(path) - } else { - patterns = append(patterns, pattern) - } - } - } - return &ParsedPatterns{ - matchableStringSet: matchableStringSet, - patterns: patterns, - } -} - -func MatchPatternOrExact(patterns *ParsedPatterns, candidate string) core.Pattern { - if patterns.matchableStringSet.Has(candidate) { - return core.Pattern{ - Text: candidate, - StarIndex: -1, - } - } - if len(patterns.patterns) == 0 { - return core.Pattern{} - } - return core.FindBestPatternMatch(patterns.patterns, core.Identity, candidate) -} - -// If you import from "." inside a containing directory "/foo", the result of `tspath.NormalizePath` -// would be "/foo", but this loses the information that `foo` is a directory and we intended -// to look inside of it. The Node CommonJS resolution algorithm doesn't call this out -// (https://nodejs.org/api/modules.html#all-together), but it seems that module paths ending -// in `.` are actually normalized to `./` before proceeding with the resolution algorithm. -func normalizePathForCJSResolution(containingDirectory string, moduleName string) string { - combined := tspath.CombinePaths(containingDirectory, moduleName) - parts := tspath.GetPathComponents(combined, "") - lastPart := parts[len(parts)-1] - if lastPart == "." || lastPart == ".." { - return tspath.EnsureTrailingDirectorySeparator(tspath.NormalizePath(combined)) - } - return tspath.NormalizePath(combined) -} - -func matchesPatternWithTrailer(target string, name string) bool { - if strings.HasSuffix(target, "*") { - return false - } - starPos := strings.Index(target, "*") - if starPos == -1 { - return false - } - return strings.HasPrefix(name, target[:starPos]) && strings.HasSuffix(name, target[starPos+1:]) -} - -/** True if `extension` is one of the supported `extensions`. */ -func extensionIsOk(extensions extensions, extension string) bool { - return (extensions&extensionsJavaScript != 0 && (extension == tspath.ExtensionJs || extension == tspath.ExtensionJsx || extension == tspath.ExtensionMjs || extension == tspath.ExtensionCjs) || - (extensions&extensionsTypeScript != 0 && (extension == tspath.ExtensionTs || extension == tspath.ExtensionTsx || extension == tspath.ExtensionMts || extension == tspath.ExtensionCts)) || - (extensions&extensionsDeclaration != 0 && (extension == tspath.ExtensionDts || extension == tspath.ExtensionDmts || extension == tspath.ExtensionDcts)) || - (extensions&extensionsJson != 0 && extension == tspath.ExtensionJson)) -} - -func ResolveConfig(moduleName string, containingFile string, host ResolutionHost) *ResolvedModule { - resolver := NewResolver(host, &core.CompilerOptions{ModuleResolution: core.ModuleResolutionKindNodeNext}, "", "") - return resolver.resolveConfig(moduleName, containingFile) -} - -func GetAutomaticTypeDirectiveNames(options *core.CompilerOptions, host ResolutionHost) []string { - if options.Types != nil { - return options.Types - } - - var result []string - typeRoots, _ := options.GetEffectiveTypeRoots(host.GetCurrentDirectory()) - for _, root := range typeRoots { - if host.FS().DirectoryExists(root) { - for _, typeDirectivePath := range host.FS().GetAccessibleEntries(root).Directories { - normalized := tspath.NormalizePath(typeDirectivePath) - packageJsonPath := tspath.CombinePaths(root, normalized, "package.json") - isNotNeededPackage := false - if host.FS().FileExists(packageJsonPath) { - contents, _ := host.FS().ReadFile(packageJsonPath) - packageJsonContent, _ := packagejson.Parse([]byte(contents)) - // `types-publisher` sometimes creates packages with `"typings": null` for packages that don't provide their own types. - // See `createNotNeededPackageJSON` in the types-publisher` repo. - isNotNeededPackage = packageJsonContent.Typings.Null - } - if !isNotNeededPackage { - baseFileName := tspath.GetBaseFileName(normalized) - if !strings.HasPrefix(baseFileName, ".") { - result = append(result, baseFileName) - } - } - } - } - } - return result -} diff --git a/kitcom/internal/tsgo/module/resolver_test.go b/kitcom/internal/tsgo/module/resolver_test.go deleted file mode 100644 index e5f4396..0000000 --- a/kitcom/internal/tsgo/module/resolver_test.go +++ /dev/null @@ -1,398 +0,0 @@ -package module_test - -import ( - "io" - "os" - "path/filepath" - "regexp" - "slices" - "sync" - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsonutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/baseline" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/vfstest" - "github.com/go-json-experiment/json" - "github.com/go-json-experiment/json/jsontext" - "gotest.tools/v3/assert" - "gotest.tools/v3/assert/cmp" -) - -var skip = []string{ - "allowJsCrossMonorepoPackage.ts", - "APILibCheck.ts", - "APISample_compile.ts", - "APISample_jsdoc.ts", - "APISample_linter.ts", - "APISample_parseConfig.ts", - "APISample_transform.ts", - "APISample_Watch.ts", - "APISample_watcher.ts", - "APISample_WatchWithDefaults.ts", - "APISample_WatchWithOwnWatchHost.ts", - "bundlerNodeModules1(module=esnext).ts", - "bundlerNodeModules1(module=preserve).ts", - "commonJsExportTypeDeclarationError.ts", - "commonSourceDir5.ts", - "commonSourceDirectory.ts", - "computedEnumMemberSyntacticallyString2(isolatedmodules=false).ts", - "computedEnumMemberSyntacticallyString2(isolatedmodules=true).ts", - "declarationEmitCommonSourceDirectoryDoesNotContainAllFiles.ts", - "declarationEmitForGlobalishSpecifierSymlink.ts", - "declarationEmitForGlobalishSpecifierSymlink2.ts", - "declarationEmitReexportedSymlinkReference.ts", - "declarationEmitReexportedSymlinkReference2.ts", - "declarationEmitReexportedSymlinkReference3.ts", - "declarationEmitSymlinkPaths.ts", - "decoratorMetadataTypeOnlyExport.ts", - "decoratorMetadataTypeOnlyImport.ts", - "enumNoInitializerFollowsNonLiteralInitializer.ts", - "enumWithNonLiteralStringInitializer.ts", - "es6ImportWithJsDocTags.ts", - "importAttributes9.ts", - "importSpecifiers_js.ts", - "importTag17.ts", - "isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=false,verbatimmodulesyntax=false).ts", - "isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=false,verbatimmodulesyntax=true).ts", - "isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=true,verbatimmodulesyntax=false).ts", - "isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=true,verbatimmodulesyntax=true).ts", - "jsDeclarationEmitExportedClassWithExtends.ts", - "jsxNamespaceGlobalReexport.tsx", - "jsxNamespaceGlobalReexportMissingAliasTarget.tsx", - "mergeSymbolReexportedTypeAliasInstantiation.ts", - "mergeSymbolReexportInterface.ts", - "mergeSymbolRexportFunction.ts", - "missingMemberErrorHasShortPath.ts", - "moduleResolutionAsTypeReferenceDirective.ts", - "moduleResolutionAsTypeReferenceDirectiveAmbient.ts", - "moduleResolutionAsTypeReferenceDirectiveScoped.ts", - "moduleResolutionWithSymlinks_notInNodeModules.ts", - "moduleResolutionWithSymlinks_preserveSymlinks.ts", - "moduleResolutionWithSymlinks_referenceTypes.ts", - "moduleResolutionWithSymlinks_withOutDir.ts", - "moduleResolutionWithSymlinks.ts", - "nodeAllowJsPackageSelfName(module=node16).ts", - "nodeAllowJsPackageSelfName(module=nodenext).ts", - "nodeAllowJsPackageSelfName2.ts", - "nodeModulesAllowJsConditionalPackageExports(module=node16).ts", - "nodeModulesAllowJsConditionalPackageExports(module=nodenext).ts", - "nodeModulesAllowJsPackageExports(module=node16).ts", - "nodeModulesAllowJsPackageExports(module=nodenext).ts", - "nodeModulesAllowJsPackageImports(module=node16).ts", - "nodeModulesAllowJsPackageImports(module=nodenext).ts", - "nodeModulesAllowJsPackagePatternExports(module=node16).ts", - "nodeModulesAllowJsPackagePatternExports(module=nodenext).ts", - "nodeModulesAllowJsPackagePatternExportsTrailers(module=node16).ts", - "nodeModulesAllowJsPackagePatternExportsTrailers(module=nodenext).ts", - "nodeModulesConditionalPackageExports(module=node16).ts", - "nodeModulesConditionalPackageExports(module=nodenext).ts", - "nodeModulesDeclarationEmitWithPackageExports(module=node16).ts", - "nodeModulesDeclarationEmitWithPackageExports(module=nodenext).ts", - "nodeModulesExportsBlocksTypesVersions(module=node16).ts", - "nodeModulesExportsBlocksTypesVersions(module=nodenext).ts", - "nodeModulesImportResolutionIntoExport(module=node16).ts", - "nodeModulesImportResolutionIntoExport(module=nodenext).ts", - "nodeModulesImportResolutionNoCycle(module=node16).ts", - "nodeModulesImportResolutionNoCycle(module=nodenext).ts", - "nodeModulesPackageExports(module=node16).ts", - "nodeModulesPackageExports(module=nodenext).ts", - "nodeModulesPackagePatternExports(module=node16).ts", - "nodeModulesPackagePatternExports(module=nodenext).ts", - "nodeModulesPackagePatternExportsExclude(module=node16).ts", - "nodeModulesPackagePatternExportsExclude(module=nodenext).ts", - "nodeModulesPackagePatternExportsTrailers(module=node16).ts", - "nodeModulesPackagePatternExportsTrailers(module=nodenext).ts", - "nodeNextImportModeImplicitIndexResolution.ts", - "nodeNextImportModeImplicitIndexResolution2.ts", - "nodeNextPackageImportMapRootDir.ts", - "nodeNextPackageSelfNameWithOutDir.ts", - "nodeNextPackageSelfNameWithOutDirDeclDir.ts", - "nodeNextPackageSelfNameWithOutDirDeclDirComposite.ts", - "nodeNextPackageSelfNameWithOutDirDeclDirCompositeNestedDirs.ts", - "nodeNextPackageSelfNameWithOutDirDeclDirNestedDirs.ts", - "nodeNextPackageSelfNameWithOutDirDeclDirRootDir.ts", - "nodeNextPackageSelfNameWithOutDirRootDir.ts", - "resolutionModeImportType1(moduleresolution=bundler).ts", - "resolutionModeImportType1(moduleresolution=node10).ts", - "resolutionModeTypeOnlyImport1(moduleresolution=bundler).ts", - "resolutionModeTypeOnlyImport1(moduleresolution=node10).ts", - "selfNameAndImportsEmitInclusion.ts", - "selfNameModuleAugmentation.ts", - "symbolLinkDeclarationEmitModuleNames.ts", - "symbolLinkDeclarationEmitModuleNamesImportRef.ts", - "symbolLinkDeclarationEmitModuleNamesRootDir.ts", - "symlinkedWorkspaceDependenciesNoDirectLinkGeneratesDeepNonrelativeName.ts", - "symlinkedWorkspaceDependenciesNoDirectLinkGeneratesNonrelativeName.ts", - "symlinkedWorkspaceDependenciesNoDirectLinkOptionalGeneratesNonrelativeName.ts", - "symlinkedWorkspaceDependenciesNoDirectLinkPeerGeneratesNonrelativeName.ts", - "typeGuardNarrowsIndexedAccessOfKnownProperty8.ts", - "typesVersions.ambientModules.ts", - "typesVersions.multiFile.ts", - "typesVersionsDeclarationEmit.ambient.ts", - "typesVersionsDeclarationEmit.multiFile.ts", - "typesVersionsDeclarationEmit.multiFileBackReferenceToSelf.ts", - "typesVersionsDeclarationEmit.multiFileBackReferenceToUnmapped.ts", -} - -type vfsModuleResolutionHost struct { - mu sync.Mutex - fs vfs.FS - currentDirectory string - traces []string -} - -func fixRoot(path string) string { - rootLength := tspath.GetRootLength(path) - if rootLength == 0 { - return tspath.CombinePaths("/.src", path) - } - return path -} - -func newVFSModuleResolutionHost(files map[string]string, currentDirectory string) *vfsModuleResolutionHost { - fs := make(map[string]string, len(files)) - for name, content := range files { - fs[fixRoot(name)] = content - } - if currentDirectory == "" { - currentDirectory = "/.src" - } else if currentDirectory[0] != '/' { - currentDirectory = "/.src/" + currentDirectory - } - return &vfsModuleResolutionHost{ - fs: vfstest.FromMap(fs, true /*useCaseSensitiveFileNames*/), - currentDirectory: currentDirectory, - } -} - -func (v *vfsModuleResolutionHost) FS() vfs.FS { - return v.fs -} - -// GetCurrentDirectory implements ModuleResolutionHost. -func (v *vfsModuleResolutionHost) GetCurrentDirectory() string { - return v.currentDirectory -} - -// Trace implements ModuleResolutionHost. -func (v *vfsModuleResolutionHost) Trace(msg string) { - v.mu.Lock() - defer v.mu.Unlock() - v.traces = append(v.traces, msg) -} - -type functionCall struct { - call string - args rawArgs - returnValue map[string]any -} -type traceTestCase struct { - name string - currentDirectory string - trace bool - compilerOptions *core.CompilerOptions - files map[string]string - calls []functionCall -} -type rawFile struct { - Name string `json:"name"` - Content string `json:"content"` -} -type rawArgs struct { - // getPackageScopeForPath - Directory string `json:"directory"` - - // resolveModuleName, resolveTypeReferenceDirective - Name string `json:"name"` - ContainingFile string `json:"containingFile"` - CompilerOptions *core.CompilerOptions `json:"compilerOptions"` - ResolutionMode int `json:"resolutionMode"` - RedirectedRef *struct { - SourceFile struct { - FileName string `json:"fileName"` - } `json:"sourceFile"` - CommandLine struct { - CompilerOptions *core.CompilerOptions `json:"options"` - } `json:"commandLine"` - } `json:"redirectedReference"` -} -type rawTest struct { - Test string `json:"test"` - CurrentDirectory string `json:"currentDirectory"` - Trace bool `json:"trace"` - Files []rawFile `json:"files"` - Call string `json:"call"` - Args rawArgs `json:"args"` - Return map[string]any `json:"return"` -} - -var typesVersionsMessageRegex = regexp.MustCompile(`that matches compiler version '[^']+'`) - -func sanitizeTraceOutput(trace string) string { - return typesVersionsMessageRegex.ReplaceAllString(trace, "that matches compiler version '3.1.0-dev'") -} - -type RedirectRef struct { - fileName string - options *core.CompilerOptions -} - -func (r *RedirectRef) ConfigName() string { - return r.fileName -} - -func (r *RedirectRef) CompilerOptions() *core.CompilerOptions { - return r.options -} - -var _ module.ResolvedProjectReference = (*RedirectRef)(nil) - -func doCall(t *testing.T, resolver *module.Resolver, call functionCall, skipLocations bool) { - switch call.call { - case "resolveModuleName", "resolveTypeReferenceDirective": - var redirectedReference module.ResolvedProjectReference - if call.args.RedirectedRef != nil { - redirectedReference = &RedirectRef{ - fileName: call.args.RedirectedRef.SourceFile.FileName, - options: call.args.RedirectedRef.CommandLine.CompilerOptions, - } - } - - errorMessageArgs := []any{call.args.Name, call.args.ContainingFile} - if call.call == "resolveModuleName" { - resolved, _ := resolver.ResolveModuleName(call.args.Name, call.args.ContainingFile, core.ModuleKind(call.args.ResolutionMode), redirectedReference) - assert.Check(t, resolved != nil, "ResolveModuleName should not return nil", errorMessageArgs) - if expectedResolvedModule, ok := call.returnValue["resolvedModule"].(map[string]any); ok { - assert.Check(t, resolved.IsResolved(), errorMessageArgs) - assert.Check(t, cmp.Equal(resolved.ResolvedFileName, expectedResolvedModule["resolvedFileName"].(string)), errorMessageArgs) - assert.Check(t, cmp.Equal(resolved.Extension, expectedResolvedModule["extension"].(string)), errorMessageArgs) - assert.Check(t, cmp.Equal(resolved.ResolvedUsingTsExtension, expectedResolvedModule["resolvedUsingTsExtension"].(bool)), errorMessageArgs) - assert.Check(t, cmp.Equal(resolved.IsExternalLibraryImport, expectedResolvedModule["isExternalLibraryImport"].(bool)), errorMessageArgs) - } else { - assert.Check(t, !resolved.IsResolved(), errorMessageArgs) - } - } else { - resolved, _ := resolver.ResolveTypeReferenceDirective(call.args.Name, call.args.ContainingFile, core.ModuleKind(call.args.ResolutionMode), redirectedReference) - assert.Check(t, resolved != nil, "ResolveTypeReferenceDirective should not return nil", errorMessageArgs) - if expectedResolvedTypeReferenceDirective, ok := call.returnValue["resolvedTypeReferenceDirective"].(map[string]any); ok { - assert.Check(t, resolved.IsResolved(), errorMessageArgs) - assert.Check(t, cmp.Equal(resolved.ResolvedFileName, expectedResolvedTypeReferenceDirective["resolvedFileName"].(string)), errorMessageArgs) - assert.Check(t, cmp.Equal(resolved.Primary, expectedResolvedTypeReferenceDirective["primary"].(bool)), errorMessageArgs) - assert.Check(t, cmp.Equal(resolved.IsExternalLibraryImport, expectedResolvedTypeReferenceDirective["isExternalLibraryImport"].(bool)), errorMessageArgs) - } else { - assert.Check(t, !resolved.IsResolved(), errorMessageArgs) - } - } - case "getPackageScopeForPath": - resolver.GetPackageScopeForPath(call.args.Directory) - default: - t.Errorf("Unexpected call: %s", call.call) - } -} - -func runTraceBaseline(t *testing.T, test traceTestCase) { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - host := newVFSModuleResolutionHost(test.files, test.currentDirectory) - resolver := module.NewResolver(host, test.compilerOptions, "", "") - - for _, call := range test.calls { - doCall(t, resolver, call, false /*skipLocations*/) - if t.Failed() { - t.FailNow() - } - } - - t.Run("concurrent", func(t *testing.T) { - concurrentHost := newVFSModuleResolutionHost(test.files, test.currentDirectory) - concurrentResolver := module.NewResolver(concurrentHost, test.compilerOptions, "", "") - - var wg sync.WaitGroup - for _, call := range test.calls { - wg.Add(1) - go func() { - defer wg.Done() - doCall(t, concurrentResolver, call, true /*skipLocations*/) - }() - } - - wg.Wait() - }) - - if test.trace { - t.Run("trace", func(t *testing.T) { - output, err := jsonutil.MarshalIndent(resolver, "", " ") - if err != nil { - t.Fatal(err) - } - baseline.Run( - t, - tspath.RemoveFileExtension(test.name)+".trace.json", - sanitizeTraceOutput(string(output)), - baseline.Options{Subfolder: "module/resolver"}, - ) - }) - } - }) -} - -func TestModuleResolver(t *testing.T) { - t.Parallel() - testsFilePath := filepath.Join(repo.TestDataPath, "fixtures", "module", "resolvertests.json") - // Read file one line at a time - file, err := os.Open(testsFilePath) - if err != nil { - t.Fatal(err) - } - t.Cleanup(func() { - file.Close() - }) - decoder := jsontext.NewDecoder(file) - var currentTestCase traceTestCase - for { - if decoder.PeekKind() == 0 { - _, err := decoder.ReadToken() - if err == io.EOF { //nolint:errorlint - break - } - t.Fatal(err) - } - - var testJSON rawTest - if err := json.UnmarshalDecode(decoder, &testJSON); err != nil { - t.Fatal(err) - } - if testJSON.Files != nil { - if currentTestCase.name != "" && !slices.Contains(skip, currentTestCase.name) { - runTraceBaseline(t, currentTestCase) - } - currentTestCase = traceTestCase{ - name: testJSON.Test, - currentDirectory: testJSON.CurrentDirectory, - // !!! no traces are passing yet because of missing cache implementation - trace: false, - files: make(map[string]string, len(testJSON.Files)), - } - for _, file := range testJSON.Files { - currentTestCase.files[file.Name] = file.Content - } - } else if testJSON.Call != "" { - currentTestCase.calls = append(currentTestCase.calls, functionCall{ - call: testJSON.Call, - args: testJSON.Args, - returnValue: testJSON.Return, - }) - if currentTestCase.compilerOptions == nil && testJSON.Args.CompilerOptions != nil { - currentTestCase.compilerOptions = testJSON.Args.CompilerOptions - } - } else { - t.Fatalf("Unexpected JSON: %v", testJSON) - } - } -} diff --git a/kitcom/internal/tsgo/module/types.go b/kitcom/internal/tsgo/module/types.go deleted file mode 100644 index 57a5d1e..0000000 --- a/kitcom/internal/tsgo/module/types.go +++ /dev/null @@ -1,147 +0,0 @@ -package module - -import ( - "fmt" - "math/bits" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" -) - -type ResolutionHost interface { - FS() vfs.FS - GetCurrentDirectory() string -} - -type ModeAwareCacheKey struct { - Name string - Mode core.ResolutionMode -} - -type ResolvedProjectReference interface { - ConfigName() string - CompilerOptions() *core.CompilerOptions -} - -type NodeResolutionFeatures int32 - -const ( - NodeResolutionFeaturesImports NodeResolutionFeatures = 1 << iota - NodeResolutionFeaturesSelfName - NodeResolutionFeaturesExports - NodeResolutionFeaturesExportsPatternTrailers - - NodeResolutionFeaturesNone NodeResolutionFeatures = 0 - NodeResolutionFeaturesAll = NodeResolutionFeaturesImports | NodeResolutionFeaturesSelfName | NodeResolutionFeaturesExports | NodeResolutionFeaturesExportsPatternTrailers - NodeResolutionFeaturesNode16Default = NodeResolutionFeaturesImports | NodeResolutionFeaturesSelfName | NodeResolutionFeaturesExports | NodeResolutionFeaturesExportsPatternTrailers - NodeResolutionFeaturesNodeNextDefault = NodeResolutionFeaturesAll - NodeResolutionFeaturesBundlerDefault = NodeResolutionFeaturesImports | NodeResolutionFeaturesSelfName | NodeResolutionFeaturesExports | NodeResolutionFeaturesExportsPatternTrailers -) - -type PackageId struct { - Name string - SubModuleName string - Version string - PeerDependencies string -} - -func (p *PackageId) String() string { - return fmt.Sprintf("%s@%s%s", p.PackageName(), p.Version, p.PeerDependencies) -} - -func (p *PackageId) PackageName() string { - if p.SubModuleName != "" { - return p.Name + "/" + p.SubModuleName - } - return p.Name -} - -type LookupLocations struct { - FailedLookupLocations []string - AffectingLocations []string - ResolutionDiagnostics []*ast.Diagnostic -} - -type ResolvedModule struct { - LookupLocations - ResolvedFileName string - OriginalPath string - Extension string - ResolvedUsingTsExtension bool - PackageId PackageId - IsExternalLibraryImport bool - AlternateResult string -} - -func (r *ResolvedModule) IsResolved() bool { - return r != nil && r.ResolvedFileName != "" -} - -func (r *ResolvedModule) GetLookupLocations() *LookupLocations { - return &r.LookupLocations -} - -type ResolvedTypeReferenceDirective struct { - LookupLocations - Primary bool - ResolvedFileName string - OriginalPath string - PackageId PackageId - IsExternalLibraryImport bool -} - -func (r *ResolvedTypeReferenceDirective) IsResolved() bool { - return r.ResolvedFileName != "" -} - -func (r *ResolvedTypeReferenceDirective) GetLookupLocations() *LookupLocations { - return &r.LookupLocations -} - -type extensions int32 - -const ( - extensionsTypeScript extensions = 1 << iota - extensionsJavaScript - extensionsDeclaration - extensionsJson - - extensionsImplementationFiles = extensionsTypeScript | extensionsJavaScript -) - -func (e extensions) String() string { - result := make([]string, 0, bits.OnesCount(uint(e))) - if e&extensionsTypeScript != 0 { - result = append(result, "TypeScript") - } - if e&extensionsJavaScript != 0 { - result = append(result, "JavaScript") - } - if e&extensionsDeclaration != 0 { - result = append(result, "Declaration") - } - if e&extensionsJson != 0 { - result = append(result, "JSON") - } - return strings.Join(result, ", ") -} - -func (e extensions) Array() []string { - result := []string{} - if e&extensionsTypeScript != 0 { - result = append(result, tspath.ExtensionTs, tspath.ExtensionTsx) - } - if e&extensionsJavaScript != 0 { - result = append(result, tspath.ExtensionJs, tspath.ExtensionJsx) - } - if e&extensionsDeclaration != 0 { - result = append(result, tspath.ExtensionDts) - } - if e&extensionsJson != 0 { - result = append(result, tspath.ExtensionJson) - } - return result -} diff --git a/kitcom/internal/tsgo/module/util.go b/kitcom/internal/tsgo/module/util.go deleted file mode 100644 index 6b9519c..0000000 --- a/kitcom/internal/tsgo/module/util.go +++ /dev/null @@ -1,155 +0,0 @@ -package module - -import ( - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/semver" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -var typeScriptVersion = semver.MustParse(core.Version()) - -const InferredTypesContainingFile = "__inferred type names__.ts" - -func ParseNodeModuleFromPath(resolved string, isFolder bool) string { - path := tspath.NormalizePath(resolved) - idx := strings.LastIndex(path, "/node_modules/") - if idx == -1 { - return "" - } - - indexAfterNodeModules := idx + len("/node_modules/") - indexAfterPackageName := moveToNextDirectorySeparatorIfAvailable(path, indexAfterNodeModules, isFolder) - if path[indexAfterNodeModules] == '@' { - indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName, isFolder) - } - return path[:indexAfterPackageName] -} - -func ParsePackageName(moduleName string) (packageName, rest string) { - idx := strings.Index(moduleName, "/") - if len(moduleName) > 0 && moduleName[0] == '@' { - offset := idx + 1 - idx = strings.Index(moduleName[offset:], "/") - if idx != -1 { - idx += offset - } - } - if idx == -1 { - return moduleName, "" - } - return moduleName[:idx], moduleName[idx+1:] -} - -func MangleScopedPackageName(packageName string) string { - if packageName[0] == '@' { - idx := strings.Index(packageName, "/") - if idx == -1 { - return packageName - } - return packageName[1:idx] + "__" + packageName[idx+1:] - } - return packageName -} - -func UnmangleScopedPackageName(packageName string) string { - idx := strings.Index(packageName, "__") - if idx != -1 { - return "@" + packageName[:idx] + "/" + packageName[idx+2:] - } - return packageName -} - -func GetTypesPackageName(packageName string) string { - return "@types/" + MangleScopedPackageName(packageName) -} - -func ComparePatternKeys(a, b string) int { - aPatternIndex := strings.Index(a, "*") - bPatternIndex := strings.Index(b, "*") - baseLenA := len(a) - if aPatternIndex != -1 { - baseLenA = aPatternIndex + 1 - } - baseLenB := len(b) - if bPatternIndex != -1 { - baseLenB = bPatternIndex + 1 - } - - if baseLenA > baseLenB { - return -1 - } - if baseLenB > baseLenA { - return 1 - } - if aPatternIndex == -1 { - return 1 - } - if bPatternIndex == -1 { - return -1 - } - if len(a) > len(b) { - return -1 - } - if len(b) > len(a) { - return 1 - } - return 0 -} - -// Returns a DiagnosticMessage if we won't include a resolved module due to its extension. -// The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to. -// This returns a diagnostic even if the module will be an untyped module. -func GetResolutionDiagnostic(options *core.CompilerOptions, resolvedModule *ResolvedModule, file *ast.SourceFile) *diagnostics.Message { - needJsx := func() *diagnostics.Message { - if options.Jsx != core.JsxEmitNone { - return nil - } - return diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set - } - - needAllowJs := func() *diagnostics.Message { - if options.GetAllowJS() || !options.NoImplicitAny.DefaultIfUnknown(options.Strict).IsTrue() { - return nil - } - return diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type - } - - needResolveJsonModule := func() *diagnostics.Message { - if options.GetResolveJsonModule() { - return nil - } - return diagnostics.Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used - } - - needAllowArbitraryExtensions := func() *diagnostics.Message { - if file.IsDeclarationFile || options.AllowArbitraryExtensions.IsTrue() { - return nil - } - return diagnostics.Module_0_was_resolved_to_1_but_allowArbitraryExtensions_is_not_set - } - - switch resolvedModule.Extension { - case tspath.ExtensionTs, tspath.ExtensionDts, - tspath.ExtensionMts, tspath.ExtensionDmts, - tspath.ExtensionCts, tspath.ExtensionDcts: - // These are always allowed. - return nil - case tspath.ExtensionTsx: - return needJsx() - case tspath.ExtensionJsx: - if message := needJsx(); message != nil { - return message - } - return needAllowJs() - case tspath.ExtensionJs, tspath.ExtensionMjs, tspath.ExtensionCjs: - return needAllowJs() - case tspath.ExtensionJson: - return needResolveJsonModule() - default: - return needAllowArbitraryExtensions() - } -} diff --git a/kitcom/internal/tsgo/nodebuilder/types.go b/kitcom/internal/tsgo/nodebuilder/types.go deleted file mode 100644 index 80aca25..0000000 --- a/kitcom/internal/tsgo/nodebuilder/types.go +++ /dev/null @@ -1,80 +0,0 @@ -// Exports interfaces and types defining the node builder - concrete implementations are on top of the checker, but these types and interfaces are used by the emit resolver in the printer -package nodebuilder - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers" -) - -// TODO: previously all symboltracker methods were optional, but now they're required. -type SymbolTracker interface { - GetModuleSpecifierGenerationHost() modulespecifiers.ModuleSpecifierGenerationHost - - TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool - ReportInaccessibleThisError() - ReportPrivateInBaseOfClassExpression(propertyName string) - ReportInaccessibleUniqueSymbolError() - ReportCyclicStructureError() - ReportLikelyUnsafeImportRequiredError(specifier string) - ReportTruncationError() - ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) - ReportNonSerializableProperty(propertyName string) - - ReportInferenceFallback(node *ast.Node) - PushErrorFallbackNode(node *ast.Node) - PopErrorFallbackNode() -} - -// NOTE: If modifying this enum, must modify `TypeFormatFlags` too! -type Flags int32 - -const ( - FlagsNone Flags = 0 - // Options - FlagsNoTruncation Flags = 1 << 0 - FlagsWriteArrayAsGenericType Flags = 1 << 1 - FlagsGenerateNamesForShadowedTypeParams Flags = 1 << 2 - FlagsUseStructuralFallback Flags = 1 << 3 - FlagsForbidIndexedAccessSymbolReferences Flags = 1 << 4 - FlagsWriteTypeArgumentsOfSignature Flags = 1 << 5 - FlagsUseFullyQualifiedType Flags = 1 << 6 - FlagsUseOnlyExternalAliasing Flags = 1 << 7 - FlagsSuppressAnyReturnType Flags = 1 << 8 - FlagsWriteTypeParametersInQualifiedName Flags = 1 << 9 - FlagsMultilineObjectLiterals Flags = 1 << 10 - FlagsWriteClassExpressionAsTypeLiteral Flags = 1 << 11 - FlagsUseTypeOfFunction Flags = 1 << 12 - FlagsOmitParameterModifiers Flags = 1 << 13 - FlagsUseAliasDefinedOutsideCurrentScope Flags = 1 << 14 - FlagsUseSingleQuotesForStringLiteralType Flags = 1 << 28 - FlagsNoTypeReduction Flags = 1 << 29 - FlagsOmitThisParameter Flags = 1 << 25 - FlagsWriteCallStyleSignature Flags = 1 << 27 - // Error handling - FlagsAllowThisInObjectLiteral Flags = 1 << 15 - FlagsAllowQualifiedNameInPlaceOfIdentifier Flags = 1 << 16 - FlagsAllowAnonymousIdentifier Flags = 1 << 17 - FlagsAllowEmptyUnionOrIntersection Flags = 1 << 18 - FlagsAllowEmptyTuple Flags = 1 << 19 - FlagsAllowUniqueESSymbolType Flags = 1 << 20 - FlagsAllowEmptyIndexInfoType Flags = 1 << 21 - // Errors (cont.) - FlagsAllowNodeModulesRelativePaths Flags = 1 << 26 - FlagsIgnoreErrors Flags = FlagsAllowThisInObjectLiteral | FlagsAllowQualifiedNameInPlaceOfIdentifier | FlagsAllowAnonymousIdentifier | FlagsAllowEmptyUnionOrIntersection | FlagsAllowEmptyTuple | FlagsAllowEmptyIndexInfoType | FlagsAllowNodeModulesRelativePaths - // State - FlagsInObjectTypeLiteral Flags = 1 << 22 - FlagsInTypeAlias Flags = 1 << 23 - FlagsInInitialEntityName Flags = 1 << 24 -) - -/** @internal */ - -type InternalFlags int32 - -const ( - InternalFlagsNone InternalFlags = 0 - InternalFlagsWriteComputedProps InternalFlags = 1 << 0 - InternalFlagsNoSyntacticPrinter InternalFlags = 1 << 1 - InternalFlagsDoNotIncludeSymbolChain InternalFlags = 1 << 2 - InternalFlagsAllowUnresolvedNames InternalFlags = 1 << 3 -)