extract-const-value.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.extractExportedConstValue = extractExportedConstValue;
  6. class NoSuchDeclarationError extends Error {
  7. }
  8. exports.NoSuchDeclarationError = NoSuchDeclarationError;
  9. function isExportDeclaration(node) {
  10. return node.type === "ExportDeclaration";
  11. }
  12. function isVariableDeclaration(node) {
  13. return node.type === "VariableDeclaration";
  14. }
  15. function isIdentifier(node) {
  16. return node.type === "Identifier";
  17. }
  18. function isBooleanLiteral(node) {
  19. return node.type === "BooleanLiteral";
  20. }
  21. function isNullLiteral(node) {
  22. return node.type === "NullLiteral";
  23. }
  24. function isStringLiteral(node) {
  25. return node.type === "StringLiteral";
  26. }
  27. function isNumericLiteral(node) {
  28. return node.type === "NumericLiteral";
  29. }
  30. function isArrayExpression(node) {
  31. return node.type === "ArrayExpression";
  32. }
  33. function isObjectExpression(node) {
  34. return node.type === "ObjectExpression";
  35. }
  36. function isKeyValueProperty(node) {
  37. return node.type === "KeyValueProperty";
  38. }
  39. function isRegExpLiteral(node) {
  40. return node.type === "RegExpLiteral";
  41. }
  42. function isTemplateLiteral(node) {
  43. return node.type === "TemplateLiteral";
  44. }
  45. class UnsupportedValueError extends Error {
  46. constructor(message, paths){
  47. super(message);
  48. // Generating "path" that looks like "config.runtime[0].value"
  49. let codePath;
  50. if (paths) {
  51. codePath = "";
  52. for (const path of paths){
  53. if (path[0] === "[") {
  54. // "array" + "[0]"
  55. codePath += path;
  56. } else {
  57. if (codePath === "") {
  58. codePath = path;
  59. } else {
  60. // "object" + ".key"
  61. codePath += `.${path}`;
  62. }
  63. }
  64. }
  65. }
  66. this.path = codePath;
  67. }
  68. }
  69. exports.UnsupportedValueError = UnsupportedValueError;
  70. function extractValue(node, path) {
  71. if (isNullLiteral(node)) {
  72. return null;
  73. } else if (isBooleanLiteral(node)) {
  74. // e.g. true / false
  75. return node.value;
  76. } else if (isStringLiteral(node)) {
  77. // e.g. "abc"
  78. return node.value;
  79. } else if (isNumericLiteral(node)) {
  80. // e.g. 123
  81. return node.value;
  82. } else if (isRegExpLiteral(node)) {
  83. // e.g. /abc/i
  84. return new RegExp(node.pattern, node.flags);
  85. } else if (isIdentifier(node)) {
  86. switch(node.value){
  87. case "undefined":
  88. return undefined;
  89. default:
  90. throw new UnsupportedValueError(`Unknown identifier "${node.value}"`, path);
  91. }
  92. } else if (isArrayExpression(node)) {
  93. // e.g. [1, 2, 3]
  94. const arr = [];
  95. for(let i = 0, len = node.elements.length; i < len; i++){
  96. const elem = node.elements[i];
  97. if (elem) {
  98. if (elem.spread) {
  99. // e.g. [ ...a ]
  100. throw new UnsupportedValueError("Unsupported spread operator in the Array Expression", path);
  101. }
  102. arr.push(extractValue(elem.expression, path && [
  103. ...path,
  104. `[${i}]`
  105. ]));
  106. } else {
  107. // e.g. [1, , 2]
  108. // ^^
  109. arr.push(undefined);
  110. }
  111. }
  112. return arr;
  113. } else if (isObjectExpression(node)) {
  114. // e.g. { a: 1, b: 2 }
  115. const obj = {};
  116. for (const prop of node.properties){
  117. if (!isKeyValueProperty(prop)) {
  118. // e.g. { ...a }
  119. throw new UnsupportedValueError("Unsupported spread operator in the Object Expression", path);
  120. }
  121. let key;
  122. if (isIdentifier(prop.key)) {
  123. // e.g. { a: 1, b: 2 }
  124. key = prop.key.value;
  125. } else if (isStringLiteral(prop.key)) {
  126. // e.g. { "a": 1, "b": 2 }
  127. key = prop.key.value;
  128. } else {
  129. throw new UnsupportedValueError(`Unsupported key type "${prop.key.type}" in the Object Expression`, path);
  130. }
  131. obj[key] = extractValue(prop.value, path && [
  132. ...path,
  133. key
  134. ]);
  135. }
  136. return obj;
  137. } else if (isTemplateLiteral(node)) {
  138. // e.g. `abc`
  139. if (node.expressions.length !== 0) {
  140. // TODO: should we add support for `${'e'}d${'g'}'e'`?
  141. throw new UnsupportedValueError("Unsupported template literal with expressions", path);
  142. }
  143. // When TemplateLiteral has 0 expressions, the length of quasis is always 1.
  144. // Because when parsing TemplateLiteral, the parser yields the first quasi,
  145. // then the first expression, then the next quasi, then the next expression, etc.,
  146. // until the last quasi.
  147. // Thus if there is no expression, the parser ends at the frst and also last quasis
  148. //
  149. // A "cooked" interpretation where backslashes have special meaning, while a
  150. // "raw" interpretation where backslashes do not have special meaning
  151. // https://exploringjs.com/impatient-js/ch_template-literals.html#template-strings-cooked-vs-raw
  152. const [{ cooked , raw }] = node.quasis;
  153. return cooked != null ? cooked : raw;
  154. } else {
  155. throw new UnsupportedValueError(`Unsupported node type "${node.type}"`, path);
  156. }
  157. }
  158. function extractExportedConstValue(module, exportedName) {
  159. for (const moduleItem of module.body){
  160. if (!isExportDeclaration(moduleItem)) {
  161. continue;
  162. }
  163. const declaration = moduleItem.declaration;
  164. if (!isVariableDeclaration(declaration)) {
  165. continue;
  166. }
  167. if (declaration.kind !== "const") {
  168. continue;
  169. }
  170. for (const decl of declaration.declarations){
  171. if (isIdentifier(decl.id) && decl.id.value === exportedName && decl.init) {
  172. return extractValue(decl.init, [
  173. exportedName
  174. ]);
  175. }
  176. }
  177. }
  178. throw new NoSuchDeclarationError();
  179. }
  180. //# sourceMappingURL=extract-const-value.js.map