Skip to content

Commit

Permalink
Fully support || and && in pluginToggleBooleanFlag
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Sep 12, 2023
1 parent 9240942 commit de7683b
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 92 deletions.
110 changes: 68 additions & 42 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,6 @@ module.exports = function (api) {
{ name: "IS_STANDALONE", value: env === "standalone" },
"flag-IS_STANDALONE",
],
[
pluginToggleBooleanFlag,
{
name: "USE_ESM_OR_STANDALONE",
value: outputType === "module" || env === "standalone",
},
"flag-USE_ESM_OR_STANDALONE",
],

[
pluginToggleBooleanFlag,
{
Expand Down Expand Up @@ -516,24 +507,65 @@ function pluginPolyfillsOldNode({ template, types: t }) {
* @returns {import("@babel/core").PluginObj}
*/
function pluginToggleBooleanFlag({ types: t }, { name, value }) {
function check(test) {
let keepConsequent = value;
function evaluate(test) {
const res = {
replace: replacement => ({ replacement, value: null, unrelated: false }),
value: value => ({ replacement: null, value, unrelated: false }),
unrelated: () => ({
replacement: test.node,
value: null,
unrelated: true,
}),
};

if (test.isIdentifier({ name }) || test.matchesPattern(name)) {
return res.value(value);
}

if (test.isUnaryExpression({ operator: "!" })) {
test = test.get("argument");
keepConsequent = !keepConsequent;
const arg = evaluate(test.get("argument"));
return arg.unrelated
? res.unrelated()
: arg.replacement
? res.replacement(t.unaryExpression("!", arg.replacement))
: res.value(!arg.value);
}
return {
test,
keepConsequent,
};

if (test.isLogicalExpression({ operator: "||" })) {
const left = evaluate(test.get("left"));
const right = evaluate(test.get("right"));

if (left.value === true || right.value === true) return res.value(true);
if (left.value === false && right.value === false) {
return res.value(false);
}
if (left.value === false) return res.replace(right.replacement);
if (right.value === false) return res.replace(left.replacement);
if (left.unrelated && right.unrelated) return res.unrelated();
return t.logicalExpression("||", left.replacement, right.replacement);
}

if (test.isLogicalExpression({ operator: "&&" })) {
const left = evaluate(test.get("left"));
const right = evaluate(test.get("right"));

if (left.value === true && right.value === true) return res.value(true);
if (left.value === false || right.value === false) {
return res.value(false);
}
if (left.value === true) return res.replace(right.replacement);
if (right.value === true) return res.replace(left.replacement);
if (left.unrelated && right.unrelated) return res.unrelated();
return t.logicalExpression("&&", left.replacement, right.replacement);
}

return res.unrelated();
}

return {
visitor: {
"IfStatement|ConditionalExpression"(path) {
// eslint-disable-next-line prefer-const
let { test, keepConsequent } = check(path.get("test"));
let test = path.get("test");

// yarn-plugin-conditions injects bool(process.env.BABEL_8_BREAKING)
// tests, to properly cast the env variable to a boolean.
Expand All @@ -545,32 +577,26 @@ function pluginToggleBooleanFlag({ types: t }, { name, value }) {
test = test.get("arguments")[0];
}

if (!test.isIdentifier({ name }) && !test.matchesPattern(name)) return;
const res = evaluate(test);

path.replaceWith(
keepConsequent
? path.node.consequent
: path.node.alternate || t.emptyStatement()
);
if (res.unrelated) return;
if (res.replacement) {
path.get("test").replaceWith(res.replacement);
} else {
path.replaceWith(
res.value
? path.node.consequent
: path.node.alternate || t.emptyStatement()
);
}
},
LogicalExpression(path) {
const { test, keepConsequent } = check(path.get("left"));

if (!test.matchesPattern(name)) return;

switch (path.node.operator) {
case "&&":
path.replaceWith(
keepConsequent ? path.node.right : t.booleanLiteral(false)
);
break;
case "||":
path.replaceWith(
keepConsequent ? t.booleanLiteral(true) : path.node.right
);
break;
default:
throw path.buildCodeFrameError("This check could not be stripped.");
const res = evaluate(path.get("test"));
if (res.unrelated) return;
if (res.replacement) {
path.get("test").replaceWith(res.replacement);
} else {
path.replaceWith(t.booleanLiteral(res.value));
}
},
MemberExpression(path) {
Expand Down
114 changes: 64 additions & 50 deletions packages/babel-preset-env/src/available-plugins.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/* eslint sort-keys: "error" */

declare const USE_ESM_OR_STANDALONE: boolean;

import syntaxImportAssertions from "@babel/plugin-syntax-import-assertions";
import syntaxImportAttributes from "@babel/plugin-syntax-import-attributes";

Expand Down Expand Up @@ -152,60 +150,76 @@ if (!process.env.BABEL_8_BREAKING) {
const e = () => () => () => ({});

Object.assign(availablePlugins, {
"syntax-async-generators": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-async-generators"),
"syntax-class-properties": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-class-properties"),
"syntax-class-static-block": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-class-static-block"),
"syntax-dynamic-import": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-dynamic-import"),
"syntax-export-namespace-from": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-export-namespace-from"),
"syntax-import-meta": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-import-meta"),
"syntax-json-strings": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-json-strings"),
"syntax-logical-assignment-operators": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-logical-assignment-operators"),
"syntax-nullish-coalescing-operator": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-nullish-coalescing-operator"),
"syntax-numeric-separator": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-numeric-separator"),
"syntax-object-rest-spread": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-object-rest-spread"),
"syntax-optional-catch-binding": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-optional-catch-binding"),
"syntax-optional-chaining": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-optional-chaining"),
"syntax-private-property-in-object": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-private-property-in-object"),
"syntax-top-level-await": USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-top-level-await"),
"syntax-async-generators":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-async-generators"),
"syntax-class-properties":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-class-properties"),
"syntax-class-static-block":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-class-static-block"),
"syntax-dynamic-import":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-dynamic-import"),
"syntax-export-namespace-from":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-export-namespace-from"),
"syntax-import-meta":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-import-meta"),
"syntax-json-strings":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-json-strings"),
"syntax-logical-assignment-operators":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-logical-assignment-operators"),
"syntax-nullish-coalescing-operator":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-nullish-coalescing-operator"),
"syntax-numeric-separator":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-numeric-separator"),
"syntax-object-rest-spread":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-object-rest-spread"),
"syntax-optional-catch-binding":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-optional-catch-binding"),
"syntax-optional-chaining":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-optional-chaining"),
"syntax-private-property-in-object":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-private-property-in-object"),
"syntax-top-level-await":
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-top-level-await"),
});

// This is a CJS plugin that depends on a package from the monorepo, so it
// breaks using ESM. Given that ESM builds are new enough to have this
// syntax enabled by default, we can safely skip enabling it.
if (!USE_ESM) {
// @ts-expect-error unknown key
availablePlugins["unicode-sets-regex"] = USE_ESM_OR_STANDALONE
? e()
: () => require("@babel/plugin-syntax-unicode-sets-regex");
availablePlugins["unicode-sets-regex"] =
USE_ESM || IS_STANDALONE
? e()
: () => require("@babel/plugin-syntax-unicode-sets-regex");
}
}

0 comments on commit de7683b

Please sign in to comment.