unimport.a78aa044.cjs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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. function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
  7. const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
  8. const excludeRE = [
  9. // imported/exported from other module
  10. /\b(import|export)\b([\w$*{},\s]+?)\bfrom\s*["']/g,
  11. // defined as function
  12. /\bfunction\s*([\w$]+)\s*\(/g,
  13. // defined as class
  14. /\bclass\s*([\w$]+)\s*\{/g,
  15. // defined as local variable
  16. // eslint-disable-next-line regexp/no-super-linear-backtracking
  17. /\b(?:const|let|var)\s+?(\[.*?\]|\{.*?\}|.+?)\s*?[=;\n]/gs
  18. ];
  19. const importAsRE = /^.*\sas\s+/;
  20. const separatorRE = /[,[\]{}\n]|\b(?:import|export)\b/g;
  21. const matchRE = /(^|\.\.\.|(?:\bcase|\?)\s+|[^\w$/)]|\bextends\s+)([\w$]+)\s*(?=[.()[\]}:;?+\-*&|`<>,\n]|\b(?:instanceof|in)\b|$|(?<=extends\s+\w+)\s+\{)/g;
  22. const regexRE = /\/\S*?(?<!\\)(?<!\[[^\]]*)\/[gimsuy]*/g;
  23. function stripCommentsAndStrings(code, options) {
  24. return stripLiteral.stripLiteral(code, options).replace(regexRE, 'new RegExp("")');
  25. }
  26. function defineUnimportPreset(preset) {
  27. return preset;
  28. }
  29. const safePropertyName = /^[a-z$_][\w$]*$/i;
  30. function stringifyWith(withValues) {
  31. let withDefs = "";
  32. for (let entries = Object.entries(withValues), l = entries.length, i = 0; i < l; i++) {
  33. const [prop, value] = entries[i];
  34. withDefs += safePropertyName.test(prop) ? prop : JSON.stringify(prop);
  35. withDefs += `: ${JSON.stringify(String(value))}`;
  36. if (i + 1 !== l)
  37. withDefs += ", ";
  38. }
  39. return `{ ${withDefs} }`;
  40. }
  41. function stringifyImports(imports, isCJS = false) {
  42. const map = toImportModuleMap(imports);
  43. return Object.entries(map).flatMap(([name, importSet]) => {
  44. const entries = [];
  45. const imports2 = Array.from(importSet).filter((i) => {
  46. if (!i.name || i.as === "") {
  47. let importStr;
  48. if (isCJS) {
  49. importStr = `require('${name}');`;
  50. } else {
  51. importStr = `import '${name}'`;
  52. if (i.with)
  53. importStr += ` with ${stringifyWith(i.with)}`;
  54. importStr += ";";
  55. }
  56. entries.push(importStr);
  57. return false;
  58. } else if (i.name === "default" || i.name === "=") {
  59. let importStr;
  60. if (isCJS) {
  61. importStr = i.name === "=" ? `const ${i.as} = require('${name}');` : `const { default: ${i.as} } = require('${name}');`;
  62. } else {
  63. importStr = `import ${i.as} from '${name}'`;
  64. if (i.with)
  65. importStr += ` with ${stringifyWith(i.with)}`;
  66. importStr += ";";
  67. }
  68. entries.push(importStr);
  69. return false;
  70. } else if (i.name === "*") {
  71. let importStr;
  72. if (isCJS) {
  73. importStr = `const ${i.as} = require('${name}');`;
  74. } else {
  75. importStr = `import * as ${i.as} from '${name}'`;
  76. if (i.with)
  77. importStr += ` with ${stringifyWith(i.with)}`;
  78. importStr += ";";
  79. }
  80. entries.push(importStr);
  81. return false;
  82. } else if (!isCJS && i.with) {
  83. entries.push(`import { ${stringifyImportAlias(i)} } from '${name}' with ${stringifyWith(i.with)};`);
  84. return false;
  85. }
  86. return true;
  87. });
  88. if (imports2.length) {
  89. const importsAs = imports2.map((i) => stringifyImportAlias(i, isCJS));
  90. entries.push(
  91. isCJS ? `const { ${importsAs.join(", ")} } = require('${name}');` : `import { ${importsAs.join(", ")} } from '${name}';`
  92. );
  93. }
  94. return entries;
  95. }).join("\n");
  96. }
  97. function dedupeImports(imports, warn) {
  98. const map = /* @__PURE__ */ new Map();
  99. const indexToRemove = /* @__PURE__ */ new Set();
  100. imports.filter((i) => !i.disabled).forEach((i, idx) => {
  101. if (i.declarationType === "enum")
  102. return;
  103. const name = i.as ?? i.name;
  104. if (!map.has(name)) {
  105. map.set(name, idx);
  106. return;
  107. }
  108. const other = imports[map.get(name)];
  109. if (other.from === i.from) {
  110. indexToRemove.add(idx);
  111. return;
  112. }
  113. const diff = (other.priority || 1) - (i.priority || 1);
  114. if (diff === 0)
  115. warn(`Duplicated imports "${name}", the one from "${other.from}" has been ignored and "${i.from}" is used`);
  116. if (diff <= 0) {
  117. indexToRemove.add(map.get(name));
  118. map.set(name, idx);
  119. } else {
  120. indexToRemove.add(idx);
  121. }
  122. });
  123. return imports.filter((_, idx) => !indexToRemove.has(idx));
  124. }
  125. function toExports(imports, fileDir, includeType = false) {
  126. const map = toImportModuleMap(imports, includeType);
  127. return Object.entries(map).flatMap(([name, imports2]) => {
  128. if (isFilePath(name))
  129. name = name.replace(/\.[a-z]+$/i, "");
  130. if (fileDir && pathe.isAbsolute(name)) {
  131. name = pathe.relative(fileDir, name);
  132. if (!name.match(/^[./]/))
  133. name = `./${name}`;
  134. }
  135. const entries = [];
  136. const filtered = Array.from(imports2).filter((i) => {
  137. if (i.name === "*") {
  138. entries.push(`export * as ${i.as} from '${name}';`);
  139. return false;
  140. }
  141. return true;
  142. });
  143. if (filtered.length)
  144. entries.push(`export { ${filtered.map((i) => stringifyImportAlias(i, false)).join(", ")} } from '${name}';`);
  145. return entries;
  146. }).join("\n");
  147. }
  148. function stripFileExtension(path) {
  149. return path.replace(/\.[a-z]+$/i, "");
  150. }
  151. function toTypeDeclarationItems(imports, options) {
  152. return imports.map((i) => {
  153. const from = options?.resolvePath?.(i) || stripFileExtension(i.typeFrom || i.from);
  154. let typeDef = "";
  155. if (i.with)
  156. typeDef += `import('${from}', { with: ${stringifyWith(i.with)} })`;
  157. else
  158. typeDef += `import('${from}')`;
  159. if (i.name !== "*" && i.name !== "=")
  160. typeDef += `['${i.name}']`;
  161. return `const ${i.as}: typeof ${typeDef}`;
  162. }).sort();
  163. }
  164. function toTypeDeclarationFile(imports, options) {
  165. const items = toTypeDeclarationItems(imports, options);
  166. const {
  167. exportHelper = true
  168. } = options || {};
  169. let declaration = "";
  170. if (exportHelper)
  171. declaration += "export {}\n";
  172. declaration += `declare global {
  173. ${items.map((i) => ` ${i}`).join("\n")}
  174. }`;
  175. return declaration;
  176. }
  177. function toTypeReExports(imports, options) {
  178. const importsMap = /* @__PURE__ */ new Map();
  179. imports.forEach((i) => {
  180. const from = options?.resolvePath?.(i) || stripFileExtension(i.typeFrom || i.from);
  181. const list = importsMap.get(from) || [];
  182. list.push(i);
  183. importsMap.set(from, list);
  184. });
  185. const code = Array.from(importsMap.entries()).flatMap(([from, items]) => {
  186. const names = items.map((i) => {
  187. let name = i.name === "*" ? "default" : i.name;
  188. if (i.as && i.as !== name)
  189. name += ` as ${i.as}`;
  190. return name;
  191. });
  192. return [
  193. // Because of TypeScript's limitation, it errors when re-exporting type in declare.
  194. // But it actually works so we use @ts-ignore to dismiss the error.
  195. "// @ts-ignore",
  196. // Re-export type
  197. `export type { ${names.join(", ")} } from '${from}'`,
  198. // If a module is only been re-exported as type, TypeScript will not initialize it for some reason.
  199. // Adding an import statement will fix it.
  200. `import('${from}')`
  201. ];
  202. });
  203. return `// for type re-export
  204. declare global {
  205. ${code.map((i) => ` ${i}`).join("\n")}
  206. }`;
  207. }
  208. function stringifyImportAlias(item, isCJS = false) {
  209. return item.as === void 0 || item.name === item.as ? item.name : isCJS ? `${item.name}: ${item.as}` : `${item.name} as ${item.as}`;
  210. }
  211. function toImportModuleMap(imports, includeType = false) {
  212. const map = {};
  213. for (const _import of imports) {
  214. if (_import.type && !includeType)
  215. continue;
  216. if (!map[_import.from])
  217. map[_import.from] = /* @__PURE__ */ new Set();
  218. map[_import.from].add(_import);
  219. }
  220. return map;
  221. }
  222. function getString(code) {
  223. if (typeof code === "string")
  224. return code;
  225. return code.toString();
  226. }
  227. function getMagicString(code) {
  228. if (typeof code === "string")
  229. return new MagicString__default(code);
  230. return code;
  231. }
  232. function addImportToCode(code, imports, isCJS = false, mergeExisting = false, injectAtLast = false, firstOccurrence = Number.POSITIVE_INFINITY, onResolved, onStringified) {
  233. let newImports = [];
  234. const s = getMagicString(code);
  235. let _staticImports;
  236. const strippedCode = stripCommentsAndStrings(s.original);
  237. function findStaticImportsLazy() {
  238. if (!_staticImports) {
  239. _staticImports = mlly.findStaticImports(s.original).filter((i) => Boolean(strippedCode.slice(i.start, i.end).trim())).map((i) => mlly.parseStaticImport(i));
  240. }
  241. return _staticImports;
  242. }
  243. function hasShebang() {
  244. const shebangRegex = /^#!.+/;
  245. return shebangRegex.test(s.original);
  246. }
  247. if (mergeExisting && !isCJS) {
  248. const existingImports = findStaticImportsLazy();
  249. const map = /* @__PURE__ */ new Map();
  250. imports.forEach((i) => {
  251. const target = existingImports.find((e) => e.specifier === i.from && e.imports.startsWith("{"));
  252. if (!target)
  253. return newImports.push(i);
  254. if (!map.has(target))
  255. map.set(target, []);
  256. map.get(target).push(i);
  257. });
  258. for (const [target, items] of map.entries()) {
  259. const strings = items.map((i) => `${stringifyImportAlias(i)}, `);
  260. const importLength = target.code.match(/^\s*import\s*\{/)?.[0]?.length;
  261. if (importLength)
  262. s.appendLeft(target.start + importLength, ` ${strings.join("").trim()}`);
  263. }
  264. } else {
  265. newImports = imports;
  266. }
  267. newImports = onResolved?.(newImports) ?? newImports;
  268. let newEntries = stringifyImports(newImports, isCJS);
  269. newEntries = onStringified?.(newEntries, newImports) ?? newEntries;
  270. if (newEntries) {
  271. const insertionIndex = injectAtLast ? findStaticImportsLazy().reverse().find((i) => i.end <= firstOccurrence)?.end ?? 0 : 0;
  272. if (insertionIndex > 0)
  273. s.appendRight(insertionIndex, `
  274. ${newEntries}
  275. `);
  276. else if (hasShebang())
  277. s.appendLeft(s.original.indexOf("\n") + 1, `
  278. ${newEntries}
  279. `);
  280. else
  281. s.prepend(`${newEntries}
  282. `);
  283. }
  284. return {
  285. s,
  286. get code() {
  287. return s.toString();
  288. }
  289. };
  290. }
  291. function normalizeImports(imports) {
  292. for (const _import of imports)
  293. _import.as = _import.as ?? _import.name;
  294. return imports;
  295. }
  296. function resolveIdAbsolute(id, parentId) {
  297. return mlly.resolvePath(id, {
  298. url: parentId
  299. });
  300. }
  301. function isFilePath(path) {
  302. return path.startsWith(".") || pathe.isAbsolute(path) || path.includes("://");
  303. }
  304. const toImports = stringifyImports;
  305. const contextRE = /\b_ctx\.([$\w]+)\b/g;
  306. const UNREF_KEY = "__unimport_unref_";
  307. function vueTemplateAddon() {
  308. const self = {
  309. async transform(s, id) {
  310. if (!s.original.includes("_ctx.") || s.original.includes(UNREF_KEY))
  311. return s;
  312. const matches = Array.from(s.original.matchAll(contextRE));
  313. const imports = await this.getImports();
  314. let targets = [];
  315. for (const match of matches) {
  316. const name = match[1];
  317. const item = imports.find((i) => i.as === name);
  318. if (!item)
  319. continue;
  320. const start = match.index;
  321. const end = start + match[0].length;
  322. const tempName = `__unimport_${name}`;
  323. s.overwrite(start, end, `(${JSON.stringify(name)} in _ctx ? _ctx.${name} : ${UNREF_KEY}(${tempName}))`);
  324. if (!targets.find((i) => i.as === tempName)) {
  325. targets.push({
  326. ...item,
  327. as: tempName
  328. });
  329. }
  330. }
  331. if (targets.length) {
  332. targets.push({
  333. name: "unref",
  334. from: "vue",
  335. as: UNREF_KEY
  336. });
  337. for (const addon of this.addons) {
  338. if (addon === self)
  339. continue;
  340. targets = await addon.injectImportsResolved?.call(this, targets, s, id) ?? targets;
  341. }
  342. let injection = stringifyImports(targets);
  343. for (const addon of this.addons) {
  344. if (addon === self)
  345. continue;
  346. injection = await addon.injectImportsStringified?.call(this, injection, targets, s, id) ?? injection;
  347. }
  348. s.prepend(injection);
  349. }
  350. return s;
  351. },
  352. async declaration(dts, options) {
  353. const imports = await this.getImports();
  354. const items = imports.map((i) => {
  355. if (i.type || i.dtsDisabled)
  356. return "";
  357. const from = options?.resolvePath?.(i) || i.from;
  358. return `readonly ${i.as}: UnwrapRef<typeof import('${from}')${i.name !== "*" ? `['${i.name}']` : ""}>`;
  359. }).filter(Boolean).sort();
  360. const extendItems = items.map((i) => ` ${i}`).join("\n");
  361. return `${dts}
  362. // for vue template auto import
  363. import { UnwrapRef } from 'vue'
  364. declare module 'vue' {
  365. interface ComponentCustomProperties {
  366. ${extendItems}
  367. }
  368. }`;
  369. }
  370. };
  371. return self;
  372. }
  373. exports.addImportToCode = addImportToCode;
  374. exports.dedupeImports = dedupeImports;
  375. exports.defineUnimportPreset = defineUnimportPreset;
  376. exports.excludeRE = excludeRE;
  377. exports.getMagicString = getMagicString;
  378. exports.getString = getString;
  379. exports.importAsRE = importAsRE;
  380. exports.matchRE = matchRE;
  381. exports.normalizeImports = normalizeImports;
  382. exports.resolveIdAbsolute = resolveIdAbsolute;
  383. exports.separatorRE = separatorRE;
  384. exports.stringifyImports = stringifyImports;
  385. exports.stripCommentsAndStrings = stripCommentsAndStrings;
  386. exports.stripFileExtension = stripFileExtension;
  387. exports.toExports = toExports;
  388. exports.toImports = toImports;
  389. exports.toTypeDeclarationFile = toTypeDeclarationFile;
  390. exports.toTypeDeclarationItems = toTypeDeclarationItems;
  391. exports.toTypeReExports = toTypeReExports;
  392. exports.vueTemplateAddon = vueTemplateAddon;