d64ac2d788e411dad27d0e5f99fd8c99f00fb94404a0984fc977e6af8dd6f46b07e27065421a649ddfbcf30bada077dcfebb5aa574841b1f263767b7d14c97 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. 'use strict';
  2. const pathe = require('pathe');
  3. const mlly = require('mlly');
  4. const MagicString = require('magic-string');
  5. const stripLiteral = require('strip-literal');
  6. const excludeRE = [
  7. // imported/exported from other module
  8. /\b(import|export)\b(.+?)\bfrom\b/gs,
  9. // defined as function
  10. /\bfunction\s*([\w_$]+?)\s*\(/gs,
  11. // defined as class
  12. /\bclass\s*([\w_$]+?)\s*{/gs,
  13. // defined as local variable
  14. /\b(?:const|let|var)\s+?(\[.*?\]|\{.*?\}|.+?)\s*?[=;\n]/gs
  15. ];
  16. const importAsRE = /^.*\sas\s+/;
  17. const separatorRE = /[,[\]{}\n]|\bimport\b/g;
  18. const matchRE = /(^|\.\.\.|(?:\bcase|\?)\s+|[^\w_$\/)])([\w_$]+)\s*(?=[.()[\]}:;?+\-*&|`<>,\n]|\b(?:instanceof|in)\b|$)/g;
  19. const regexRE = /\/[^\s]*?(?<!\\)(?<!\[[^\]]*)\/[gimsuy]*/g;
  20. function stripCommentsAndStrings(code) {
  21. return stripLiteral.stripLiteral(code).replace(regexRE, 'new RegExp("")');
  22. }
  23. function defineUnimportPreset(preset) {
  24. return preset;
  25. }
  26. function toImports(imports, isCJS = false) {
  27. const map = toImportModuleMap(imports);
  28. return Object.entries(map).flatMap(([name, importSet]) => {
  29. const entries = [];
  30. const imports2 = Array.from(importSet).filter((i) => {
  31. if (!i.name || i.as === "") {
  32. entries.push(
  33. isCJS ? `require('${name}');` : `import '${name}';`
  34. );
  35. return false;
  36. } else if (i.name === "default") {
  37. entries.push(
  38. isCJS ? `const { default: ${i.as} } = require('${name}');` : `import ${i.as} from '${name}';`
  39. );
  40. return false;
  41. } else if (i.name === "*") {
  42. entries.push(
  43. isCJS ? `const ${i.as} = require('${name}');` : `import * as ${i.as} from '${name}';`
  44. );
  45. return false;
  46. }
  47. return true;
  48. });
  49. if (imports2.length) {
  50. const importsAs = imports2.map((i) => stringifyImportAlias(i, isCJS));
  51. entries.push(
  52. isCJS ? `const { ${importsAs.join(", ")} } = require('${name}');` : `import { ${importsAs.join(", ")} } from '${name}';`
  53. );
  54. }
  55. return entries;
  56. }).join("\n");
  57. }
  58. function dedupeImports(imports, warn) {
  59. const map = /* @__PURE__ */ new Map();
  60. const indexToRemove = /* @__PURE__ */ new Set();
  61. imports.filter((i) => !i.disabled).forEach((i, idx) => {
  62. const name = i.as ?? i.name;
  63. if (!map.has(name)) {
  64. map.set(name, idx);
  65. return;
  66. }
  67. const other = imports[map.get(name)];
  68. if (other.from === i.from) {
  69. indexToRemove.add(idx);
  70. return;
  71. }
  72. const diff = (other.priority || 1) - (i.priority || 1);
  73. if (diff === 0) {
  74. warn(`Duplicated imports "${name}", the one from "${other.from}" has been ignored`);
  75. }
  76. if (diff <= 0) {
  77. indexToRemove.add(map.get(name));
  78. map.set(name, idx);
  79. } else {
  80. indexToRemove.add(idx);
  81. }
  82. });
  83. return imports.filter((_, idx) => !indexToRemove.has(idx));
  84. }
  85. function toExports(imports, fileDir) {
  86. const map = toImportModuleMap(imports);
  87. return Object.entries(map).flatMap(([name, imports2]) => {
  88. name = name.replace(/\.[a-z]+$/, "");
  89. if (fileDir && pathe.isAbsolute(name)) {
  90. name = pathe.relative(fileDir, name);
  91. if (!name.match(/^[.\/]/)) {
  92. name = "./" + name;
  93. }
  94. }
  95. const entries = [];
  96. const filtered = Array.from(imports2).filter((i) => {
  97. if (i.name === "*") {
  98. entries.push(`export * as ${i.as} from '${name}';`);
  99. return false;
  100. }
  101. return true;
  102. });
  103. if (filtered.length) {
  104. entries.push(`export { ${filtered.map((i) => stringifyImportAlias(i, false)).join(", ")} } from '${name}';`);
  105. }
  106. return entries;
  107. }).join("\n");
  108. }
  109. function toTypeDeclarationItems(imports, options) {
  110. return imports.map((i) => {
  111. const from = options?.resolvePath?.(i) || i.from;
  112. return `const ${i.as}: typeof import('${from}')${i.name !== "*" ? `['${i.name}']` : ""}`;
  113. }).sort();
  114. }
  115. function toTypeDeclarationFile(imports, options) {
  116. const items = toTypeDeclarationItems(imports, options);
  117. const {
  118. exportHelper = true
  119. } = options || {};
  120. let declaration = "";
  121. if (exportHelper) {
  122. declaration += "export {}\n";
  123. }
  124. declaration += "declare global {\n" + items.map((i) => " " + i).join("\n") + "\n}";
  125. return declaration;
  126. }
  127. function stringifyImportAlias(item, isCJS = false) {
  128. return item.as === void 0 || item.name === item.as ? item.name : isCJS ? `${item.name}: ${item.as}` : `${item.name} as ${item.as}`;
  129. }
  130. function toImportModuleMap(imports) {
  131. const map = {};
  132. for (const _import of imports) {
  133. if (!map[_import.from]) {
  134. map[_import.from] = /* @__PURE__ */ new Set();
  135. }
  136. map[_import.from].add(_import);
  137. }
  138. return map;
  139. }
  140. function getString(code) {
  141. if (typeof code === "string") {
  142. return code;
  143. }
  144. return code.toString();
  145. }
  146. function getMagicString(code) {
  147. if (typeof code === "string") {
  148. return new MagicString(code);
  149. }
  150. return code;
  151. }
  152. function addImportToCode(code, imports, isCJS = false, mergeExisting = false) {
  153. let newImports = [];
  154. const s = getMagicString(code);
  155. if (mergeExisting && !isCJS) {
  156. const existing = mlly.findStaticImports(s.original).map((i) => mlly.parseStaticImport(i));
  157. const map = /* @__PURE__ */ new Map();
  158. imports.forEach((i) => {
  159. const target = existing.find((e) => e.specifier === i.from && e.imports.startsWith("{"));
  160. if (!target) {
  161. return newImports.push(i);
  162. }
  163. if (!map.has(target)) {
  164. map.set(target, []);
  165. }
  166. map.get(target).push(i);
  167. });
  168. for (const [target, items] of map.entries()) {
  169. const strings = items.map((i) => stringifyImportAlias(i) + ", ");
  170. const importLength = target.code.match(/^\s*import\s*{/)?.[0]?.length;
  171. if (importLength) {
  172. s.appendLeft(target.start + importLength, " " + strings.join("").trim());
  173. }
  174. }
  175. } else {
  176. newImports = imports;
  177. }
  178. const newEntries = toImports(newImports, isCJS);
  179. if (newEntries) {
  180. s.prepend(newEntries + "\n");
  181. }
  182. return {
  183. s,
  184. get code() {
  185. return s.toString();
  186. }
  187. };
  188. }
  189. function normalizeImports(imports) {
  190. for (const _import of imports) {
  191. _import.as = _import.as ?? _import.name;
  192. }
  193. return imports;
  194. }
  195. function resolveIdAbsolute(id, parentId) {
  196. return mlly.resolvePath(id, {
  197. url: parentId
  198. });
  199. }
  200. const contextRE = /\b_ctx\.([\w_]+)\b/g;
  201. const UNREF_KEY = "_unimport_unref_";
  202. const vueTemplateAddon = () => ({
  203. async transform(s) {
  204. if (!s.original.includes("_ctx.")) {
  205. return s;
  206. }
  207. const matches = Array.from(s.original.matchAll(contextRE));
  208. const imports = await this.getImports();
  209. const targets = [];
  210. for (const match of matches) {
  211. const name = match[1];
  212. const item = imports.find((i) => i.as === name);
  213. if (!item) {
  214. continue;
  215. }
  216. const start = match.index;
  217. const end = start + match[0].length;
  218. const tempName = `_unimport_${name}`;
  219. s.overwrite(start, end, `${UNREF_KEY}(${tempName})`);
  220. if (!targets.find((i) => i.as === tempName)) {
  221. targets.push({
  222. ...item,
  223. as: tempName
  224. });
  225. }
  226. }
  227. if (targets.length) {
  228. targets.push({
  229. name: "unref",
  230. from: "vue",
  231. as: UNREF_KEY
  232. });
  233. s.prepend(toImports(targets));
  234. }
  235. return s;
  236. },
  237. async declaration(dts, options) {
  238. const imports = await this.getImports();
  239. const items = imports.map((i) => {
  240. const from = options?.resolvePath?.(i) || i.from;
  241. return `readonly ${i.as}: UnwrapRef<typeof import('${from}')${i.name !== "*" ? `['${i.name}']` : ""}>`;
  242. }).sort();
  243. return dts + `
  244. // for vue template auto import
  245. import { UnwrapRef } from 'vue'
  246. declare module 'vue' {
  247. interface ComponentCustomProperties {
  248. ${items.map((i) => " " + i).join("\n")}
  249. }
  250. }
  251. `;
  252. }
  253. });
  254. exports.addImportToCode = addImportToCode;
  255. exports.dedupeImports = dedupeImports;
  256. exports.defineUnimportPreset = defineUnimportPreset;
  257. exports.excludeRE = excludeRE;
  258. exports.getMagicString = getMagicString;
  259. exports.getString = getString;
  260. exports.importAsRE = importAsRE;
  261. exports.matchRE = matchRE;
  262. exports.normalizeImports = normalizeImports;
  263. exports.resolveIdAbsolute = resolveIdAbsolute;
  264. exports.separatorRE = separatorRE;
  265. exports.stripCommentsAndStrings = stripCommentsAndStrings;
  266. exports.toExports = toExports;
  267. exports.toImports = toImports;
  268. exports.toTypeDeclarationFile = toTypeDeclarationFile;
  269. exports.toTypeDeclarationItems = toTypeDeclarationItems;
  270. exports.vueTemplateAddon = vueTemplateAddon;