UNPKG

2.25 kBJavaScriptView Raw
1// Limited implementation of python % string operator, supports only %s and %r for now
2// (other formats are not used here, but may appear in custom templates)
3
4'use strict'
5
6const { inspect } = require('util')
7
8
9module.exports = function sub(pattern, ...values) {
10 let regex = /%(?:(%)|(-)?(\*)?(?:\((\w+)\))?([A-Za-z]))/g
11
12 let result = pattern.replace(regex, function (_, is_literal, is_left_align, is_padded, name, format) {
13 if (is_literal) return '%'
14
15 let padded_count = 0
16 if (is_padded) {
17 if (values.length === 0) throw new TypeError('not enough arguments for format string')
18 padded_count = values.shift()
19 if (!Number.isInteger(padded_count)) throw new TypeError('* wants int')
20 }
21
22 let str
23 if (name !== undefined) {
24 let dict = values[0]
25 if (typeof dict !== 'object' || dict === null) throw new TypeError('format requires a mapping')
26 if (!(name in dict)) throw new TypeError(`no such key: '${name}'`)
27 str = dict[name]
28 } else {
29 if (values.length === 0) throw new TypeError('not enough arguments for format string')
30 str = values.shift()
31 }
32
33 switch (format) {
34 case 's':
35 str = String(str)
36 break
37 case 'r':
38 str = inspect(str)
39 break
40 case 'd':
41 case 'i':
42 if (typeof str !== 'number') {
43 throw new TypeError(`%${format} format: a number is required, not ${typeof str}`)
44 }
45 str = String(str.toFixed(0))
46 break
47 default:
48 throw new TypeError(`unsupported format character '${format}'`)
49 }
50
51 if (padded_count > 0) {
52 return is_left_align ? str.padEnd(padded_count) : str.padStart(padded_count)
53 } else {
54 return str
55 }
56 })
57
58 if (values.length) {
59 if (values.length === 1 && typeof values[0] === 'object' && values[0] !== null) {
60 // mapping
61 } else {
62 throw new TypeError('not all arguments converted during string formatting')
63 }
64 }
65
66 return result
67}