Dashboard sipadu mbip
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  4. var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
  5. const p = require('path');
  6. const resolve = require('resolve'); // const printAST = require('ast-pretty-print')
  7. const macrosRegex = /[./]macro(\.js)?$/; // https://stackoverflow.com/a/32749533/971592
  8. class MacroError extends Error {
  9. constructor(message) {
  10. super(message);
  11. this.name = 'MacroError';
  12. /* istanbul ignore else */
  13. if (typeof Error.captureStackTrace === 'function') {
  14. Error.captureStackTrace(this, this.constructor);
  15. } else if (!this.stack) {
  16. this.stack = new Error(message).stack;
  17. }
  18. }
  19. }
  20. let _configExplorer = null;
  21. function getConfigExporer() {
  22. return _configExplorer = _configExplorer || // Lazy load cosmiconfig since it is a relatively large bundle
  23. require('cosmiconfig').cosmiconfigSync('babel-plugin-macros', {
  24. searchPlaces: ['package.json', '.babel-plugin-macrosrc', '.babel-plugin-macrosrc.json', '.babel-plugin-macrosrc.yaml', '.babel-plugin-macrosrc.yml', '.babel-plugin-macrosrc.js', 'babel-plugin-macros.config.js'],
  25. packageProp: 'babelMacros'
  26. });
  27. }
  28. function createMacro(macro, options = {}) {
  29. if (options.configName === 'options') {
  30. throw new Error(`You cannot use the configName "options". It is reserved for babel-plugin-macros.`);
  31. }
  32. macroWrapper.isBabelMacro = true;
  33. macroWrapper.options = options;
  34. return macroWrapper;
  35. function macroWrapper(args) {
  36. const {
  37. source,
  38. isBabelMacrosCall
  39. } = args;
  40. if (!isBabelMacrosCall) {
  41. throw new MacroError(`The macro you imported from "${source}" is being executed outside the context of compilation with babel-plugin-macros. ` + `This indicates that you don't have the babel plugin "babel-plugin-macros" configured correctly. ` + `Please see the documentation for how to configure babel-plugin-macros properly: ` + 'https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/user.md');
  42. }
  43. return macro(args);
  44. }
  45. }
  46. function nodeResolvePath(source, basedir) {
  47. return resolve.sync(source, {
  48. basedir
  49. });
  50. }
  51. function macrosPlugin(babel, _ref = {}) {
  52. let {
  53. require: _require = require,
  54. resolvePath = nodeResolvePath
  55. } = _ref,
  56. options = (0, _objectWithoutPropertiesLoose2.default)(_ref, ["require", "resolvePath"]);
  57. function interopRequire(path) {
  58. // eslint-disable-next-line import/no-dynamic-require
  59. const o = _require(path);
  60. return o && o.__esModule && o.default ? o.default : o;
  61. }
  62. return {
  63. name: 'macros',
  64. visitor: {
  65. Program(progPath, state) {
  66. progPath.traverse({
  67. ImportDeclaration(path) {
  68. const isMacros = looksLike(path, {
  69. node: {
  70. source: {
  71. value: v => macrosRegex.test(v)
  72. }
  73. }
  74. });
  75. if (!isMacros) {
  76. return;
  77. }
  78. const imports = path.node.specifiers.map(s => ({
  79. localName: s.local.name,
  80. importedName: s.type === 'ImportDefaultSpecifier' ? 'default' : s.imported.name
  81. }));
  82. const source = path.node.source.value;
  83. const result = applyMacros({
  84. path,
  85. imports,
  86. source,
  87. state,
  88. babel,
  89. interopRequire,
  90. resolvePath,
  91. options
  92. });
  93. if (!result || !result.keepImports) {
  94. path.remove();
  95. }
  96. },
  97. VariableDeclaration(path) {
  98. const isMacros = child => looksLike(child, {
  99. node: {
  100. init: {
  101. callee: {
  102. type: 'Identifier',
  103. name: 'require'
  104. },
  105. arguments: args => args.length === 1 && macrosRegex.test(args[0].value)
  106. }
  107. }
  108. });
  109. path.get('declarations').filter(isMacros).forEach(child => {
  110. const imports = child.node.id.name ? [{
  111. localName: child.node.id.name,
  112. importedName: 'default'
  113. }] : child.node.id.properties.map(property => ({
  114. localName: property.value.name,
  115. importedName: property.key.name
  116. }));
  117. const call = child.get('init');
  118. const source = call.node.arguments[0].value;
  119. const result = applyMacros({
  120. path: call,
  121. imports,
  122. source,
  123. state,
  124. babel,
  125. interopRequire,
  126. resolvePath,
  127. options
  128. });
  129. if (!result || !result.keepImports) {
  130. child.remove();
  131. }
  132. });
  133. }
  134. });
  135. }
  136. }
  137. };
  138. } // eslint-disable-next-line complexity
  139. function applyMacros({
  140. path,
  141. imports,
  142. source,
  143. state,
  144. babel,
  145. interopRequire,
  146. resolvePath,
  147. options
  148. }) {
  149. /* istanbul ignore next (pretty much only useful for astexplorer I think) */
  150. const {
  151. file: {
  152. opts: {
  153. filename = ''
  154. }
  155. }
  156. } = state;
  157. let hasReferences = false;
  158. const referencePathsByImportName = imports.reduce((byName, {
  159. importedName,
  160. localName
  161. }) => {
  162. const binding = path.scope.getBinding(localName);
  163. byName[importedName] = binding.referencePaths;
  164. hasReferences = hasReferences || Boolean(byName[importedName].length);
  165. return byName;
  166. }, {});
  167. const isRelative = source.indexOf('.') === 0;
  168. const requirePath = resolvePath(source, p.dirname(getFullFilename(filename)));
  169. const macro = interopRequire(requirePath);
  170. if (!macro.isBabelMacro) {
  171. throw new Error(`The macro imported from "${source}" must be wrapped in "createMacro" ` + `which you can get from "babel-plugin-macros". ` + `Please refer to the documentation to see how to do this properly: https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/author.md#writing-a-macro`);
  172. }
  173. const config = getConfig(macro, filename, source, options);
  174. let result;
  175. try {
  176. /**
  177. * Other plugins that run before babel-plugin-macros might use path.replace, where a path is
  178. * put into its own replacement. Apparently babel does not update the scope after such
  179. * an operation. As a remedy, the whole scope is traversed again with an empty "Identifier"
  180. * visitor - this makes the problem go away.
  181. *
  182. * See: https://github.com/kentcdodds/import-all.macro/issues/7
  183. */
  184. state.file.scope.path.traverse({
  185. Identifier() {}
  186. });
  187. result = macro({
  188. references: referencePathsByImportName,
  189. source,
  190. state,
  191. babel,
  192. config,
  193. isBabelMacrosCall: true
  194. });
  195. } catch (error) {
  196. if (error.name === 'MacroError') {
  197. throw error;
  198. }
  199. error.message = `${source}: ${error.message}`;
  200. if (!isRelative) {
  201. error.message = `${error.message} Learn more: https://www.npmjs.com/package/${source.replace( // remove everything after package name
  202. // @org/package/macro -> @org/package
  203. // package/macro -> package
  204. /^((?:@[^/]+\/)?[^/]+).*/, '$1')}`;
  205. }
  206. throw error;
  207. }
  208. return result;
  209. }
  210. function getConfigFromFile(configName, filename) {
  211. try {
  212. const loaded = getConfigExporer().search(filename);
  213. if (loaded) {
  214. return {
  215. options: loaded.config[configName],
  216. path: loaded.filepath
  217. };
  218. }
  219. } catch (e) {
  220. return {
  221. error: e
  222. };
  223. }
  224. return {};
  225. }
  226. function getConfigFromOptions(configName, options) {
  227. if (options.hasOwnProperty(configName)) {
  228. if (options[configName] && typeof options[configName] !== 'object') {
  229. // eslint-disable-next-line no-console
  230. console.error(`The macro plugin options' ${configName} property was not an object or null.`);
  231. } else {
  232. return {
  233. options: options[configName]
  234. };
  235. }
  236. }
  237. return {};
  238. }
  239. function getConfig(macro, filename, source, options) {
  240. const {
  241. configName
  242. } = macro.options;
  243. if (configName) {
  244. const fileConfig = getConfigFromFile(configName, filename);
  245. const optionsConfig = getConfigFromOptions(configName, options);
  246. if (optionsConfig.options === undefined && fileConfig.options === undefined) {
  247. // eslint-disable-next-line no-console
  248. console.error(`There was an error trying to load the config "${configName}" ` + `for the macro imported from "${source}. ` + `Please see the error thrown for more information.`);
  249. if (fileConfig.error !== undefined) {
  250. throw fileConfig.error;
  251. }
  252. }
  253. if (fileConfig.options !== undefined && optionsConfig.options !== undefined && typeof fileConfig.options !== 'object') {
  254. throw new Error(`${fileConfig.path} specified a ${configName} config of type ` + `${typeof optionsConfig.options}, but the the macros plugin's ` + `options.${configName} did contain an object. Both configs must ` + `contain objects for their options to be mergeable.`);
  255. }
  256. return (0, _extends2.default)({}, optionsConfig.options, {}, fileConfig.options);
  257. }
  258. return undefined;
  259. }
  260. /*
  261. istanbul ignore next
  262. because this is hard to test
  263. and not worth it...
  264. */
  265. function getFullFilename(filename) {
  266. if (p.isAbsolute(filename)) {
  267. return filename;
  268. }
  269. return p.join(process.cwd(), filename);
  270. }
  271. function looksLike(a, b) {
  272. return a && b && Object.keys(b).every(bKey => {
  273. const bVal = b[bKey];
  274. const aVal = a[bKey];
  275. if (typeof bVal === 'function') {
  276. return bVal(aVal);
  277. }
  278. return isPrimitive(bVal) ? bVal === aVal : looksLike(aVal, bVal);
  279. });
  280. }
  281. function isPrimitive(val) {
  282. // eslint-disable-next-line
  283. return val == null || /^[sbn]/.test(typeof val);
  284. }
  285. module.exports = macrosPlugin;
  286. Object.assign(module.exports, {
  287. createMacro,
  288. MacroError
  289. });