utils.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. "use strict";
  2. exports.__esModule = true;
  3. exports.createUtilsGetter = createUtilsGetter;
  4. exports.getImportSource = getImportSource;
  5. exports.getRequireSource = getRequireSource;
  6. exports.has = has;
  7. exports.intersection = intersection;
  8. exports.resolveInstance = resolveInstance;
  9. exports.resolveKey = resolveKey;
  10. exports.resolveSource = resolveSource;
  11. var _babel = _interopRequireWildcard(require("@babel/core"));
  12. function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
  13. const {
  14. types: t,
  15. template: template
  16. } = _babel.default || _babel;
  17. function intersection(a, b) {
  18. const result = new Set();
  19. a.forEach(v => b.has(v) && result.add(v));
  20. return result;
  21. }
  22. function has(object, key) {
  23. return Object.prototype.hasOwnProperty.call(object, key);
  24. }
  25. function resolve(path, resolved = new Set()) {
  26. if (resolved.has(path)) return;
  27. resolved.add(path);
  28. if (path.isVariableDeclarator()) {
  29. if (path.get("id").isIdentifier()) {
  30. return resolve(path.get("init"), resolved);
  31. }
  32. } else if (path.isReferencedIdentifier()) {
  33. const binding = path.scope.getBinding(path.node.name);
  34. if (!binding) return path;
  35. if (!binding.constant) return;
  36. return resolve(binding.path, resolved);
  37. }
  38. return path;
  39. }
  40. function resolveId(path) {
  41. if (path.isIdentifier() && !path.scope.hasBinding(path.node.name, /* noGlobals */true)) {
  42. return path.node.name;
  43. }
  44. const resolved = resolve(path);
  45. if (resolved != null && resolved.isIdentifier()) {
  46. return resolved.node.name;
  47. }
  48. }
  49. function resolveKey(path, computed = false) {
  50. const {
  51. scope
  52. } = path;
  53. if (path.isStringLiteral()) return path.node.value;
  54. const isIdentifier = path.isIdentifier();
  55. if (isIdentifier && !(computed || path.parent.computed)) {
  56. return path.node.name;
  57. }
  58. if (computed && path.isMemberExpression() && path.get("object").isIdentifier({
  59. name: "Symbol"
  60. }) && !scope.hasBinding("Symbol", /* noGlobals */true)) {
  61. const sym = resolveKey(path.get("property"), path.node.computed);
  62. if (sym) return "Symbol." + sym;
  63. }
  64. if (isIdentifier ? scope.hasBinding(path.node.name, /* noGlobals */true) : path.isPure()) {
  65. const {
  66. value
  67. } = path.evaluate();
  68. if (typeof value === "string") return value;
  69. }
  70. }
  71. function resolveInstance(obj) {
  72. const source = resolveSource(obj);
  73. return source.placement === "prototype" ? source.id : null;
  74. }
  75. function resolveSource(obj) {
  76. if (obj.isMemberExpression() && obj.get("property").isIdentifier({
  77. name: "prototype"
  78. })) {
  79. const id = resolveId(obj.get("object"));
  80. if (id) {
  81. return {
  82. id,
  83. placement: "prototype"
  84. };
  85. }
  86. return {
  87. id: null,
  88. placement: null
  89. };
  90. }
  91. const id = resolveId(obj);
  92. if (id) {
  93. return {
  94. id,
  95. placement: "static"
  96. };
  97. }
  98. const path = resolve(obj);
  99. switch (path == null ? void 0 : path.type) {
  100. case "NullLiteral":
  101. return {
  102. id: null,
  103. placement: null
  104. };
  105. case "RegExpLiteral":
  106. return {
  107. id: "RegExp",
  108. placement: "prototype"
  109. };
  110. case "StringLiteral":
  111. case "TemplateLiteral":
  112. return {
  113. id: "String",
  114. placement: "prototype"
  115. };
  116. case "NumericLiteral":
  117. return {
  118. id: "Number",
  119. placement: "prototype"
  120. };
  121. case "BooleanLiteral":
  122. return {
  123. id: "Boolean",
  124. placement: "prototype"
  125. };
  126. case "BigIntLiteral":
  127. return {
  128. id: "BigInt",
  129. placement: "prototype"
  130. };
  131. case "ObjectExpression":
  132. return {
  133. id: "Object",
  134. placement: "prototype"
  135. };
  136. case "ArrayExpression":
  137. return {
  138. id: "Array",
  139. placement: "prototype"
  140. };
  141. case "FunctionExpression":
  142. case "ArrowFunctionExpression":
  143. case "ClassExpression":
  144. return {
  145. id: "Function",
  146. placement: "prototype"
  147. };
  148. // new Constructor() -> resolve the constructor name
  149. case "NewExpression":
  150. {
  151. const calleeId = resolveId(path.get("callee"));
  152. if (calleeId) return {
  153. id: calleeId,
  154. placement: "prototype"
  155. };
  156. return {
  157. id: null,
  158. placement: null
  159. };
  160. }
  161. // Unary expressions -> result type depends on operator
  162. case "UnaryExpression":
  163. {
  164. const {
  165. operator
  166. } = path.node;
  167. if (operator === "typeof") return {
  168. id: "String",
  169. placement: "prototype"
  170. };
  171. if (operator === "!" || operator === "delete") return {
  172. id: "Boolean",
  173. placement: "prototype"
  174. };
  175. // Unary + always produces Number (throws on BigInt)
  176. if (operator === "+") return {
  177. id: "Number",
  178. placement: "prototype"
  179. };
  180. // Unary - and ~ can produce Number or BigInt depending on operand
  181. if (operator === "-" || operator === "~") {
  182. const arg = resolveInstance(path.get("argument"));
  183. if (arg === "BigInt") return {
  184. id: "BigInt",
  185. placement: "prototype"
  186. };
  187. if (arg !== null) return {
  188. id: "Number",
  189. placement: "prototype"
  190. };
  191. return {
  192. id: null,
  193. placement: null
  194. };
  195. }
  196. return {
  197. id: null,
  198. placement: null
  199. };
  200. }
  201. // ++i, i++ produce Number or BigInt depending on the argument
  202. case "UpdateExpression":
  203. {
  204. const arg = resolveInstance(path.get("argument"));
  205. if (arg === "BigInt") return {
  206. id: "BigInt",
  207. placement: "prototype"
  208. };
  209. if (arg !== null) return {
  210. id: "Number",
  211. placement: "prototype"
  212. };
  213. return {
  214. id: null,
  215. placement: null
  216. };
  217. }
  218. // Binary expressions -> result type depends on operator
  219. case "BinaryExpression":
  220. {
  221. const {
  222. operator
  223. } = path.node;
  224. if (operator === "==" || operator === "!=" || operator === "===" || operator === "!==" || operator === "<" || operator === ">" || operator === "<=" || operator === ">=" || operator === "instanceof" || operator === "in") {
  225. return {
  226. id: "Boolean",
  227. placement: "prototype"
  228. };
  229. }
  230. // >>> always produces Number
  231. if (operator === ">>>") {
  232. return {
  233. id: "Number",
  234. placement: "prototype"
  235. };
  236. }
  237. // Arithmetic and bitwise operators can produce Number or BigInt
  238. if (operator === "-" || operator === "*" || operator === "/" || operator === "%" || operator === "**" || operator === "&" || operator === "|" || operator === "^" || operator === "<<" || operator === ">>") {
  239. const left = resolveInstance(path.get("left"));
  240. const right = resolveInstance(path.get("right"));
  241. if (left === "BigInt" && right === "BigInt") {
  242. return {
  243. id: "BigInt",
  244. placement: "prototype"
  245. };
  246. }
  247. if (left !== null && right !== null) {
  248. return {
  249. id: "Number",
  250. placement: "prototype"
  251. };
  252. }
  253. return {
  254. id: null,
  255. placement: null
  256. };
  257. }
  258. // + depends on operand types: string wins, otherwise number or bigint
  259. if (operator === "+") {
  260. const left = resolveInstance(path.get("left"));
  261. const right = resolveInstance(path.get("right"));
  262. if (left === "String" || right === "String") {
  263. return {
  264. id: "String",
  265. placement: "prototype"
  266. };
  267. }
  268. if (left === "Number" && right === "Number") {
  269. return {
  270. id: "Number",
  271. placement: "prototype"
  272. };
  273. }
  274. if (left === "BigInt" && right === "BigInt") {
  275. return {
  276. id: "BigInt",
  277. placement: "prototype"
  278. };
  279. }
  280. }
  281. return {
  282. id: null,
  283. placement: null
  284. };
  285. }
  286. // (a, b, c) -> the result is the last expression
  287. case "SequenceExpression":
  288. {
  289. const expressions = path.get("expressions");
  290. return resolveSource(expressions[expressions.length - 1]);
  291. }
  292. // a = b -> the result is the right side
  293. case "AssignmentExpression":
  294. {
  295. if (path.node.operator === "=") {
  296. return resolveSource(path.get("right"));
  297. }
  298. return {
  299. id: null,
  300. placement: null
  301. };
  302. }
  303. // a ? b : c -> if both branches resolve to the same type, use it
  304. case "ConditionalExpression":
  305. {
  306. const consequent = resolveSource(path.get("consequent"));
  307. const alternate = resolveSource(path.get("alternate"));
  308. if (consequent.id && consequent.id === alternate.id) {
  309. return consequent;
  310. }
  311. return {
  312. id: null,
  313. placement: null
  314. };
  315. }
  316. // (expr) -> unwrap parenthesized expressions
  317. case "ParenthesizedExpression":
  318. return resolveSource(path.get("expression"));
  319. // TypeScript / Flow type wrappers -> unwrap to the inner expression
  320. case "TSAsExpression":
  321. case "TSSatisfiesExpression":
  322. case "TSNonNullExpression":
  323. case "TSInstantiationExpression":
  324. case "TSTypeAssertion":
  325. case "TypeCastExpression":
  326. return resolveSource(path.get("expression"));
  327. }
  328. return {
  329. id: null,
  330. placement: null
  331. };
  332. }
  333. function getImportSource({
  334. node
  335. }) {
  336. if (node.specifiers.length === 0) return node.source.value;
  337. }
  338. function getRequireSource({
  339. node
  340. }) {
  341. if (!t.isExpressionStatement(node)) return;
  342. const {
  343. expression
  344. } = node;
  345. if (t.isCallExpression(expression) && t.isIdentifier(expression.callee) && expression.callee.name === "require" && expression.arguments.length === 1 && t.isStringLiteral(expression.arguments[0])) {
  346. return expression.arguments[0].value;
  347. }
  348. }
  349. function hoist(node) {
  350. // @ts-expect-error
  351. node._blockHoist = 3;
  352. return node;
  353. }
  354. function createUtilsGetter(cache) {
  355. return path => {
  356. const prog = path.findParent(p => p.isProgram());
  357. return {
  358. injectGlobalImport(url, moduleName) {
  359. cache.storeAnonymous(prog, url, moduleName, (isScript, source) => {
  360. return isScript ? template.statement.ast`require(${source})` : t.importDeclaration([], source);
  361. });
  362. },
  363. injectNamedImport(url, name, hint = name, moduleName) {
  364. return cache.storeNamed(prog, url, name, moduleName, (isScript, source, name) => {
  365. const id = prog.scope.generateUidIdentifier(hint);
  366. return {
  367. node: isScript ? hoist(template.statement.ast`
  368. var ${id} = require(${source}).${name}
  369. `) : t.importDeclaration([t.importSpecifier(id, name)], source),
  370. name: id.name
  371. };
  372. });
  373. },
  374. injectDefaultImport(url, hint = url, moduleName) {
  375. return cache.storeNamed(prog, url, "default", moduleName, (isScript, source) => {
  376. const id = prog.scope.generateUidIdentifier(hint);
  377. return {
  378. node: isScript ? hoist(template.statement.ast`var ${id} = require(${source})`) : t.importDeclaration([t.importDefaultSpecifier(id)], source),
  379. name: id.name
  380. };
  381. });
  382. }
  383. };
  384. };
  385. }