ts-internals.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.getPatternFromSpec = exports.createTsInternals = void 0;
  4. const path_1 = require("path");
  5. const util_1 = require("./util");
  6. /** @internal */
  7. exports.createTsInternals = (0, util_1.cachedLookup)(createTsInternalsUncached);
  8. /**
  9. * Given a reference to the TS compiler, return some TS internal functions that we
  10. * could not or did not want to grab off the `ts` object.
  11. * These have been copy-pasted from TS's source and tweaked as necessary.
  12. *
  13. * NOTE: This factory returns *only* functions which need a reference to the TS
  14. * compiler. Other functions do not need a reference to the TS compiler so are
  15. * exported directly from this file.
  16. */
  17. function createTsInternalsUncached(_ts) {
  18. const ts = _ts;
  19. /**
  20. * Copied from:
  21. * https://github.com/microsoft/TypeScript/blob/v4.3.2/src/compiler/commandLineParser.ts#L2821-L2846
  22. */
  23. function getExtendsConfigPath(extendedConfig, host, basePath, errors, createDiagnostic) {
  24. extendedConfig = (0, util_1.normalizeSlashes)(extendedConfig);
  25. if (isRootedDiskPath(extendedConfig) ||
  26. startsWith(extendedConfig, './') ||
  27. startsWith(extendedConfig, '../')) {
  28. let extendedConfigPath = getNormalizedAbsolutePath(extendedConfig, basePath);
  29. if (!host.fileExists(extendedConfigPath) &&
  30. !endsWith(extendedConfigPath, ts.Extension.Json)) {
  31. extendedConfigPath = `${extendedConfigPath}.json`;
  32. if (!host.fileExists(extendedConfigPath)) {
  33. errors.push(createDiagnostic(ts.Diagnostics.File_0_not_found, extendedConfig));
  34. return undefined;
  35. }
  36. }
  37. return extendedConfigPath;
  38. }
  39. // If the path isn't a rooted or relative path, resolve like a module
  40. const resolved = ts.nodeModuleNameResolver(extendedConfig, combinePaths(basePath, 'tsconfig.json'), { moduleResolution: ts.ModuleResolutionKind.NodeJs }, host,
  41. /*cache*/ undefined,
  42. /*projectRefs*/ undefined,
  43. /*lookupConfig*/ true);
  44. if (resolved.resolvedModule) {
  45. return resolved.resolvedModule.resolvedFileName;
  46. }
  47. errors.push(createDiagnostic(ts.Diagnostics.File_0_not_found, extendedConfig));
  48. return undefined;
  49. }
  50. return { getExtendsConfigPath };
  51. }
  52. // These functions have alternative implementation to avoid copying too much from TS
  53. function isRootedDiskPath(path) {
  54. return (0, path_1.isAbsolute)(path);
  55. }
  56. function combinePaths(path, ...paths) {
  57. return (0, util_1.normalizeSlashes)((0, path_1.resolve)(path, ...paths.filter((path) => path)));
  58. }
  59. function getNormalizedAbsolutePath(fileName, currentDirectory) {
  60. return (0, util_1.normalizeSlashes)(currentDirectory != null
  61. ? (0, path_1.resolve)(currentDirectory, fileName)
  62. : (0, path_1.resolve)(fileName));
  63. }
  64. function startsWith(str, prefix) {
  65. return str.lastIndexOf(prefix, 0) === 0;
  66. }
  67. function endsWith(str, suffix) {
  68. const expectedPos = str.length - suffix.length;
  69. return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos;
  70. }
  71. // Reserved characters, forces escaping of any non-word (or digit), non-whitespace character.
  72. // It may be inefficient (we could just match (/[-[\]{}()*+?.,\\^$|#\s]/g), but this is future
  73. // proof.
  74. const reservedCharacterPattern = /[^\w\s\/]/g;
  75. /**
  76. * @internal
  77. * See also: getRegularExpressionForWildcard, which seems to do almost the same thing
  78. */
  79. function getPatternFromSpec(spec, basePath) {
  80. const pattern = spec && getSubPatternFromSpec(spec, basePath, excludeMatcher);
  81. return pattern && `^(${pattern})${'($|/)'}`;
  82. }
  83. exports.getPatternFromSpec = getPatternFromSpec;
  84. function getSubPatternFromSpec(spec, basePath, { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter, }) {
  85. let subpattern = '';
  86. let hasWrittenComponent = false;
  87. const components = getNormalizedPathComponents(spec, basePath);
  88. const lastComponent = last(components);
  89. // getNormalizedPathComponents includes the separator for the root component.
  90. // We need to remove to create our regex correctly.
  91. components[0] = removeTrailingDirectorySeparator(components[0]);
  92. if (isImplicitGlob(lastComponent)) {
  93. components.push('**', '*');
  94. }
  95. let optionalCount = 0;
  96. for (let component of components) {
  97. if (component === '**') {
  98. subpattern += doubleAsteriskRegexFragment;
  99. }
  100. else {
  101. if (hasWrittenComponent) {
  102. subpattern += directorySeparator;
  103. }
  104. subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
  105. }
  106. hasWrittenComponent = true;
  107. }
  108. while (optionalCount > 0) {
  109. subpattern += ')?';
  110. optionalCount--;
  111. }
  112. return subpattern;
  113. }
  114. const directoriesMatcher = {
  115. singleAsteriskRegexFragment: '[^/]*',
  116. /**
  117. * Regex for the ** wildcard. Matches any num of subdirectories. When used for including
  118. * files or directories, does not match subdirectories that start with a . character
  119. */
  120. doubleAsteriskRegexFragment: `(/[^/.][^/]*)*?`,
  121. replaceWildcardCharacter: (match) => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment),
  122. };
  123. const excludeMatcher = {
  124. singleAsteriskRegexFragment: '[^/]*',
  125. doubleAsteriskRegexFragment: '(/.+?)?',
  126. replaceWildcardCharacter: (match) => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment),
  127. };
  128. function getNormalizedPathComponents(path, currentDirectory) {
  129. return reducePathComponents(getPathComponents(path, currentDirectory));
  130. }
  131. function getPathComponents(path, currentDirectory = '') {
  132. path = combinePaths(currentDirectory, path);
  133. return pathComponents(path, getRootLength(path));
  134. }
  135. function reducePathComponents(components) {
  136. if (!some(components))
  137. return [];
  138. const reduced = [components[0]];
  139. for (let i = 1; i < components.length; i++) {
  140. const component = components[i];
  141. if (!component)
  142. continue;
  143. if (component === '.')
  144. continue;
  145. if (component === '..') {
  146. if (reduced.length > 1) {
  147. if (reduced[reduced.length - 1] !== '..') {
  148. reduced.pop();
  149. continue;
  150. }
  151. }
  152. else if (reduced[0])
  153. continue;
  154. }
  155. reduced.push(component);
  156. }
  157. return reduced;
  158. }
  159. function getRootLength(path) {
  160. const rootLength = getEncodedRootLength(path);
  161. return rootLength < 0 ? ~rootLength : rootLength;
  162. }
  163. function getEncodedRootLength(path) {
  164. if (!path)
  165. return 0;
  166. const ch0 = path.charCodeAt(0);
  167. // POSIX or UNC
  168. if (ch0 === 47 /* CharacterCodes.slash */ || ch0 === 92 /* CharacterCodes.backslash */) {
  169. if (path.charCodeAt(1) !== ch0)
  170. return 1; // POSIX: "/" (or non-normalized "\")
  171. const p1 = path.indexOf(ch0 === 47 /* CharacterCodes.slash */ ? directorySeparator : altDirectorySeparator, 2);
  172. if (p1 < 0)
  173. return path.length; // UNC: "//server" or "\\server"
  174. return p1 + 1; // UNC: "//server/" or "\\server\"
  175. }
  176. // DOS
  177. if (isVolumeCharacter(ch0) && path.charCodeAt(1) === 58 /* CharacterCodes.colon */) {
  178. const ch2 = path.charCodeAt(2);
  179. if (ch2 === 47 /* CharacterCodes.slash */ || ch2 === 92 /* CharacterCodes.backslash */)
  180. return 3; // DOS: "c:/" or "c:\"
  181. if (path.length === 2)
  182. return 2; // DOS: "c:" (but not "c:d")
  183. }
  184. // URL
  185. const schemeEnd = path.indexOf(urlSchemeSeparator);
  186. if (schemeEnd !== -1) {
  187. const authorityStart = schemeEnd + urlSchemeSeparator.length;
  188. const authorityEnd = path.indexOf(directorySeparator, authorityStart);
  189. if (authorityEnd !== -1) {
  190. // URL: "file:///", "file://server/", "file://server/path"
  191. // For local "file" URLs, include the leading DOS volume (if present).
  192. // Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
  193. // special case interpreted as "the machine from which the URL is being interpreted".
  194. const scheme = path.slice(0, schemeEnd);
  195. const authority = path.slice(authorityStart, authorityEnd);
  196. if (scheme === 'file' &&
  197. (authority === '' || authority === 'localhost') &&
  198. isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
  199. const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
  200. if (volumeSeparatorEnd !== -1) {
  201. if (path.charCodeAt(volumeSeparatorEnd) === 47 /* CharacterCodes.slash */) {
  202. // URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
  203. return ~(volumeSeparatorEnd + 1);
  204. }
  205. if (volumeSeparatorEnd === path.length) {
  206. // URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
  207. // but not "file:///c:d" or "file:///c%3ad"
  208. return ~volumeSeparatorEnd;
  209. }
  210. }
  211. }
  212. return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
  213. }
  214. return ~path.length; // URL: "file://server", "http://server"
  215. }
  216. // relative
  217. return 0;
  218. }
  219. function ensureTrailingDirectorySeparator(path) {
  220. if (!hasTrailingDirectorySeparator(path)) {
  221. return path + directorySeparator;
  222. }
  223. return path;
  224. }
  225. function hasTrailingDirectorySeparator(path) {
  226. return (path.length > 0 && isAnyDirectorySeparator(path.charCodeAt(path.length - 1)));
  227. }
  228. function isAnyDirectorySeparator(charCode) {
  229. return (charCode === 47 /* CharacterCodes.slash */ || charCode === 92 /* CharacterCodes.backslash */);
  230. }
  231. function removeTrailingDirectorySeparator(path) {
  232. if (hasTrailingDirectorySeparator(path)) {
  233. return path.substr(0, path.length - 1);
  234. }
  235. return path;
  236. }
  237. const directorySeparator = '/';
  238. const altDirectorySeparator = '\\';
  239. const urlSchemeSeparator = '://';
  240. function isVolumeCharacter(charCode) {
  241. return ((charCode >= 97 /* CharacterCodes.a */ && charCode <= 122 /* CharacterCodes.z */) ||
  242. (charCode >= 65 /* CharacterCodes.A */ && charCode <= 90 /* CharacterCodes.Z */));
  243. }
  244. function getFileUrlVolumeSeparatorEnd(url, start) {
  245. const ch0 = url.charCodeAt(start);
  246. if (ch0 === 58 /* CharacterCodes.colon */)
  247. return start + 1;
  248. if (ch0 === 37 /* CharacterCodes.percent */ &&
  249. url.charCodeAt(start + 1) === 51 /* CharacterCodes._3 */) {
  250. const ch2 = url.charCodeAt(start + 2);
  251. if (ch2 === 97 /* CharacterCodes.a */ || ch2 === 65 /* CharacterCodes.A */)
  252. return start + 3;
  253. }
  254. return -1;
  255. }
  256. function some(array, predicate) {
  257. if (array) {
  258. if (predicate) {
  259. for (const v of array) {
  260. if (predicate(v)) {
  261. return true;
  262. }
  263. }
  264. }
  265. else {
  266. return array.length > 0;
  267. }
  268. }
  269. return false;
  270. }
  271. function pathComponents(path, rootLength) {
  272. const root = path.substring(0, rootLength);
  273. const rest = path.substring(rootLength).split(directorySeparator);
  274. if (rest.length && !lastOrUndefined(rest))
  275. rest.pop();
  276. return [root, ...rest];
  277. }
  278. function lastOrUndefined(array) {
  279. return array.length === 0 ? undefined : array[array.length - 1];
  280. }
  281. function last(array) {
  282. // Debug.assert(array.length !== 0);
  283. return array[array.length - 1];
  284. }
  285. function replaceWildcardCharacter(match, singleAsteriskRegexFragment) {
  286. return match === '*'
  287. ? singleAsteriskRegexFragment
  288. : match === '?'
  289. ? '[^/]'
  290. : '\\' + match;
  291. }
  292. /**
  293. * An "includes" path "foo" is implicitly a glob "foo/** /*" (without the space) if its last component has no extension,
  294. * and does not contain any glob characters itself.
  295. */
  296. function isImplicitGlob(lastPathComponent) {
  297. return !/[.*?]/.test(lastPathComponent);
  298. }
  299. //# sourceMappingURL=ts-internals.js.map