awilix
Version:
Extremely powerful dependency injection container.
147 lines • 5.21 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseParameterList = parseParameterList;
const function_tokenizer_1 = require("./function-tokenizer");
/*
* Parses the parameter list of a function string, including ES6 class constructors.
*
* @param {string} source
* The source of a function to extract the parameter list from
*
* @return {Array<Parameter> | null}
* Returns an array of parameters, or `null` if no
* constructor was found for a class.
*/
function parseParameterList(source) {
const { next: _next, done } = (0, function_tokenizer_1.createTokenizer)(source);
const params = [];
let t = null;
nextToken();
while (!done()) {
switch (t.type) {
case 'class': {
const foundConstructor = advanceToConstructor();
// If we didn't find a constructor token, then we know that there
// are no dependencies in the defined class.
if (!foundConstructor) {
return null;
}
break;
}
case 'function': {
const next = nextToken();
if (next.type === 'ident' || next.type === '*') {
// This is the function name or a generator star. Skip it.
nextToken();
}
break;
}
case '(':
// Start parsing parameter names.
parseParams();
break;
case ')':
// We're now out of the parameter list.
return params;
// When we're encountering an identifier token
// at this level, it could be because it's an arrow function
// with a single parameter, e.g. `foo => ...`.
// This path won't be hit if we've already identified the `(` token.
case 'ident': {
// Likely a paren-less arrow function
// which can have no default args.
const param = { name: t.value, optional: false };
if (t.value === 'async') {
// Given it's the very first token, we can assume it's an async function,
// so skip the async keyword if the next token is not an equals sign, in which
// case it is a single-arg arrow func.
const next = nextToken();
if (next && next.type !== '=') {
break;
}
}
params.push(param);
return params;
}
/* istanbul ignore next */
default:
throw unexpected();
}
}
return params;
/**
* After having been placed within the parameter list of
* a function, parses the parameters.
*/
function parseParams() {
// Current token is a left-paren
let param = { name: '', optional: false };
while (!done()) {
nextToken();
switch (t.type) {
case 'ident':
param.name = t.value;
break;
case '=':
param.optional = true;
break;
case ',':
params.push(param);
param = { name: '', optional: false };
break;
case ')':
if (param.name) {
params.push(param);
}
return;
/* istanbul ignore next */
default:
throw unexpected();
}
}
}
/**
* Advances until we reach the constructor identifier followed by
* a `(` token.
*
* @returns `true` if a constructor was found, `false` otherwise.
*/
function advanceToConstructor() {
while (!done()) {
if (isConstructorToken()) {
// Consume the token
nextToken(1 /* TokenizerFlags.Dumb */);
// If the current token now isn't a `(`, then it wasn't the actual
// constructor.
if (t.type !== '(') {
continue;
}
return true;
}
nextToken(1 /* TokenizerFlags.Dumb */);
}
return false;
}
/**
* Determines if the current token represents a constructor, and the next token after it is a paren
* @return {boolean}
*/
function isConstructorToken() {
return t.type === 'ident' && t.value === 'constructor';
}
/**
* Advances the tokenizer and stores the previous token in history
*/
function nextToken(flags = 0 /* TokenizerFlags.None */) {
t = _next(flags);
return t;
}
/**
* Returns an error describing an unexpected token.
*/
/* istanbul ignore next */
function unexpected() {
return new SyntaxError(`Parsing parameter list, did not expect ${t.type} token${t.value ? ` (${t.value})` : ''}`);
}
}
//# sourceMappingURL=param-parser.js.map
;