utils.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.combineSourcemaps = exports.processSrcSet = exports.generateCodeFrame = exports.locToStartAndEnd = exports.posToNumber = exports.offsetToLineColumnByLines = exports.offsetToLineColumn = exports.offsetToStartAndEnd = exports.pad = exports.isObject = exports.asyncReplace = exports.multilineCommentsRE = exports.isDataUrl = exports.dataUrlRE = exports.isExternalUrl = exports.externalRE = exports.cleanUrl = exports.hashRE = exports.queryRE = exports.normalizePath = exports.isWindows = exports.deepImportRE = exports.bareImportRE = exports.slash = void 0;
  7. /**
  8. * https://github.com/vitejs/vite/blob/main/packages/vite/src/node/utils.ts
  9. */
  10. const os_1 = __importDefault(require("os"));
  11. const path_1 = __importDefault(require("path"));
  12. const remapping_1 = __importDefault(require("@ampproject/remapping"));
  13. const lines_and_columns_1 = require("lines-and-columns");
  14. function slash(p) {
  15. return p.replace(/\\/g, '/');
  16. }
  17. exports.slash = slash;
  18. exports.bareImportRE = /^[\w@](?!.*:\/\/)/;
  19. exports.deepImportRE = /^([^@][^/]*)\/|^(@[^/]+\/[^/]+)\//;
  20. exports.isWindows = os_1.default.platform() === 'win32';
  21. function normalizePath(id) {
  22. return path_1.default.posix.normalize(exports.isWindows ? slash(id) : id);
  23. }
  24. exports.normalizePath = normalizePath;
  25. exports.queryRE = /\?.*$/s;
  26. exports.hashRE = /#.*$/s;
  27. const cleanUrl = (url) => url.replace(exports.hashRE, '').replace(exports.queryRE, '');
  28. exports.cleanUrl = cleanUrl;
  29. exports.externalRE = /^(https?:)?\/\//;
  30. const isExternalUrl = (url) => exports.externalRE.test(url);
  31. exports.isExternalUrl = isExternalUrl;
  32. exports.dataUrlRE = /^\s*data:/i;
  33. const isDataUrl = (url) => exports.dataUrlRE.test(url);
  34. exports.isDataUrl = isDataUrl;
  35. exports.multilineCommentsRE = /\/\*(.|[\r\n])*?\*\//gm;
  36. async function asyncReplace(input, re, replacer) {
  37. let match;
  38. let remaining = input;
  39. let rewritten = '';
  40. while ((match = re.exec(remaining))) {
  41. rewritten += remaining.slice(0, match.index);
  42. rewritten += await replacer(match);
  43. remaining = remaining.slice(match.index + match[0].length);
  44. }
  45. rewritten += remaining;
  46. return rewritten;
  47. }
  48. exports.asyncReplace = asyncReplace;
  49. function isObject(value) {
  50. return Object.prototype.toString.call(value) === '[object Object]';
  51. }
  52. exports.isObject = isObject;
  53. const splitRE = /\r?\n/;
  54. const range = 2;
  55. function pad(source, n = 2) {
  56. const lines = source.split(splitRE);
  57. return lines.map((l) => ` `.repeat(n) + l).join(`\n`);
  58. }
  59. exports.pad = pad;
  60. function offsetToStartAndEnd(source, startOffset, endOffset) {
  61. const lines = new lines_and_columns_1.LinesAndColumns(source);
  62. return {
  63. start: offsetToLineColumnByLines(lines, startOffset),
  64. end: offsetToLineColumnByLines(lines, endOffset),
  65. source: '',
  66. };
  67. }
  68. exports.offsetToStartAndEnd = offsetToStartAndEnd;
  69. function offsetToLineColumn(source, offset) {
  70. return offsetToLineColumnByLines(new lines_and_columns_1.LinesAndColumns(source), offset);
  71. }
  72. exports.offsetToLineColumn = offsetToLineColumn;
  73. function offsetToLineColumnByLines(lines, offset) {
  74. let location = lines.locationForIndex(offset);
  75. if (!location) {
  76. location = lines.locationForIndex(offset);
  77. }
  78. // lines-and-columns is 0-indexed while positions are 1-indexed
  79. return { line: location.line + 1, column: location.column, offset: 0 };
  80. }
  81. exports.offsetToLineColumnByLines = offsetToLineColumnByLines;
  82. function posToNumber(source, pos) {
  83. if (typeof pos === 'number')
  84. return pos;
  85. return posToNumberByLines(new lines_and_columns_1.LinesAndColumns(source), pos.line, pos.column);
  86. }
  87. exports.posToNumber = posToNumber;
  88. function posToNumberByLines(lines, line, column) {
  89. // lines-and-columns is 0-indexed while positions are 1-indexed
  90. return lines.indexForLocation({ line: line - 1, column }) || 0;
  91. }
  92. function locToStartAndEnd(source, loc) {
  93. const lines = new lines_and_columns_1.LinesAndColumns(source);
  94. const start = posToNumberByLines(lines, loc.start.line, loc.start.column);
  95. const end = loc.end
  96. ? posToNumberByLines(lines, loc.end.line, loc.end.column)
  97. : undefined;
  98. return { start, end };
  99. }
  100. exports.locToStartAndEnd = locToStartAndEnd;
  101. function generateCodeFrame(source, start = 0, end) {
  102. start = posToNumber(source, start);
  103. end = end || start;
  104. // Split the content into individual lines but capture the newline sequence
  105. // that separated each line. This is important because the actual sequence is
  106. // needed to properly take into account the full line length for offset
  107. // comparison
  108. let lines = source.split(/(\r?\n)/);
  109. // Separate the lines and newline sequences into separate arrays for easier referencing
  110. const newlineSequences = lines.filter((_, idx) => idx % 2 === 1);
  111. lines = lines.filter((_, idx) => idx % 2 === 0);
  112. let count = 0;
  113. const res = [];
  114. for (let i = 0; i < lines.length; i++) {
  115. count +=
  116. lines[i].length +
  117. ((newlineSequences[i] && newlineSequences[i].length) || 0);
  118. if (count >= start) {
  119. for (let j = i - range; j <= i + range || end > count; j++) {
  120. if (j < 0 || j >= lines.length)
  121. continue;
  122. const line = j + 1;
  123. res.push(`${line}${' '.repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`);
  124. const lineLength = lines[j].length;
  125. const newLineSeqLength = (newlineSequences[j] && newlineSequences[j].length) || 0;
  126. if (j === i) {
  127. // push underline
  128. const pad = start - (count - (lineLength + newLineSeqLength));
  129. const length = Math.max(1, end > count ? lineLength - pad : end - start);
  130. res.push(` | ` + ' '.repeat(pad) + '^'.repeat(length));
  131. }
  132. else if (j > i) {
  133. if (end > count) {
  134. const length = Math.max(Math.min(end - count, lineLength), 1);
  135. res.push(` | ` + '^'.repeat(length));
  136. }
  137. count += lineLength + newLineSeqLength;
  138. }
  139. }
  140. break;
  141. }
  142. }
  143. return res.join('\n');
  144. }
  145. exports.generateCodeFrame = generateCodeFrame;
  146. const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g;
  147. const imageSetUrlRE = /^(?:[\w\-]+\(.*?\)|'.*?'|".*?"|\S*)/;
  148. async function processSrcSet(srcs, replacer) {
  149. const imageCandidates = srcs
  150. .split(',')
  151. .map((s) => {
  152. const src = s.replace(escapedSpaceCharacters, ' ').trim();
  153. const [url] = imageSetUrlRE.exec(src) || [];
  154. return {
  155. url: url,
  156. descriptor: src?.slice(url.length).trim(),
  157. };
  158. })
  159. .filter(({ url }) => !!url);
  160. const ret = await Promise.all(imageCandidates.map(async ({ url, descriptor }) => {
  161. return {
  162. url: await replacer({ url, descriptor }),
  163. descriptor,
  164. };
  165. }));
  166. const url = ret.reduce((prev, { url, descriptor }, index) => {
  167. descriptor = descriptor || '';
  168. return (prev +=
  169. url + ` ${descriptor}${index === ret.length - 1 ? '' : ', '}`);
  170. }, '');
  171. return url;
  172. }
  173. exports.processSrcSet = processSrcSet;
  174. function escapeToLinuxLikePath(path) {
  175. if (/^[A-Z]:/.test(path)) {
  176. return path.replace(/^([A-Z]):\//, '/windows/$1/');
  177. }
  178. if (/^\/[^/]/.test(path)) {
  179. return `/linux${path}`;
  180. }
  181. return path;
  182. }
  183. function unescapeToLinuxLikePath(path) {
  184. if (path.startsWith('/linux/')) {
  185. return path.slice('/linux'.length);
  186. }
  187. if (path.startsWith('/windows/')) {
  188. return path.replace(/^\/windows\/([A-Z])\//, '$1:/');
  189. }
  190. return path;
  191. }
  192. // based on https://github.com/sveltejs/svelte/blob/abf11bb02b2afbd3e4cac509a0f70e318c306364/src/compiler/utils/mapped_code.ts#L221
  193. const nullSourceMap = {
  194. names: [],
  195. sources: [],
  196. mappings: '',
  197. version: 3,
  198. };
  199. function combineSourcemaps(filename, sourcemapList, excludeContent = true) {
  200. if (sourcemapList.length === 0 ||
  201. sourcemapList.every((m) => m.sources.length === 0)) {
  202. return { ...nullSourceMap };
  203. }
  204. // hack for parse broken with normalized absolute paths on windows (C:/path/to/something).
  205. // escape them to linux like paths
  206. // also avoid mutation here to prevent breaking plugin's using cache to generate sourcemaps like vue (see #7442)
  207. sourcemapList = sourcemapList.map((sourcemap) => {
  208. const newSourcemaps = { ...sourcemap };
  209. newSourcemaps.sources = sourcemap.sources.map((source) => source ? escapeToLinuxLikePath(source) : null);
  210. if (sourcemap.sourceRoot) {
  211. newSourcemaps.sourceRoot = escapeToLinuxLikePath(sourcemap.sourceRoot);
  212. }
  213. return newSourcemaps;
  214. });
  215. const escapedFilename = escapeToLinuxLikePath(filename);
  216. // We don't declare type here so we can convert/fake/map as RawSourceMap
  217. let map; //: SourceMap
  218. let mapIndex = 1;
  219. const useArrayInterface = sourcemapList.slice(0, -1).find((m) => m.sources.length !== 1) === undefined;
  220. if (useArrayInterface) {
  221. map = (0, remapping_1.default)(sourcemapList, () => null, excludeContent);
  222. }
  223. else {
  224. map = (0, remapping_1.default)(sourcemapList[0], function loader(sourcefile) {
  225. if (sourcefile === escapedFilename && sourcemapList[mapIndex]) {
  226. return sourcemapList[mapIndex++];
  227. }
  228. else {
  229. return null;
  230. }
  231. }, excludeContent);
  232. }
  233. if (!map.file) {
  234. delete map.file;
  235. }
  236. // unescape the previous hack
  237. map.sources = map.sources.map((source) => source ? unescapeToLinuxLikePath(source) : source);
  238. map.file = filename;
  239. return map;
  240. }
  241. exports.combineSourcemaps = combineSourcemaps;