jsconfig-paths-plugin.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.hasZeroOrOneAsteriskCharacter = hasZeroOrOneAsteriskCharacter;
  6. exports.pathIsRelative = pathIsRelative;
  7. exports.tryParsePattern = tryParsePattern;
  8. exports.findBestPatternMatch = findBestPatternMatch;
  9. exports.matchPatternOrExact = matchPatternOrExact;
  10. exports.isString = isString;
  11. exports.matchedText = matchedText;
  12. exports.patternText = patternText;
  13. var _path = _interopRequireDefault(require("path"));
  14. var _debug = require("next/dist/compiled/debug");
  15. function _interopRequireDefault(obj) {
  16. return obj && obj.__esModule ? obj : {
  17. default: obj
  18. };
  19. }
  20. const log = (0, _debug).debug("next:jsconfig-paths-plugin");
  21. const asterisk = 0x2a;
  22. function hasZeroOrOneAsteriskCharacter(str) {
  23. let seenAsterisk = false;
  24. for(let i = 0; i < str.length; i++){
  25. if (str.charCodeAt(i) === asterisk) {
  26. if (!seenAsterisk) {
  27. seenAsterisk = true;
  28. } else {
  29. // have already seen asterisk
  30. return false;
  31. }
  32. }
  33. }
  34. return true;
  35. }
  36. function pathIsRelative(testPath) {
  37. return /^\.\.?($|[\\/])/.test(testPath);
  38. }
  39. function tryParsePattern(pattern) {
  40. // This should be verified outside of here and a proper error thrown.
  41. const indexOfStar = pattern.indexOf("*");
  42. return indexOfStar === -1 ? undefined : {
  43. prefix: pattern.slice(0, indexOfStar),
  44. suffix: pattern.slice(indexOfStar + 1)
  45. };
  46. }
  47. function isPatternMatch({ prefix , suffix }, candidate) {
  48. return candidate.length >= prefix.length + suffix.length && candidate.startsWith(prefix) && candidate.endsWith(suffix);
  49. }
  50. function findBestPatternMatch(values, getPattern, candidate) {
  51. let matchedValue;
  52. // use length of prefix as betterness criteria
  53. let longestMatchPrefixLength = -1;
  54. for (const v of values){
  55. const pattern = getPattern(v);
  56. if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) {
  57. longestMatchPrefixLength = pattern.prefix.length;
  58. matchedValue = v;
  59. }
  60. }
  61. return matchedValue;
  62. }
  63. function matchPatternOrExact(patternStrings, candidate) {
  64. const patterns = [];
  65. for (const patternString of patternStrings){
  66. if (!hasZeroOrOneAsteriskCharacter(patternString)) continue;
  67. const pattern = tryParsePattern(patternString);
  68. if (pattern) {
  69. patterns.push(pattern);
  70. } else if (patternString === candidate) {
  71. // pattern was matched as is - no need to search further
  72. return patternString;
  73. }
  74. }
  75. return findBestPatternMatch(patterns, (_)=>_, candidate);
  76. }
  77. function isString(text) {
  78. return typeof text === "string";
  79. }
  80. function matchedText(pattern, candidate) {
  81. return candidate.substring(pattern.prefix.length, candidate.length - pattern.suffix.length);
  82. }
  83. function patternText({ prefix , suffix }) {
  84. return `${prefix}*${suffix}`;
  85. }
  86. /**
  87. * Calls the iterator function for each entry of the array
  88. * until the first result or error is reached
  89. */ function forEachBail(array, iterator, callback) {
  90. if (array.length === 0) return callback();
  91. let i = 0;
  92. const next = ()=>{
  93. let loop = undefined;
  94. iterator(array[i++], (err, result)=>{
  95. if (err || result !== undefined || i >= array.length) {
  96. return callback(err, result);
  97. }
  98. if (loop === false) while(next());
  99. loop = true;
  100. });
  101. if (!loop) loop = false;
  102. return loop;
  103. };
  104. while(next());
  105. }
  106. const NODE_MODULES_REGEX = /node_modules/;
  107. class JsConfigPathsPlugin {
  108. constructor(paths, resolvedBaseUrl){
  109. this.paths = paths;
  110. this.resolvedBaseUrl = resolvedBaseUrl;
  111. this.jsConfigPlugin = true;
  112. log("tsconfig.json or jsconfig.json paths: %O", paths);
  113. log("resolved baseUrl: %s", resolvedBaseUrl);
  114. }
  115. apply(resolver) {
  116. const target = resolver.ensureHook("resolve");
  117. resolver.getHook("described-resolve").tapAsync("JsConfigPathsPlugin", (request, resolveContext, callback)=>{
  118. const paths = this.paths;
  119. const pathsKeys = Object.keys(paths);
  120. // If no aliases are added bail out
  121. if (pathsKeys.length === 0) {
  122. log("paths are empty, bailing out");
  123. return callback();
  124. }
  125. const moduleName = request.request;
  126. // Exclude node_modules from paths support (speeds up resolving)
  127. if (request.path.match(NODE_MODULES_REGEX)) {
  128. log("skipping request as it is inside node_modules %s", moduleName);
  129. return callback();
  130. }
  131. if (_path.default.posix.isAbsolute(moduleName) || process.platform === "win32" && _path.default.win32.isAbsolute(moduleName)) {
  132. log("skipping request as it is an absolute path %s", moduleName);
  133. return callback();
  134. }
  135. if (pathIsRelative(moduleName)) {
  136. log("skipping request as it is a relative path %s", moduleName);
  137. return callback();
  138. }
  139. // log('starting to resolve request %s', moduleName)
  140. // If the module name does not match any of the patterns in `paths` we hand off resolving to webpack
  141. const matchedPattern = matchPatternOrExact(pathsKeys, moduleName);
  142. if (!matchedPattern) {
  143. log("moduleName did not match any paths pattern %s", moduleName);
  144. return callback();
  145. }
  146. const matchedStar = isString(matchedPattern) ? undefined : matchedText(matchedPattern, moduleName);
  147. const matchedPatternText = isString(matchedPattern) ? matchedPattern : patternText(matchedPattern);
  148. let triedPaths = [];
  149. forEachBail(paths[matchedPatternText], (subst, pathCallback)=>{
  150. const curPath = matchedStar ? subst.replace("*", matchedStar) : subst;
  151. // Ensure .d.ts is not matched
  152. if (curPath.endsWith(".d.ts")) {
  153. // try next path candidate
  154. return pathCallback();
  155. }
  156. const candidate = _path.default.join(this.resolvedBaseUrl, curPath);
  157. const obj = Object.assign({}, request, {
  158. request: candidate
  159. });
  160. resolver.doResolve(target, obj, `Aliased with tsconfig.json or jsconfig.json ${matchedPatternText} to ${candidate}`, resolveContext, (resolverErr, resolverResult)=>{
  161. if (resolverErr || resolverResult === undefined) {
  162. triedPaths.push(candidate);
  163. // try next path candidate
  164. return pathCallback();
  165. }
  166. return pathCallback(resolverErr, resolverResult);
  167. });
  168. }, callback);
  169. });
  170. }
  171. }
  172. exports.JsConfigPathsPlugin = JsConfigPathsPlugin;
  173. //# sourceMappingURL=jsconfig-paths-plugin.js.map