Skip to content

Commit

Permalink
Add flag to disable ES transforms (#623)
Browse files Browse the repository at this point in the history
The idea of this PR is to allow to opt-out the transformations of "modern" ES features like optional chaining, numbers separators, etc.

Please check issues #602, #622 for more details.

I've tried to implement such a flag, as @alangpierce suggested in #602.
The implementation appeared bigger than I expected as I had to implement changes in 3 main modules:
1. `RootTransformer` – omit unnecessary ES transformers
2. `HelperManager` – disable unused helpers emitting (reverted after finding in #623 (comment))
3. `TokenProcessor` – disable helpers insertion into a final code, e.g. `_optionalChain(...)` wrapper.

All described scenarios were covered by test cases.
  • Loading branch information
webschik authored Jun 19, 2021
1 parent 7784361 commit edc56e0
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/Options-gen-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const Options = t.iface([], {
sourceMapOptions: t.opt("SourceMapOptions"),
filePath: t.opt("string"),
production: t.opt("boolean"),
disableESTransforms: t.opt("boolean"),
});

const exportedTypeSuite: t.ITypeSuite = {
Expand Down
5 changes: 5 additions & 0 deletions src/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ export interface Options {
* If specified, omit any development-specific code in the output.
*/
production?: boolean;
/**
* Opts out ES syntax transformations, like optional chaining, nullish coalescing, numeric
* separators, etc.
*/
disableESTransforms?: boolean;
}

export function validateOptions(options: Options): void {
Expand Down
11 changes: 9 additions & 2 deletions src/TokenProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default class TokenProcessor {
readonly code: string,
readonly tokens: Array<Token>,
readonly isFlowEnabled: boolean,
readonly disableESTransforms: boolean,
readonly helperManager: HelperManager,
) {}

Expand Down Expand Up @@ -210,6 +211,12 @@ export default class TokenProcessor {
if (token.numNullishCoalesceStarts || token.isOptionalChainStart) {
token.isAsyncOperation = isAsyncOperation(this);
}
if (this.disableESTransforms) {
if (token.isAsyncOperation) {
this.resultCode += "await ";
}
return;
}
if (token.numNullishCoalesceStarts) {
for (let i = 0; i < token.numNullishCoalesceStarts; i++) {
if (token.isAsyncOperation) {
Expand Down Expand Up @@ -242,10 +249,10 @@ export default class TokenProcessor {

private appendTokenSuffix(): void {
const token = this.currentToken();
if (token.isOptionalChainEnd) {
if (token.isOptionalChainEnd && !this.disableESTransforms) {
this.resultCode += "])";
}
if (token.numNullishCoalesceEnds) {
if (token.numNullishCoalesceEnds && !this.disableESTransforms) {
for (let i = 0; i < token.numNullishCoalesceEnds; i++) {
this.resultCode += "))";
}
Expand Down
9 changes: 8 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,20 @@ function getSucraseContext(code: string, options: Options): SucraseContext {
const isJSXEnabled = options.transforms.includes("jsx");
const isTypeScriptEnabled = options.transforms.includes("typescript");
const isFlowEnabled = options.transforms.includes("flow");
const disableESTransforms = options.disableESTransforms === true;
const file = parse(code, isJSXEnabled, isTypeScriptEnabled, isFlowEnabled);
const tokens = file.tokens;
const scopes = file.scopes;

const nameManager = new NameManager(code, tokens);
const helperManager = new HelperManager(nameManager);
const tokenProcessor = new TokenProcessor(code, tokens, isFlowEnabled, helperManager);
const tokenProcessor = new TokenProcessor(
code,
tokens,
isFlowEnabled,
disableESTransforms,
helperManager,
);
const enableLegacyTypeScriptModuleInterop = Boolean(options.enableLegacyTypeScriptModuleInterop);

let importProcessor = null;
Expand Down
13 changes: 8 additions & 5 deletions src/transformers/RootTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@ export default class RootTransformer {
this.isImportsTransformEnabled = transforms.includes("imports");
this.isReactHotLoaderTransformEnabled = transforms.includes("react-hot-loader");

this.transformers.push(
new OptionalChainingNullishTransformer(tokenProcessor, this.nameManager),
);
this.transformers.push(new NumericSeparatorTransformer(tokenProcessor));
this.transformers.push(new OptionalCatchBindingTransformer(tokenProcessor, this.nameManager));
if (!options.disableESTransforms) {
this.transformers.push(
new OptionalChainingNullishTransformer(tokenProcessor, this.nameManager),
);
this.transformers.push(new NumericSeparatorTransformer(tokenProcessor));
this.transformers.push(new OptionalCatchBindingTransformer(tokenProcessor, this.nameManager));
}

if (transforms.includes("jsx")) {
this.transformers.push(
new JSXTransformer(this, tokenProcessor, importProcessor, this.nameManager, options),
Expand Down
2 changes: 1 addition & 1 deletion test/identifyShadowedGlobals-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function assertHasShadowedGlobals(code: string, expected: boolean): void {
const file = parse(code, false, false, false);
const nameManager = new NameManager(code, file.tokens);
const helperManager = new HelperManager(nameManager);
const tokenProcessor = new TokenProcessor(code, file.tokens, false, helperManager);
const tokenProcessor = new TokenProcessor(code, file.tokens, false, false, helperManager);
const importProcessor = new CJSImportProcessor(
nameManager,
tokenProcessor,
Expand Down
19 changes: 18 additions & 1 deletion test/react-hot-loader-test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type {Transform} from "../src";
import * as assert from "assert";

import {Transform, transform} from "../src";
import {ESMODULE_PREFIX, IMPORT_DEFAULT_PREFIX, RHL_PREFIX} from "./prefixes";
import {assertResult} from "./util";

Expand Down Expand Up @@ -249,4 +251,19 @@ describe("transform react-hot-loader", () => {
},
);
});

it("checks that filePath is specified", () => {
assert.throws(() => {
transform(
`
import React from 'react';
export const App = () => <div />;
`,
{
transforms: ["jsx", "imports", "react-hot-loader"],
},
);
}, new Error("filePath is required when using the react-hot-loader transform."));
});
});
46 changes: 46 additions & 0 deletions test/sucrase-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1301,4 +1301,50 @@ describe("sucrase", () => {
{transforms: []},
);
});

it("omits optional changing nullish transformations if ES transforms disabled", () => {
assertResult(
`
await navigator.share?.({});
console.log(window.globalStore?.value);
`,
`
await navigator.share?.({});
console.log(window.globalStore?.value);
`,
{transforms: [], disableESTransforms: true},
);
});

it("omits optional catch binding transformations if ES transforms disabled", () => {
assertResult(
`
try {
throw 0;
} catch {
console.log('Caught');
}
`,
`
try {
throw 0;
} catch {
console.log('Caught');
}
`,
{transforms: [], disableESTransforms: true},
);
});

it("omits numeric separator transformations if ES transforms disabled", () => {
assertResult(
`
console.log(123_456.777_888_999);
`,
`
console.log(123_456.777_888_999);
`,
{transforms: [], disableESTransforms: true},
);
});
});

0 comments on commit edc56e0

Please # to comment.