From 76164bbc5a3b4484fad70667cdafa551d7404cfd Mon Sep 17 00:00:00 2001 From: aleclarson Date: Sun, 18 Nov 2018 16:06:05 -0500 Subject: [PATCH] fix: imported function calls (take 2) --- src/transformers/CJSImportTransformer.ts | 55 ++++++++++++++++++------ src/transformers/RootTransformer.ts | 6 +-- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/transformers/CJSImportTransformer.ts b/src/transformers/CJSImportTransformer.ts index 5f1a62c5..a5e3a5b3 100644 --- a/src/transformers/CJSImportTransformer.ts +++ b/src/transformers/CJSImportTransformer.ts @@ -174,22 +174,51 @@ export default class CJSImportTransformer extends Transformer { const replacement = this.importProcessor.getIdentifierReplacement( this.tokens.identifierNameForToken(token), ); - if (replacement) { - this.tokens.replaceToken(replacement); - - // Any number of closing parens is tolerated. - while (!this.tokens.isAtEnd() && this.tokens.currentToken().type === tt.parenR) { - this.rootTransformer.processToken(); + if (!replacement) { + return false; + } + let openParenIndex = this.tokens.currentIndex(); + while (++openParenIndex < this.tokens.tokens.length) { + const token = this.tokens.tokens[openParenIndex]; + if (token.type === tt.parenL) { + // Avoid treating imported functions as methods of their `exports` object + // by using `(0, f)` when the identifier is in a paren expression. Else + // use `Function.prototype.call` when the identifier is a guaranteed + // function call. When using `call`, pass undefined as the context. + switch (this.tokens.tokenAtRelativeIndex(1).type) { + case tt.parenL: { + // We can use `(0, f)` when the previous token is an open brace, + // semicolon, or equal operator. This saves space for function calls + // with arguments, because then `void 0` is omitted. + const prevToken = this.tokens.tokenAtRelativeIndex(-1); + if ( + prevToken.type !== tt.braceL && + prevToken.type !== tt.semi && + prevToken.type !== tt.eq + ) { + // NOTE: This line inserts a new parenthesis, which must be accounted + // for by recursively calling the `processBalancedCode` method of the + // root transformer. + const hasArgs = this.tokens.tokenAtRelativeIndex(2).type !== tt.parenR; + this.tokens.replaceToken(`${replacement}.call(` + (hasArgs ? "void 0, " : "")); + this.tokens.removeToken(); // Remove the old paren. + this.rootTransformer.processBalancedCode(0, 1); + return true; + } + } + case tt.parenR: + // See here: http://2ality.com/2015/12/references.html + this.tokens.replaceToken(`(0, ${replacement})`); + return true; + } + break; } - - // Avoid treating imported functions as methods of their `exports` object by using - // the `call` method with a falsy context (which is coerced into the global context). - if (!this.tokens.isAtEnd() && this.tokens.currentToken().type === tt.parenL) { - this.tokens.replaceToken(`.call(0, `); - return true; + if (token.type !== tt.parenR) { + break; } } - return false; + this.tokens.replaceToken(replacement); + return true; } processObjectShorthand(): boolean { diff --git a/src/transformers/RootTransformer.ts b/src/transformers/RootTransformer.ts index 331405c9..76114633 100644 --- a/src/transformers/RootTransformer.ts +++ b/src/transformers/RootTransformer.ts @@ -101,9 +101,9 @@ export default class RootTransformer { } } - processBalancedCode(): void { - let braceDepth = 0; - let parenDepth = 0; + processBalancedCode(initialBraceDepth: number = 0, initialParenDepth: number = 0): void { + let braceDepth = initialBraceDepth; + let parenDepth = initialParenDepth; while (!this.tokens.isAtEnd()) { if (this.tokens.matches1(tt.braceL) || this.tokens.matches1(tt.dollarBraceL)) { braceDepth++;