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;