inject.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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.uniViteInjectPlugin = void 0;
  7. const path_1 = require("path");
  8. const debug_1 = __importDefault(require("debug"));
  9. const pluginutils_1 = require("@rollup/pluginutils");
  10. const estree_walker_1 = require("estree-walker");
  11. const shared_1 = require("@vue/shared");
  12. const magic_string_1 = __importDefault(require("magic-string"));
  13. const utils_1 = require("../utils");
  14. const debugInject = (0, debug_1.default)('uni:inject');
  15. const debugInjectTry = (0, debug_1.default)('uni:inject-try');
  16. function uniViteInjectPlugin(name, options) {
  17. if (!options)
  18. throw new Error('Missing options');
  19. const filter = (0, pluginutils_1.createFilter)(options.include, options.exclude);
  20. const modules = (0, shared_1.extend)({}, options);
  21. delete modules.include;
  22. delete modules.exclude;
  23. delete modules.sourceMap;
  24. delete modules.callback;
  25. const reassignments = new Set();
  26. const modulesMap = new Map();
  27. const namespaceModulesMap = new Map();
  28. Object.keys(modules).forEach((name) => {
  29. if (name.endsWith('.')) {
  30. namespaceModulesMap.set(name, modules[name]);
  31. }
  32. modulesMap.set(name, modules[name]);
  33. });
  34. const hasNamespace = namespaceModulesMap.size > 0;
  35. // Fix paths on Windows
  36. if (path_1.sep !== '/') {
  37. normalizeModulesMap(modulesMap);
  38. normalizeModulesMap(namespaceModulesMap);
  39. }
  40. const firstpass = new RegExp(`(?:${Array.from(modulesMap.keys()).map(escape).join('|')})`, 'g');
  41. const sourceMap = options.sourceMap !== false;
  42. const callback = options.callback;
  43. return {
  44. name,
  45. // 确保在 commonjs 之后,否则会混合 es6 module 与 cjs 的代码,导致 commonjs 失效
  46. enforce: options.enforce ?? 'post',
  47. transform(code, id) {
  48. if (!filter(id))
  49. return null;
  50. if (!(0, utils_1.isJsFile)(id))
  51. return null;
  52. debugInjectTry(id);
  53. if (code.search(firstpass) === -1)
  54. return null;
  55. if (path_1.sep !== '/')
  56. id = id.split(path_1.sep).join('/');
  57. const ast = this.parse(code);
  58. const imports = new Set();
  59. ast.body.forEach((node) => {
  60. if (node.type === 'ImportDeclaration') {
  61. node.specifiers.forEach((specifier) => {
  62. imports.add(specifier.local.name);
  63. });
  64. }
  65. });
  66. // analyse scopes
  67. let scope = (0, pluginutils_1.attachScopes)(ast, 'scope');
  68. const magicString = new magic_string_1.default(code);
  69. const newImports = new Map();
  70. function handleReference(node, name, keypath, parent) {
  71. let mod = modulesMap.get(keypath);
  72. if (!mod && hasNamespace) {
  73. const mods = keypath.split('.');
  74. if (mods.length === 2) {
  75. mod = namespaceModulesMap.get(mods[0] + '.');
  76. if (mod) {
  77. if ((0, shared_1.isArray)(mod)) {
  78. const testFn = mod[1];
  79. if (testFn(mods[1])) {
  80. mod = [mod[0], mods[1]];
  81. }
  82. else {
  83. mod = undefined;
  84. }
  85. }
  86. else {
  87. mod = [mod, mods[1]];
  88. }
  89. }
  90. }
  91. }
  92. if (mod && !imports.has(name) && !scope.contains(name)) {
  93. if ((0, shared_1.isString)(mod))
  94. mod = [mod, 'default'];
  95. if (mod[0] === id)
  96. return false;
  97. const hash = `${keypath}:${mod[0]}:${mod[1]}`;
  98. // 当 API 被覆盖定义后,不再摇树
  99. if (reassignments.has(hash)) {
  100. return false;
  101. }
  102. if (parent &&
  103. (0, utils_1.isAssignmentExpression)(parent) &&
  104. parent.left === node) {
  105. reassignments.add(hash);
  106. return false;
  107. }
  108. const importLocalName = name === keypath ? name : (0, pluginutils_1.makeLegalIdentifier)(`$inject_${keypath}`);
  109. if (!newImports.has(hash)) {
  110. if (mod[1] === '*') {
  111. newImports.set(hash, `import * as ${importLocalName} from '${mod[0]}';`);
  112. }
  113. else {
  114. newImports.set(hash, `import { ${mod[1]} as ${importLocalName} } from '${mod[0]}';`);
  115. callback && callback(newImports, mod);
  116. }
  117. }
  118. if (name !== keypath) {
  119. magicString.overwrite(node.start, node.end, importLocalName, {
  120. storeName: true,
  121. });
  122. }
  123. return true;
  124. }
  125. return false;
  126. }
  127. (0, estree_walker_1.walk)(ast, {
  128. enter(node, parent) {
  129. if (sourceMap) {
  130. magicString.addSourcemapLocation(node.start);
  131. magicString.addSourcemapLocation(node.end);
  132. }
  133. if (node.scope) {
  134. scope = node.scope;
  135. }
  136. if ((0, utils_1.isProperty)(node) && node.shorthand) {
  137. const { name } = node.key;
  138. handleReference(node, name, name);
  139. this.skip();
  140. return;
  141. }
  142. if ((0, utils_1.isReference)(node, parent)) {
  143. const { name, keypath } = flatten(node);
  144. const handled = handleReference(node, name, keypath, parent);
  145. if (handled) {
  146. this.skip();
  147. }
  148. }
  149. },
  150. leave(node) {
  151. if (node.scope) {
  152. scope = scope.parent;
  153. }
  154. },
  155. });
  156. debugInject(id, newImports.size);
  157. if (newImports.size === 0) {
  158. return {
  159. code,
  160. // 不能返回 ast ,否则会导致代码不能被再次修改
  161. // 比如 App.vue 中,console.log('uniCloud') 触发了 inject 检测,检测完,发现不需要
  162. // 此时返回 ast,会导致 import { setupApp } from '@dcloudio/uni-h5' 不会被编译
  163. // ast
  164. map: null,
  165. };
  166. }
  167. const importBlock = Array.from(newImports.values()).join('\n\n');
  168. magicString.prepend(`${importBlock}\n\n`);
  169. return {
  170. code: magicString.toString(),
  171. map: sourceMap ? magicString.generateMap({ hires: true }) : null,
  172. };
  173. },
  174. };
  175. }
  176. exports.uniViteInjectPlugin = uniViteInjectPlugin;
  177. const escape = (str) => str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
  178. const flatten = (startNode) => {
  179. const parts = [];
  180. let node = startNode;
  181. while ((0, utils_1.isMemberExpression)(node)) {
  182. parts.unshift(node.property.name);
  183. node = node.object;
  184. }
  185. const { name } = node;
  186. parts.unshift(name);
  187. return { name, keypath: parts.join('.') };
  188. };
  189. function normalizeModulesMap(modulesMap) {
  190. modulesMap.forEach((mod, key) => {
  191. modulesMap.set(key, (0, shared_1.isArray)(mod)
  192. ? [mod[0].split(path_1.sep).join('/'), mod[1]]
  193. : mod.split(path_1.sep).join('/'));
  194. });
  195. }