/ lib / rawr.js
rawr.js
  1  "use strict";
  2  // Adapted from pupa Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
  3  var __importDefault = (this && this.__importDefault) || function (mod) {
  4      return (mod && mod.__esModule) ? mod : { "default": mod };
  5  };
  6  Object.defineProperty(exports, "__esModule", { value: true });
  7  exports.parse = void 0;
  8  const object_inspect_1 = __importDefault(require("object-inspect"));
  9  const { hasOwnProperty } = Object.prototype;
 10  const getIn = (obj, keyParts) => {
 11      let value = obj;
 12      for (const keyPart of keyParts) {
 13          value = value ? value[keyPart] : undefined;
 14      }
 15      return value;
 16  };
 17  const parse = (template) => {
 18      const str = template;
 19      const literals = [];
 20      const keys = [];
 21      let i = 0;
 22      let escaped = false;
 23      let partial = '';
 24      let key = null;
 25      const pushKeyPart = () => {
 26          if (!partial) {
 27              throw new SyntaxError(`empty key segment at {position: ${i}} in {template: ${template}}`);
 28          }
 29          keys[keys.length - 1].parts.push(partial);
 30          partial = '';
 31      };
 32      while (i < str.length) {
 33          if (escaped) {
 34              partial += str[i];
 35          }
 36          else if (str[i] === '\\') {
 37              if (escaped) {
 38                  partial += '\\';
 39              }
 40              escaped = !escaped;
 41          }
 42          else if (str[i] === '{') {
 43              if (key !== null) {
 44                  throw new SyntaxError(`invalid {character: ${str[i]}} at {position: ${i}} in {template: ${template}}`);
 45              }
 46              key = { name: null, parts: [], doubleCurly: false, start: i + 1, end: -1 };
 47              if (str[i + 1] === '{') {
 48                  key.doubleCurly = true;
 49                  i++;
 50              }
 51              literals.push(partial);
 52              partial = '';
 53              keys.push(key);
 54          }
 55          else if (str[i] === '}') {
 56              if (key !== null) {
 57                  if (key.start === i) {
 58                      throw new SyntaxError(`empty {braces: \`${key.doubleCurly ? '{{}}' : '{}'}\`} at {position: ${i - (key.doubleCurly ? 2 : 1)}} in {template: ${template}}`);
 59                  }
 60                  if ((key.doubleCurly && str[i + 1] !== '}') || (!key.doubleCurly && str[i + 1] === '}')) {
 61                      throw new SyntaxError(`mismatched braces at {start: ${key.start}, end: ${key.doubleCurly ? i : i + 1}} in {template: ${template}}`);
 62                  }
 63                  if (key.doubleCurly) {
 64                      i++;
 65                  }
 66                  key.name = key.name || partial;
 67                  key.end = i - 1;
 68                  pushKeyPart();
 69                  key = null;
 70              }
 71          }
 72          else if (key !== null) {
 73              if (str[i] === '.') {
 74                  pushKeyPart();
 75              }
 76              else if (str[i] === ':') {
 77                  if (partial === '') {
 78                      throw new SyntaxError(`Unexpected character at {pos: ${i}}: key cannot have an empty name`);
 79                  }
 80                  else if (key.parts.length) {
 81                      throw new SyntaxError(`key name must not have multiple parts`);
 82                  }
 83                  key.name = partial;
 84                  partial = '';
 85              }
 86              else {
 87                  partial += str[i];
 88              }
 89          }
 90          else {
 91              partial += str[i];
 92          }
 93          i++;
 94      }
 95      literals.push(partial);
 96      return { literals, keys };
 97  };
 98  exports.parse = parse;
 99  const interpolate = (literals, ...values) => {
100      let i = 0;
101      let string = literals[i];
102      for (const value of values) {
103          i++;
104          string += value;
105          string += literals[i];
106      }
107      return string;
108  };
109  const rawrInterpolate = (literals, keys, props, options) => {
110      const values = keys.map(({ name, parts }) => {
111          return `{${name}: ${(0, object_inspect_1.default)(getIn(props, parts))}}`;
112      });
113      let interpolated = interpolate(literals, ...values);
114      if (options.rest) {
115          const used = {};
116          const lines = [];
117          for (const key of keys) {
118              used[key.parts[0]] = true;
119          }
120          for (const key in props) {
121              if (hasOwnProperty.call(props, key) && !used.hasOwnProperty(key)) {
122                  lines.push((0, object_inspect_1.default)({ [key]: props[key] }));
123              }
124          }
125          if (lines.length > 0) {
126              interpolated += '\n  ' + lines.join('\n  ');
127          }
128      }
129      return interpolated;
130  };
131  const defaultOptions = {
132      rest: true,
133  };
134  const assertValidKeys = (keys) => {
135      for (const key of keys) {
136          if (key.doubleCurly) {
137              throw new SyntaxError('{{}} interpolation is reserved');
138          }
139      }
140  };
141  const rawr = (template, ...values) => {
142      if (Array.isArray(template)) {
143          // rawr`${str}{prop}` => props => string
144          const { literals, keys } = (0, exports.parse)(interpolate(template, ...values));
145          assertValidKeys(keys);
146          return (props) => rawrInterpolate(literals, keys, props, defaultOptions);
147      }
148      else {
149          // rawr('{prop}') => props => string
150          let [options = defaultOptions] = values;
151          const { literals, keys } = (0, exports.parse)(template);
152          assertValidKeys(keys);
153          if (options && typeof options === 'object') {
154              options = Object.assign(Object.assign({}, defaultOptions), options);
155          }
156          return (props) => rawrInterpolate(literals, keys, props, options);
157      }
158  };
159  exports.default = rawr;