import-injector.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _assert = require("assert");
  7. var _t = require("@babel/types");
  8. var _importBuilder = require("./import-builder.js");
  9. var _isModule = require("./is-module.js");
  10. const {
  11. identifier,
  12. importSpecifier,
  13. numericLiteral,
  14. sequenceExpression,
  15. isImportDeclaration
  16. } = _t;
  17. class ImportInjector {
  18. constructor(path, importedSource, opts) {
  19. this._defaultOpts = {
  20. importedSource: null,
  21. importedType: "commonjs",
  22. importedInterop: "babel",
  23. importingInterop: "babel",
  24. ensureLiveReference: false,
  25. ensureNoContext: false,
  26. importPosition: "before"
  27. };
  28. const programPath = path.find(p => p.isProgram());
  29. this._programPath = programPath;
  30. this._programScope = programPath.scope;
  31. this._hub = programPath.hub;
  32. this._defaultOpts = this._applyDefaults(importedSource, opts, true);
  33. }
  34. addDefault(importedSourceIn, opts) {
  35. return this.addNamed("default", importedSourceIn, opts);
  36. }
  37. addNamed(importName, importedSourceIn, opts) {
  38. _assert(typeof importName === "string");
  39. return this._generateImport(this._applyDefaults(importedSourceIn, opts), importName);
  40. }
  41. addNamespace(importedSourceIn, opts) {
  42. return this._generateImport(this._applyDefaults(importedSourceIn, opts), null);
  43. }
  44. addSideEffect(importedSourceIn, opts) {
  45. return this._generateImport(this._applyDefaults(importedSourceIn, opts), void 0);
  46. }
  47. _applyDefaults(importedSource, opts, isInit = false) {
  48. let newOpts;
  49. if (typeof importedSource === "string") {
  50. newOpts = Object.assign({}, this._defaultOpts, {
  51. importedSource
  52. }, opts);
  53. } else {
  54. _assert(!opts, "Unexpected secondary arguments.");
  55. newOpts = Object.assign({}, this._defaultOpts, importedSource);
  56. }
  57. if (!isInit && opts) {
  58. if (opts.nameHint !== undefined) newOpts.nameHint = opts.nameHint;
  59. if (opts.blockHoist !== undefined) newOpts.blockHoist = opts.blockHoist;
  60. }
  61. return newOpts;
  62. }
  63. _generateImport(opts, importName) {
  64. const isDefault = importName === "default";
  65. const isNamed = !!importName && !isDefault;
  66. const isNamespace = importName === null;
  67. const {
  68. importedSource,
  69. importedType,
  70. importedInterop,
  71. importingInterop,
  72. ensureLiveReference,
  73. ensureNoContext,
  74. nameHint,
  75. importPosition,
  76. blockHoist
  77. } = opts;
  78. let name = nameHint || importName;
  79. const isMod = (0, _isModule.default)(this._programPath);
  80. const isModuleForNode = isMod && importingInterop === "node";
  81. const isModuleForBabel = isMod && importingInterop === "babel";
  82. if (importPosition === "after" && !isMod) {
  83. throw new Error(`"importPosition": "after" is only supported in modules`);
  84. }
  85. const builder = new _importBuilder.default(importedSource, this._programScope, this._hub);
  86. if (importedType === "es6") {
  87. if (!isModuleForNode && !isModuleForBabel) {
  88. throw new Error("Cannot import an ES6 module from CommonJS");
  89. }
  90. builder.import();
  91. if (isNamespace) {
  92. builder.namespace(nameHint || importedSource);
  93. } else if (isDefault || isNamed) {
  94. builder.named(name, importName);
  95. }
  96. } else if (importedType !== "commonjs") {
  97. throw new Error(`Unexpected interopType "${importedType}"`);
  98. } else if (importedInterop === "babel") {
  99. if (isModuleForNode) {
  100. name = name !== "default" ? name : importedSource;
  101. const es6Default = `${importedSource}$es6Default`;
  102. builder.import();
  103. if (isNamespace) {
  104. builder.default(es6Default).var(name || importedSource).wildcardInterop();
  105. } else if (isDefault) {
  106. if (ensureLiveReference) {
  107. builder.default(es6Default).var(name || importedSource).defaultInterop().read("default");
  108. } else {
  109. builder.default(es6Default).var(name).defaultInterop().prop(importName);
  110. }
  111. } else if (isNamed) {
  112. builder.default(es6Default).read(importName);
  113. }
  114. } else if (isModuleForBabel) {
  115. builder.import();
  116. if (isNamespace) {
  117. builder.namespace(name || importedSource);
  118. } else if (isDefault || isNamed) {
  119. builder.named(name, importName);
  120. }
  121. } else {
  122. builder.require();
  123. if (isNamespace) {
  124. builder.var(name || importedSource).wildcardInterop();
  125. } else if ((isDefault || isNamed) && ensureLiveReference) {
  126. if (isDefault) {
  127. name = name !== "default" ? name : importedSource;
  128. builder.var(name).read(importName);
  129. builder.defaultInterop();
  130. } else {
  131. builder.var(importedSource).read(importName);
  132. }
  133. } else if (isDefault) {
  134. builder.var(name).defaultInterop().prop(importName);
  135. } else if (isNamed) {
  136. builder.var(name).prop(importName);
  137. }
  138. }
  139. } else if (importedInterop === "compiled") {
  140. if (isModuleForNode) {
  141. builder.import();
  142. if (isNamespace) {
  143. builder.default(name || importedSource);
  144. } else if (isDefault || isNamed) {
  145. builder.default(importedSource).read(name);
  146. }
  147. } else if (isModuleForBabel) {
  148. builder.import();
  149. if (isNamespace) {
  150. builder.namespace(name || importedSource);
  151. } else if (isDefault || isNamed) {
  152. builder.named(name, importName);
  153. }
  154. } else {
  155. builder.require();
  156. if (isNamespace) {
  157. builder.var(name || importedSource);
  158. } else if (isDefault || isNamed) {
  159. if (ensureLiveReference) {
  160. builder.var(importedSource).read(name);
  161. } else {
  162. builder.prop(importName).var(name);
  163. }
  164. }
  165. }
  166. } else if (importedInterop === "uncompiled") {
  167. if (isDefault && ensureLiveReference) {
  168. throw new Error("No live reference for commonjs default");
  169. }
  170. if (isModuleForNode) {
  171. builder.import();
  172. if (isNamespace) {
  173. builder.default(name || importedSource);
  174. } else if (isDefault) {
  175. builder.default(name);
  176. } else if (isNamed) {
  177. builder.default(importedSource).read(name);
  178. }
  179. } else if (isModuleForBabel) {
  180. builder.import();
  181. if (isNamespace) {
  182. builder.default(name || importedSource);
  183. } else if (isDefault) {
  184. builder.default(name);
  185. } else if (isNamed) {
  186. builder.named(name, importName);
  187. }
  188. } else {
  189. builder.require();
  190. if (isNamespace) {
  191. builder.var(name || importedSource);
  192. } else if (isDefault) {
  193. builder.var(name);
  194. } else if (isNamed) {
  195. if (ensureLiveReference) {
  196. builder.var(importedSource).read(name);
  197. } else {
  198. builder.var(name).prop(importName);
  199. }
  200. }
  201. }
  202. } else {
  203. throw new Error(`Unknown importedInterop "${importedInterop}".`);
  204. }
  205. const {
  206. statements,
  207. resultName
  208. } = builder.done();
  209. this._insertStatements(statements, importPosition, blockHoist);
  210. if ((isDefault || isNamed) && ensureNoContext && resultName.type !== "Identifier") {
  211. return sequenceExpression([numericLiteral(0), resultName]);
  212. }
  213. return resultName;
  214. }
  215. _insertStatements(statements, importPosition = "before", blockHoist = 3) {
  216. if (importPosition === "after") {
  217. if (this._insertStatementsAfter(statements)) return;
  218. } else {
  219. if (this._insertStatementsBefore(statements, blockHoist)) return;
  220. }
  221. this._programPath.unshiftContainer("body", statements);
  222. }
  223. _insertStatementsBefore(statements, blockHoist) {
  224. if (statements.length === 1 && isImportDeclaration(statements[0]) && isValueImport(statements[0])) {
  225. const firstImportDecl = this._programPath.get("body").find(p => {
  226. return p.isImportDeclaration() && isValueImport(p.node);
  227. });
  228. if ((firstImportDecl == null ? void 0 : firstImportDecl.node.source.value) === statements[0].source.value && maybeAppendImportSpecifiers(firstImportDecl.node, statements[0])) {
  229. return true;
  230. }
  231. }
  232. statements.forEach(node => {
  233. node._blockHoist = blockHoist;
  234. });
  235. const targetPath = this._programPath.get("body").find(p => {
  236. const val = p.node._blockHoist;
  237. return Number.isFinite(val) && val < 4;
  238. });
  239. if (targetPath) {
  240. targetPath.insertBefore(statements);
  241. return true;
  242. }
  243. return false;
  244. }
  245. _insertStatementsAfter(statements) {
  246. const statementsSet = new Set(statements);
  247. const importDeclarations = new Map();
  248. for (const statement of statements) {
  249. if (isImportDeclaration(statement) && isValueImport(statement)) {
  250. const source = statement.source.value;
  251. if (!importDeclarations.has(source)) importDeclarations.set(source, []);
  252. importDeclarations.get(source).push(statement);
  253. }
  254. }
  255. let lastImportPath = null;
  256. for (const bodyStmt of this._programPath.get("body")) {
  257. if (bodyStmt.isImportDeclaration() && isValueImport(bodyStmt.node)) {
  258. lastImportPath = bodyStmt;
  259. const source = bodyStmt.node.source.value;
  260. const newImports = importDeclarations.get(source);
  261. if (!newImports) continue;
  262. for (const decl of newImports) {
  263. if (!statementsSet.has(decl)) continue;
  264. if (maybeAppendImportSpecifiers(bodyStmt.node, decl)) {
  265. statementsSet.delete(decl);
  266. }
  267. }
  268. }
  269. }
  270. if (statementsSet.size === 0) return true;
  271. if (lastImportPath) lastImportPath.insertAfter(Array.from(statementsSet));
  272. return !!lastImportPath;
  273. }
  274. }
  275. exports.default = ImportInjector;
  276. function isValueImport(node) {
  277. return node.importKind !== "type" && node.importKind !== "typeof";
  278. }
  279. function hasNamespaceImport(node) {
  280. return node.specifiers.length === 1 && node.specifiers[0].type === "ImportNamespaceSpecifier" || node.specifiers.length === 2 && node.specifiers[1].type === "ImportNamespaceSpecifier";
  281. }
  282. function hasDefaultImport(node) {
  283. return node.specifiers.length > 0 && node.specifiers[0].type === "ImportDefaultSpecifier";
  284. }
  285. function maybeAppendImportSpecifiers(target, source) {
  286. if (!target.specifiers.length) {
  287. target.specifiers = source.specifiers;
  288. return true;
  289. }
  290. if (!source.specifiers.length) return true;
  291. if (hasNamespaceImport(target) || hasNamespaceImport(source)) return false;
  292. if (hasDefaultImport(source)) {
  293. if (hasDefaultImport(target)) {
  294. source.specifiers[0] = importSpecifier(source.specifiers[0].local, identifier("default"));
  295. } else {
  296. target.specifiers.unshift(source.specifiers.shift());
  297. }
  298. }
  299. target.specifiers.push(...source.specifiers);
  300. return true;
  301. }
  302. //# sourceMappingURL=import-injector.js.map