Skip to content

Commit

Permalink
feat: add option to emit nullish coalescing operator
Browse files Browse the repository at this point in the history
  • Loading branch information
shaylew authored and eventualbuddha committed Jun 4, 2022
1 parent c14d689 commit 8cafc8d
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/cli.ts
Expand Up @@ -162,6 +162,10 @@ function parseArguments(args: ReadonlyArray<string>, io: IO): CLIOptions {
baseOptions.logicalAssignment = true;
break;

case '--nullish-coalescing':
baseOptions.nullishCoalescing = true;
break;

default:
if (arg.startsWith('-')) {
io.stderr.write(`Error: unrecognized option '${arg}'\n`);
Expand Down
1 change: 1 addition & 0 deletions src/options.ts
Expand Up @@ -18,6 +18,7 @@ export interface Options {
disallowInvalidConstructors?: boolean;
optionalChaining?: boolean;
logicalAssignment?: boolean;
nullishCoalescing?: boolean;
}

export const DEFAULT_OPTIONS: Options = {
Expand Down
7 changes: 6 additions & 1 deletion src/stages/main/index.ts
Expand Up @@ -61,6 +61,7 @@ import ModuloOpCompoundAssignOpPatcher from './patchers/ModuloOpCompoundAssignOp
import ModuloOpPatcher from './patchers/ModuloOpPatcher';
import NewOpPatcher from './patchers/NewOpPatcher';
import NullishCoalescingCompoundAssignOpPatcher from './patchers/NullishCoalescingCompoundAssignOpPatcher';
import NullishCoalescingExistsOpPatcher from './patchers/NullishCoalescingExistsOpPatcher';
import ObjectInitialiserMemberPatcher from './patchers/ObjectInitialiserMemberPatcher';
import ObjectInitialiserPatcher from './patchers/ObjectInitialiserPatcher';
import OfOpPatcher from './patchers/OfOpPatcher';
Expand Down Expand Up @@ -273,7 +274,11 @@ export default class MainStage extends TransformCoffeeScriptStage {
return HeregexPatcher;

case 'ExistsOp':
return ExistsOpPatcher;
if (this.options.nullishCoalescing) {
return NullishCoalescingExistsOpPatcher;
} else {
return ExistsOpPatcher;
}

case 'LogicalAndOp':
case 'LogicalOrOp':
Expand Down
22 changes: 22 additions & 0 deletions src/stages/main/patchers/NullishCoalescingExistsOpPatcher.ts
@@ -0,0 +1,22 @@
import { PatchOptions } from '../../../patchers/types';
import BinaryOpPatcher from './BinaryOpPatcher';
import ExistsOpPatcher from './ExistsOpPatcher';

export default class NullishCoalescingExistsOpPatcher extends ExistsOpPatcher {
/**
* LEFT '?' RIGHT → `LEFT ?? RIGHT`
*/
patchAsExpression(options: PatchOptions = {}): void {
const needsTypeofCheck = this.left.mayBeUnboundReference();
if (needsTypeofCheck) {
super.patchAsExpression(options);
} else {
BinaryOpPatcher.prototype.patchAsExpression.call(this, options);
}
}

patchOperator(): void {
const opToken = this.getOperatorToken();
this.overwrite(opToken.start, opToken.end, '??');
}
}
31 changes: 31 additions & 0 deletions test/nullishCoalescing_test.ts
@@ -0,0 +1,31 @@
import check from './support/check';

function checkNullishCoalescing(source: string, expected: string): void {
check(source, expected, { options: { nullishCoalescing: true } });
}

describe('with nullish coalescing enabled', () => {
it('translates binary existence to nullish coalescing', () => {
checkNullishCoalescing(
`
a = 1
b = a ? 2
`,
`
const a = 1;
const b = a ?? 2;
`
);
});

it('falls back to a check if the LHS may be undeclared', () => {
checkNullishCoalescing(
`
b = a ? 2
`,
`
const b = typeof a !== 'undefined' && a !== null ? a : 2;
`
);
})
});

0 comments on commit 8cafc8d

Please sign in to comment.