index.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
  5. }) : (function(o, m, k, k2) {
  6. if (k2 === undefined) k2 = k;
  7. o[k2] = m[k];
  8. }));
  9. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  10. Object.defineProperty(o, "default", { enumerable: true, value: v });
  11. }) : function(o, v) {
  12. o["default"] = v;
  13. });
  14. var __importStar = (this && this.__importStar) || function (mod) {
  15. if (mod && mod.__esModule) return mod;
  16. var result = {};
  17. if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  18. __setModuleDefault(result, mod);
  19. return result;
  20. };
  21. var __importDefault = (this && this.__importDefault) || function (mod) {
  22. return (mod && mod.__esModule) ? mod : { "default": mod };
  23. };
  24. Object.defineProperty(exports, "__esModule", { value: true });
  25. const parser_1 = require("@babel/parser");
  26. const babel_walk_1 = require("babel-walk");
  27. const t = __importStar(require("@babel/types"));
  28. const globals_1 = __importDefault(require("./globals"));
  29. const parseOptions = {
  30. allowReturnOutsideFunction: true,
  31. allowImportExportEverywhere: true,
  32. };
  33. /**
  34. * Mimic `with` as far as possible but at compile time
  35. *
  36. * @param obj The object part of a with expression
  37. * @param src The body of the with expression
  38. * @param exclude A list of variable names to explicitly exclude
  39. */
  40. function addWith(obj, src, exclude = []) {
  41. // tslint:disable-next-line: no-parameter-reassignment
  42. obj = obj + '';
  43. // tslint:disable-next-line: no-parameter-reassignment
  44. src = src + '';
  45. let ast;
  46. try {
  47. ast = parser_1.parse(src, parseOptions);
  48. }
  49. catch (e) {
  50. throw Object.assign(new Error('Error parsing body of the with expression'), {
  51. component: 'src',
  52. babylonError: e,
  53. });
  54. }
  55. let objAst;
  56. try {
  57. objAst = parser_1.parse(obj, parseOptions);
  58. }
  59. catch (e) {
  60. throw Object.assign(new Error('Error parsing object part of the with expression'), {
  61. component: 'obj',
  62. babylonError: e,
  63. });
  64. }
  65. const excludeSet = new Set([
  66. 'undefined',
  67. 'this',
  68. ...exclude,
  69. ...globals_1.default(objAst).map((g) => g.name),
  70. ]);
  71. const vars = new Set(globals_1.default(ast)
  72. .map((global) => global.name)
  73. .filter((v) => !excludeSet.has(v)));
  74. if (vars.size === 0)
  75. return src;
  76. let declareLocal = '';
  77. let local = 'locals_for_with';
  78. let result = 'result_of_with';
  79. if (t.isValidIdentifier(obj)) {
  80. local = obj;
  81. }
  82. else {
  83. while (vars.has(local) || excludeSet.has(local)) {
  84. local += '_';
  85. }
  86. declareLocal = `var ${local} = (${obj});`;
  87. }
  88. while (vars.has(result) || excludeSet.has(result)) {
  89. result += '_';
  90. }
  91. const args = [
  92. 'this',
  93. ...Array.from(vars).map((v) => `${JSON.stringify(v)} in ${local} ?
  94. ${local}.${v} :
  95. typeof ${v} !== 'undefined' ? ${v} : undefined`),
  96. ];
  97. const unwrapped = unwrapReturns(ast, src, result);
  98. return `;
  99. ${declareLocal}
  100. ${unwrapped.before}
  101. (function (${Array.from(vars).join(', ')}) {
  102. ${unwrapped.body}
  103. }.call(${args.join(', ')}));
  104. ${unwrapped.after};`;
  105. }
  106. exports.default = addWith;
  107. const unwrapReturnsVisitors = babel_walk_1.recursive({
  108. Function(_node, _state, _c) {
  109. // returns in these functions are not applicable
  110. },
  111. ReturnStatement(node, state) {
  112. state.hasReturn = true;
  113. let value = '';
  114. if (node.argument) {
  115. value = `value: (${state.source(node.argument)})`;
  116. }
  117. state.replace(node, `return {${value}};`);
  118. },
  119. });
  120. /**
  121. * Take a self calling function, and unwrap it such that return inside the function
  122. * results in return outside the function
  123. *
  124. * @param src Some JavaScript code representing a self-calling function
  125. * @param result A temporary variable to store the result in
  126. */
  127. function unwrapReturns(ast, src, result) {
  128. const charArray = src.split('');
  129. const state = {
  130. hasReturn: false,
  131. source(node) {
  132. return src.slice(node.start, node.end);
  133. },
  134. replace(node, str) {
  135. charArray.fill('', node.start, node.end);
  136. charArray[node.start] = str;
  137. },
  138. };
  139. unwrapReturnsVisitors(ast, state);
  140. return {
  141. before: state.hasReturn ? `var ${result} = ` : '',
  142. body: charArray.join(''),
  143. after: state.hasReturn ? `;if (${result}) return ${result}.value` : '',
  144. };
  145. }
  146. module.exports = addWith;
  147. module.exports.default = addWith;
  148. //# sourceMappingURL=index.js.map