string-format
Advanced tools
Comparing version
@@ -1,45 +0,60 @@ | ||
// Generated by CoffeeScript 1.4.0 | ||
// Generated by CoffeeScript 1.8.0 | ||
(function() { | ||
var format, lookup, resolve, | ||
var ValueError, create, explicitToImplicit, format, implicitToExplicit, lookup, resolve, | ||
__hasProp = {}.hasOwnProperty, | ||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | ||
__slice = [].slice; | ||
format = String.prototype.format = function() { | ||
var args, explicit, idx, implicit, message, | ||
_this = this; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
if (args.length === 0) { | ||
return function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return _this.format.apply(_this, args); | ||
}; | ||
ValueError = (function(_super) { | ||
__extends(ValueError, _super); | ||
function ValueError(message) { | ||
this.message = message; | ||
} | ||
idx = 0; | ||
explicit = implicit = false; | ||
message = 'cannot switch from {} to {} numbering'.format(); | ||
return this.replace(/([{}])\1|[{](.*?)(?:!(.+?))?[}]/g, function(match, literal, key, transformer) { | ||
var fn, value, _ref, _ref1, _ref2; | ||
if (literal) { | ||
return literal; | ||
} | ||
if (key.length) { | ||
explicit = true; | ||
if (implicit) { | ||
throw new Error(message('implicit', 'explicit')); | ||
ValueError.prototype.name = 'ValueError'; | ||
return ValueError; | ||
})(Error); | ||
implicitToExplicit = 'cannot switch from implicit to explicit numbering'; | ||
explicitToImplicit = 'cannot switch from explicit to implicit numbering'; | ||
create = function(transformers) { | ||
if (transformers == null) { | ||
transformers = {}; | ||
} | ||
return function() { | ||
var args, explicit, idx, implicit, message, template; | ||
template = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; | ||
idx = 0; | ||
explicit = implicit = false; | ||
message = 'cannot switch from {} to {} numbering'; | ||
return template.replace(/([{}])\1|[{](.*?)(?:!(.+?))?[}]/g, function(match, literal, key, transformer) { | ||
var value, _ref, _ref1; | ||
if (literal) { | ||
return literal; | ||
} | ||
value = (_ref = lookup(args, key)) != null ? _ref : ''; | ||
} else { | ||
implicit = true; | ||
if (explicit) { | ||
throw new Error(message('explicit', 'implicit')); | ||
if (key.length) { | ||
if (implicit) { | ||
throw new ValueError(implicitToExplicit); | ||
} | ||
explicit = true; | ||
value = (_ref = lookup(args, key)) != null ? _ref : ''; | ||
} else { | ||
if (explicit) { | ||
throw new ValueError(explicitToImplicit); | ||
} | ||
implicit = true; | ||
value = (_ref1 = args[idx++]) != null ? _ref1 : ''; | ||
} | ||
value = (_ref1 = args[idx++]) != null ? _ref1 : ''; | ||
} | ||
value = value.toString(); | ||
if (fn = format.transformers[transformer]) { | ||
return (_ref2 = fn.call(value)) != null ? _ref2 : ''; | ||
} else { | ||
return value; | ||
} | ||
}); | ||
if (Object.prototype.hasOwnProperty.call(transformers, transformer)) { | ||
return transformers[transformer](value); | ||
} else { | ||
return value; | ||
} | ||
}); | ||
}; | ||
}; | ||
@@ -69,6 +84,22 @@ | ||
format.transformers = {}; | ||
format = create({}); | ||
format.version = '0.2.1'; | ||
format.create = create; | ||
format.extend = function(prototype, transformers) { | ||
var $format; | ||
$format = create(transformers); | ||
prototype.format = function() { | ||
return $format.apply(null, [this].concat(__slice.call(arguments))); | ||
}; | ||
}; | ||
if (typeof module !== 'undefined') { | ||
module.exports = format; | ||
} else if (typeof define === 'function' && define.amd) { | ||
define(format); | ||
} else { | ||
window.format = format; | ||
} | ||
}).call(this); |
{ | ||
"name": "string-format", | ||
"version": "0.2.1", | ||
"version": "0.5.0", | ||
"description": "Adds a `format` method to `String.prototype`. Inspired by Python's `str.format()`.", | ||
"author": "David Chambers <dc@hashify.me>", | ||
"keywords": ["string", "formatting", "language", "util"], | ||
"author": "David Chambers <dc@davidchambers.me>", | ||
"keywords": [ | ||
"string", | ||
"formatting", | ||
"language", | ||
"util" | ||
], | ||
"main": "./lib/string-format", | ||
"scripts": { | ||
"prepublish": "make clean && make", | ||
"test": "make test" | ||
}, | ||
"homepage": "https://github.com/davidchambers/string-format", | ||
"bugs": "https://github.com/davidchambers/string-format/issues", | ||
"licenses": [{ | ||
"type": "WTFPL", | ||
"url": "https://raw.github.com/davidchambers/string-format/master/LICENSE" | ||
}], | ||
"licenses": [ | ||
{ | ||
"type": "WTFPL", | ||
"url": "https://raw.github.com/davidchambers/string-format/master/LICENSE" | ||
} | ||
], | ||
"repository": { | ||
@@ -18,7 +29,13 @@ "type": "git", | ||
}, | ||
"files": [ | ||
"README.md", | ||
"lib/string-format.js", | ||
"package.json" | ||
], | ||
"devDependencies": { | ||
"coffee-script": "1.4.x", | ||
"mocha": "1.7.x", | ||
"should": "1.2.x" | ||
"coffee-script": "1.8.x", | ||
"mocha": "2.x.x", | ||
"ramda": "0.8.x", | ||
"xyz": "0.5.x" | ||
} | ||
} |
227
README.md
# String::format | ||
String::format is a small JavaScript utility which adds a `format` method | ||
to strings. It's inspired by and modelled on Python's [`str.format()`][1]. | ||
String::format is a small JavaScript library for formatting strings, based on | ||
Python's [`str.format()`][1]. For example: | ||
When `format` is invoked on a string, placeholders within the string are | ||
replaced with values determined by the arguments provided. A placeholder | ||
is a sequence of characters beginning with `{` and ending with `}`. | ||
```javascript | ||
'"{firstName} {lastName}" <{email}>'.format(user) | ||
// => '"Jane Smith" <jsmith@example.com>' | ||
``` | ||
### string.format(value1, value2, ..., valueN) | ||
The equivalent concatenation: | ||
Placeholders may contain numbers which refer to positional arguments: | ||
```coffeescript | ||
"{0}, you have {1} unread message{2}".format("Holly", 2, "s") | ||
# "Holly, you have 2 unread messages" | ||
```javascript | ||
'"' + user.firstName + ' ' + user.lastName + '" <' + user.email + '>' | ||
// => '"Jane Smith" <jsmith@example.com>' | ||
``` | ||
Unmatched placeholders produce no output: | ||
### Installation | ||
```coffeescript | ||
"{0}, you have {1} unread message{2}".format("Steve", 1) | ||
# "Steve, you have 1 unread message" | ||
``` | ||
#### Node | ||
A format string may reference a positional argument multiple times: | ||
1. Install: | ||
```coffeescript | ||
"{0} x {0} x {0} = {1}".format(3, 3*3*3) | ||
# "3 x 3 x 3 = 27" | ||
``` | ||
$ npm install string-format | ||
Positional arguments may be referenced implicitly: | ||
2. Require: | ||
```coffeescript | ||
"{}, you have {} unread message{}".format("Steve", 1) | ||
# "Steve, you have 1 unread message" | ||
var format = require('string-format') | ||
#### Browser | ||
1. Define `window.format`: | ||
<script src="path/to/string-format.js"></script> | ||
### Modes | ||
String::format can be used in two modes: [function mode](#function-mode) and | ||
[method mode](#method-mode). | ||
#### Function mode | ||
```javascript | ||
format('Hello, {}!', 'Alice') | ||
// => 'Hello, Alice!' | ||
``` | ||
A format string must not contain both implicit and explicit references: | ||
In this mode the first argument is a template string and the remaining | ||
arguments are values to be interpolated. | ||
```coffeescript | ||
"My name is {} {}. Do you like the name {0}?".format("Lemony", "Snicket") | ||
# ERROR: cannot switch from implicit to explicit numbering | ||
#### Method mode | ||
```javascript | ||
'Hello, {}!'.format('Alice') | ||
// => 'Hello, Alice!' | ||
``` | ||
`{{` and `}}` in format strings produce `{` and `}`: | ||
In this mode values to be interpolated are supplied to the `format` method | ||
of a template string. This mode is not enabled by default. The method must | ||
first be defined via [`format.extend`](#formatextendprototype-transformers): | ||
```coffeescript | ||
"{{}} creates an empty {} in {}".format("dictionary", "Python") | ||
# "{} creates an empty dictionary in Python" | ||
```javascript | ||
format.extend(String.prototype) | ||
``` | ||
Dot notation may be used to reference object properties: | ||
`format(template, $0, $1, …, $N)` and `template.format($0, $1, …, $N)` can then | ||
be used interchangeably. | ||
```coffeescript | ||
bobby = first_name: "Bobby", last_name: "Fischer" | ||
garry = first_name: "Garry", last_name: "Kasparov" | ||
### `format(template, $0, $1, …, $N)` | ||
"{0.first_name} {0.last_name} vs. {1.first_name} {1.last_name}".format(bobby, garry) | ||
# "Bobby Fischer vs. Garry Kasparov" | ||
Returns the result of replacing each `{…}` placeholder in the template string | ||
with its corresponding replacement. | ||
Placeholders may contain numbers which refer to positional arguments: | ||
```javascript | ||
'{0}, you have {1} unread message{2}'.format('Holly', 2, 's') | ||
// => 'Holly, you have 2 unread messages' | ||
``` | ||
When referencing the first positional argument, `0.` may be omitted: | ||
Unmatched placeholders produce no output: | ||
```coffeescript | ||
repo = owner: "pypy", slug: "pypy", followers: [...] | ||
"{owner}/{slug} has {followers.length} followers".format(repo) | ||
# "pypy/pypy has 516 followers" | ||
```javascript | ||
'{0}, you have {1} unread message{2}'.format('Steve', 1) | ||
// => 'Steve, you have 1 unread message' | ||
``` | ||
If the referenced property is a method, it is invoked and the result is used | ||
as the replacement string: | ||
A format string may reference a positional argument multiple times: | ||
```coffeescript | ||
me = name: "David", dob: new Date "26 Apr 1984" | ||
```javascript | ||
"The name's {1}. {0} {1}.".format('James', 'Bond') | ||
// => "The name's Bond. James Bond." | ||
``` | ||
"{name} was born in {dob.getFullYear}".format(me) | ||
# "David was born in 1984" | ||
Positional arguments may be referenced implicitly: | ||
sheldon = quip: -> "Bazinga!" | ||
"I've always wanted to go to a goth club. {quip.toUpperCase}".format(sheldon) | ||
# "I've always wanted to go to a goth club. BAZINGA!" | ||
```javascript | ||
'{}, you have {} unread message{}'.format('Steve', 1) | ||
// => 'Steve, you have 1 unread message' | ||
``` | ||
### String.prototype.format.transformers | ||
A format string must not contain both implicit and explicit references: | ||
“Transformers” can be attached to `String.prototype.format.transformers`: | ||
```javascript | ||
'My name is {} {}. Do you like the name {0}?'.format('Lemony', 'Snicket') | ||
// => ValueError: cannot switch from implicit to explicit numbering | ||
``` | ||
```coffeescript | ||
String::format.transformers.upper = -> @toUpperCase() | ||
`{{` and `}}` in format strings produce `{` and `}`: | ||
"Batman's preferred onomatopoeia: {0!upper}".format("pow!") | ||
# "Batman's preferred onomatopoeia: POW!" | ||
```javascript | ||
'{{}} creates an empty {} in {}'.format('dictionary', 'Python') | ||
// => '{} creates an empty dictionary in Python' | ||
``` | ||
Within a transformer, `this` is the string returned by the referenced object's | ||
`toString` method, so transformers may be used in conjunction with non-string | ||
objects: | ||
Dot notation may be used to reference object properties: | ||
```coffeescript | ||
peter_parker = | ||
first_name: "Peter" | ||
last_name: "Parker" | ||
toString: -> @first_name + " " + @last_name | ||
```javascript | ||
var bobby = {firstName: 'Bobby', lastName: 'Fischer'} | ||
var garry = {firstName: 'Garry', lastName: 'Kasparov'} | ||
"NAME: {!upper}".format(peter_parker) | ||
# "NAME: PETER PARKER" | ||
'{0.firstName} {0.lastName} vs. {1.firstName} {1.lastName}'.format(bobby, garry) | ||
// => 'Bobby Fischer vs. Garry Kasparov' | ||
``` | ||
A transformer could sanitizing untrusted input: | ||
`0.` may be omitted when referencing a property of `{0}`: | ||
```coffeescript | ||
String::format.transformers.escape = -> | ||
@replace /[&<>"'`]/g, (chr) -> "&#" + chr.charCodeAt(0) + ";" | ||
```javascript | ||
var repo = {owner: 'davidchambers', slug: 'string-format'} | ||
"<p class=status>{!escape}</p>".format("I <3 EICH") | ||
# "<p class=status>I <3 EICH</p>" | ||
'https://github.com/{owner}/{slug}'.format(repo) | ||
// => 'https://github.com/davidchambers/string-format' | ||
``` | ||
Or pluralize nouns, perhaps: | ||
If the referenced property is a method, it is invoked with no arguments to | ||
determine the replacement: | ||
```coffeescript | ||
String::format.transformers.s = -> "s" unless +this is 1 | ||
```javascript | ||
var sheldon = { | ||
firstName: 'Sheldon', | ||
lastName: 'Cooper', | ||
dob: new Date('1970-01-01'), | ||
fullName: function() { return '{firstName} {lastName}'.format(this) }, | ||
quip: function() { return 'Bazinga!' } | ||
} | ||
"{0}, you have {1} unread message{1!s}".format("Holly", 2) | ||
# "Holly, you have 2 unread messages" | ||
'{fullName} was born at precisely {dob.toISOString}'.format(sheldon) | ||
// => 'Sheldon Cooper was born at precisely 1970-01-01T00:00:00.000Z' | ||
"{0}, you have {1} unread message{1!s}".format("Steve", 1) | ||
# "Steve, you have 1 unread message" | ||
"I've always wanted to go to a goth club. {quip.toUpperCase}".format(sheldon) | ||
// => "I've always wanted to go to a goth club. BAZINGA!" | ||
``` | ||
String::format does not currently define any transformers. | ||
### `format.extend(prototype[, transformers])` | ||
### string.format() | ||
This function defines a `format` method on the provided prototype (presumably | ||
`String.prototype`). One may provide an object mapping names to transformers. | ||
A transformer is applied if its name appears, prefixed with `!`, after a field | ||
name in a template string. | ||
If a format string is used in multiple places, one could assign it to | ||
a variable to avoid repetition. The idiomatic alternative is to invoke | ||
`String::format` with no arguments, which produces a reusable function: | ||
```javascript | ||
format.extend(String.prototype, { | ||
escape: function(s) { | ||
return s.replace(/[&<>"'`]/g, function(c) { | ||
return '&#' + c.charCodeAt(0) + ';' | ||
}) | ||
}, | ||
upper: function(s) { return s.toUpperCase() } | ||
}) | ||
```coffeescript | ||
greet = "{0}, you have {1} unread message{1!s}".format() | ||
'Hello, {!upper}!'.format('Alice') | ||
// => 'Hello, ALICE!' | ||
greet("Holly", 2) | ||
# "Holly, you have 2 unread messages" | ||
var restaurant = { | ||
name: 'Anchor & Hope', | ||
url: 'http://anchorandhopesf.com/' | ||
} | ||
greet("Steve", 1) | ||
# "Steve, you have 1 unread message" | ||
'<a href="{url!escape}">{name!escape}</a>'.format(restaurant) | ||
// => '<a href="http://anchorandhopesf.com/">Anchor & Hope</a>' | ||
``` | ||
@@ -155,6 +185,9 @@ | ||
make setup | ||
make test | ||
```console | ||
$ npm install | ||
$ npm test | ||
``` | ||
[1]: http://docs.python.org/library/stdtypes.html#str.format | ||
[2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind |
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
89
32.84%193
20.63%0
-100%8968
-36.06%4
33.33%3
-57.14%