babel-plugin-named-params
v0.9.16
Published
Babel Plugin for Named Function Parameters
Downloads
38
Readme
babel-plugin-named-params
Babel Plugin for Named Function Parameters
About
This is a Babel transpiler plugin for transforming non-standard named function parameters in ECMAScript 2015 source code. In particular, it transpiles the following input constructs...
fn(a = "foo", 42, d = "bar", 7)
baz.quux.fn(a = "foo", 42, d = "bar", 7)
...to the output constructs...
T(undefined, fn, [ 42, 7 ], { a: "foo", d: "bar" })
T(baz.quux, baz.quux.fn, [ 42, 7 ], { a "foo", d: "bar" })
...where T
is the "trampoline" function
of the corresponding run-time module
babel-runtime-named-params
.
Assuming the function fn
was declared as function fn (a, b,
c, d) { ... }
, these output constructs under run-time finally translate into...
fn.apply(undefined, [ "foo", 42, 7, "bar" ])
baz.quux.fn.apply(baz.quux, [ "foo", 42, 7, "bar" ])
...or the equivalent of the regular calls:
fn("foo", 42, 7, "bar")
baz.quux.fn("foo", 42, 7, "bar")
Motivation
This plugin is motivated by the wish of the author to have a more elegant approach to named parameters in JavaScript than the usual convention of passing an object argument provides. For such a conventional ECMAScript 2018 function declaration...
function foo (arg1, arg2, options) {
let { opt1, opt2 } = { opt1: "def1", opt2: "def2", ...options }
console.log(arg1, arg2, opt1, opt2)
}
...in addition, to the regular usage...
foo("val1", "val2", { opt1: "val3", opt2: "val4" })
// -> "val1", "val2", "val3", "val4"
...you can now use it with less boilerplate...
foo("val1", "val2", opt1 = "val3", opt2 = "val4")
// -> "val1", "val2", "val3", "val4"
...and even make the function declaration more elegant:
function foo (arg1, arg2, opt1 = "def1", opt2 = "def2") {
console.log(arg1, arg2, opt1, opt2)
}
Additionally, similar to Unix command-line option arguments (-x
or
--xx
), which most of the time can be mixed with positional arguments,
one can now mix named and positional function arguments, too.
Features
The following particular features are provided:
Parameter Syntax: Named parameters are syntax-wise just ECMAScript assignment expressions
<identifier> = <expression>
inside function argument lists. But instead of assigning to a variable in the lexical scope of the function call, this now assigns to a parameter of the function call.Parameter Ordering: Named and positional parameters can be provided in an arbitrary order. For a function declaration
function fn (a, b, c) { ... }
all of the following function calls result in a callfn(x, y, z)
:fn(a = x, b = y, c = z) // named parameters only fn(x, b = y, z) // mixed parameters only (in positional order) fn(b = y, x, z) // mixed parameters only (in arbitrary order) fn(x, z, b = y) // mixed parameters only (in arbitrary order)
In other words, the algorithm for determining the function call parameters is: first, the parameters (names and positions) of the target function are determined via the function source code (
Function.prototype.toString()
). Second, all named parameters are translated to resulting positional parameters at their particular positions. Third, all original positional parameters are translated to resulting positional parameters at still unused positions (from left to right). All remaining unused positions are filled with the valueundefined
.Options Parameter: In the JavaScript world, there is the convention of having an
options
function parameter which receives an object of options. In case a named parameter in the function call is not found in the function declaration, but such an options parameter exists, the named parameter is passed as an option parameter field. For a function declarationfunction fn (a, b, c, options) { ... }
all of the following function calls result in a callfn(x, y, z, { foo: 42, bar: 7 })
:fn(x, y, z, options = { foo: 42, bar: 7 }) // explicit options parameter (in positional order) fn(options = { foo: 42, bar: 7 }, x, y, z) // explicit options parameter (in arbitrary order) fn(x, y, z, foo = 42, bar = 7) // options as named parameters fn(foo = 42, bar = 7, x, y, z) // options as named parameters fn(x, y, z, options = { foo: 42 }, baz = 7) // explicit and implicit options fn(x, y, z, baz = 7, options = { foo: 42 }) // explicit and implicit options fn(a = x, b = y, c = z, foo = 42, bar = 7) // everything as named parameters
Caveat Emptor
Function Declaration and Transpilation: Although the named parameters need a Babel-based transpilation theirself, the function declarations should not be transpiled to a target environment below ECMAScript 2015, as Babel would remove parameters with defaults from the function declaration. To be able to use function declarations of the form
fn (a, b, c = def1, d = def2) { ... }
you have to at least target an ECMAScript 2015 environment like Node 6 with the help of@babel/preset-env
or the underlyingfunc-params
utility function will to be able to determine thec
andd
parameters.Increased Code Size: As the determination of function parameters is done under run-time (to support arbitrary existing code which is not part of the transpilation process itself), the resulting code size of your application increased by about 26KB. This is harmless for applications and libraries in the Node environments or applications in Browser environments, but can hurt you for libraries in Browser environments. Hence, try to not use this feature for libraries intended to be used in Browser environments or accept that their size increases by about 26KB.
Decreased Run-Time Performance: As the determination of function parameters is done under run-time (to support arbitrary existing code which is not part of the transpilation process itself), the run-time performance decreases somewhat. At least on the first function invocation. Internally, the source code of the target function is parsed and the result cached in memory. So, on the first function invocation, the parsing causes the function invocation to be much slower than the regular invocation, while on the second and all subsequent function invocation, the indirect function invocation is just slightly slower than the regular invocation.
Assignment Expression: As explained above, this plugin changes the semantics of the assignment expressions inside function argument lists. By definition, in
foo(id = expr)
theexpr
is assigned to a variable with the identifierid
in the lexical scope of the function call. With this plugin, this is no longer the case. Nowexpr
is assigned to the parameter with the identifierid
in the function declaration. This is a change is semantics, of course. On the other hand, an assignment expression inside a function argument list could be considered a strange coding practice anyway.
Installation
$ npm install @babel/core
$ npm install @babel/preset-env
$ npm install babel-plugin-named-params
$ npm install babel-runtime-named-params
Usage
.babelrc
:
{
"presets": [
[ "@babel/preset-env", {
"targets": {
"node": "6.0"
}
} ]
],
"plugins": [
[ "named-params", {
"options": true,
"caching": true
} ]
]
}
sample.js
:
const f1 = (a, b, c = "foo", d = "bar") => {
console.log(`a=<${a}> b=<${b}> c=<${c}> d=<${d}>`)
}
f1(1, 2, 3, 4)
f1(1, 2, d = "4", 3)
f1(1, 2, d = "4", c = "3")
f1(a = "1", 2, c = "3", d = "4")
const f2 = (a, b, options = {}) => {
console.log(`a=<${a}> b=<${b}> options=<${JSON.stringify(options)}>`)
}
f2(1, 2)
f2(1, 2, { foo: "bar", baz: "quux" })
f2(1, 2, options = { foo: "bar", baz: "quux" })
f2(1, 2, foo = "bar", baz = "quux")
f2(1, 2, baz = "quux", options = { foo: "bar" })
$ babel-node sample.js
a=<1> b=<2> c=<3> d=<4>
a=<1> b=<2> c=<3> d=<4>
a=<1> b=<2> c=<3> d=<4>
a=<1> b=<2> c=<3> d=<4>
a=<1> b=<2> options=<{}>
a=<1> b=<2> options=<{"foo":"bar","baz":"quux"}>
a=<1> b=<2> options=<{"foo":"bar","baz":"quux"}>
a=<1> b=<2> options=<{"foo":"bar","baz":"quux"}>
a=<1> b=<2> options=<{"foo":"bar","baz":"quux"}>
License
Copyright (c) 2018-2021 Dr. Ralf S. Engelschall (http://engelschall.com/)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.