resolver-functions.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.createResolverFunctions = void 0;
  4. const path_1 = require("path");
  5. /**
  6. * @internal
  7. * In a factory because these are shared across both CompilerHost and LanguageService codepaths
  8. */
  9. function createResolverFunctions(kwargs) {
  10. const { host, ts, config, cwd, getCanonicalFileName, projectLocalResolveHelper, options, extensions, } = kwargs;
  11. const moduleResolutionCache = ts.createModuleResolutionCache(cwd, getCanonicalFileName, config.options);
  12. const knownInternalFilenames = new Set();
  13. /** "Buckets" (module directories) whose contents should be marked "internal" */
  14. const internalBuckets = new Set();
  15. // Get bucket for a source filename. Bucket is the containing `./node_modules/*/` directory
  16. // For '/project/node_modules/foo/node_modules/bar/lib/index.js' bucket is '/project/node_modules/foo/node_modules/bar/'
  17. // For '/project/node_modules/foo/node_modules/@scope/bar/lib/index.js' bucket is '/project/node_modules/foo/node_modules/@scope/bar/'
  18. const moduleBucketRe = /.*\/node_modules\/(?:@[^\/]+\/)?[^\/]+\//;
  19. function getModuleBucket(filename) {
  20. const find = moduleBucketRe.exec(filename);
  21. if (find)
  22. return find[0];
  23. return '';
  24. }
  25. // Mark that this file and all siblings in its bucket should be "internal"
  26. function markBucketOfFilenameInternal(filename) {
  27. internalBuckets.add(getModuleBucket(filename));
  28. }
  29. function isFileInInternalBucket(filename) {
  30. return internalBuckets.has(getModuleBucket(filename));
  31. }
  32. function isFileKnownToBeInternal(filename) {
  33. return knownInternalFilenames.has(filename);
  34. }
  35. /**
  36. * If we need to emit JS for a file, force TS to consider it non-external
  37. */
  38. const fixupResolvedModule = (resolvedModule) => {
  39. const { resolvedFileName } = resolvedModule;
  40. if (resolvedFileName === undefined)
  41. return;
  42. // [MUST_UPDATE_FOR_NEW_FILE_EXTENSIONS]
  43. // .ts,.mts,.cts is always switched to internal
  44. // .js is switched on-demand
  45. if (resolvedModule.isExternalLibraryImport &&
  46. ((resolvedFileName.endsWith('.ts') &&
  47. !resolvedFileName.endsWith('.d.ts')) ||
  48. (resolvedFileName.endsWith('.cts') &&
  49. !resolvedFileName.endsWith('.d.cts')) ||
  50. (resolvedFileName.endsWith('.mts') &&
  51. !resolvedFileName.endsWith('.d.mts')) ||
  52. isFileKnownToBeInternal(resolvedFileName) ||
  53. isFileInInternalBucket(resolvedFileName))) {
  54. resolvedModule.isExternalLibraryImport = false;
  55. }
  56. if (!resolvedModule.isExternalLibraryImport) {
  57. knownInternalFilenames.add(resolvedFileName);
  58. }
  59. };
  60. /*
  61. * NOTE:
  62. * Older ts versions do not pass `redirectedReference` nor `options`.
  63. * We must pass `redirectedReference` to newer ts versions, but cannot rely on `options`, hence the weird argument name
  64. */
  65. const resolveModuleNames = (moduleNames, containingFile, reusedNames, redirectedReference, optionsOnlyWithNewerTsVersions, containingSourceFile) => {
  66. return moduleNames.map((moduleName, i) => {
  67. var _a, _b;
  68. const mode = containingSourceFile
  69. ? (_b = (_a = ts).getModeForResolutionAtIndex) === null || _b === void 0 ? void 0 : _b.call(_a, containingSourceFile, i)
  70. : undefined;
  71. let { resolvedModule } = ts.resolveModuleName(moduleName, containingFile, config.options, host, moduleResolutionCache, redirectedReference, mode);
  72. if (!resolvedModule && options.experimentalTsImportSpecifiers) {
  73. const lastDotIndex = moduleName.lastIndexOf('.');
  74. const ext = lastDotIndex >= 0 ? moduleName.slice(lastDotIndex) : '';
  75. if (ext) {
  76. const replacements = extensions.tsResolverEquivalents.get(ext);
  77. for (const replacementExt of replacements !== null && replacements !== void 0 ? replacements : []) {
  78. ({ resolvedModule } = ts.resolveModuleName(moduleName.slice(0, -ext.length) + replacementExt, containingFile, config.options, host, moduleResolutionCache, redirectedReference, mode));
  79. if (resolvedModule)
  80. break;
  81. }
  82. }
  83. }
  84. if (resolvedModule) {
  85. fixupResolvedModule(resolvedModule);
  86. }
  87. return resolvedModule;
  88. });
  89. };
  90. // language service never calls this, but TS docs recommend that we implement it
  91. const getResolvedModuleWithFailedLookupLocationsFromCache = (moduleName, containingFile, resolutionMode) => {
  92. const ret = ts.resolveModuleNameFromCache(moduleName, containingFile, moduleResolutionCache, resolutionMode);
  93. if (ret && ret.resolvedModule) {
  94. fixupResolvedModule(ret.resolvedModule);
  95. }
  96. return ret;
  97. };
  98. const resolveTypeReferenceDirectives = (typeDirectiveNames, containingFile, redirectedReference, options, containingFileMode // new impliedNodeFormat is accepted by compilerHost
  99. ) => {
  100. // Note: seems to be called with empty typeDirectiveNames array for all files.
  101. // TODO consider using `ts.loadWithTypeDirectiveCache`
  102. return typeDirectiveNames.map((typeDirectiveName) => {
  103. // Copy-pasted from TS source:
  104. const nameIsString = typeof typeDirectiveName === 'string';
  105. const mode = nameIsString
  106. ? undefined
  107. : ts.getModeForFileReference(typeDirectiveName, containingFileMode);
  108. const strName = nameIsString
  109. ? typeDirectiveName
  110. : typeDirectiveName.fileName.toLowerCase();
  111. let { resolvedTypeReferenceDirective } = ts.resolveTypeReferenceDirective(strName, containingFile, config.options, host, redirectedReference, undefined, mode);
  112. if (typeDirectiveName === 'node' && !resolvedTypeReferenceDirective) {
  113. // Resolve @types/node relative to project first, then __dirname (copy logic from elsewhere / refactor into reusable function)
  114. let typesNodePackageJsonPath;
  115. try {
  116. typesNodePackageJsonPath = projectLocalResolveHelper('@types/node/package.json', true);
  117. }
  118. catch { } // gracefully do nothing when @types/node is not installed for any reason
  119. if (typesNodePackageJsonPath) {
  120. const typeRoots = [(0, path_1.resolve)(typesNodePackageJsonPath, '../..')];
  121. ({ resolvedTypeReferenceDirective } =
  122. ts.resolveTypeReferenceDirective(typeDirectiveName, containingFile, {
  123. ...config.options,
  124. typeRoots,
  125. }, host, redirectedReference));
  126. }
  127. }
  128. if (resolvedTypeReferenceDirective) {
  129. fixupResolvedModule(resolvedTypeReferenceDirective);
  130. }
  131. return resolvedTypeReferenceDirective;
  132. });
  133. };
  134. return {
  135. resolveModuleNames,
  136. getResolvedModuleWithFailedLookupLocationsFromCache,
  137. resolveTypeReferenceDirectives,
  138. isFileKnownToBeInternal,
  139. markBucketOfFilenameInternal,
  140. };
  141. }
  142. exports.createResolverFunctions = createResolverFunctions;
  143. //# sourceMappingURL=resolver-functions.js.map