Implement @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining (#13009)
This commit is contained in:
parent
974f9c5b01
commit
c2a42492db
@ -426,6 +426,7 @@ const libBundles = [
|
||||
"packages/babel-plugin-proposal-optional-chaining",
|
||||
"packages/babel-preset-typescript",
|
||||
"packages/babel-helper-member-expression-to-functions",
|
||||
"packages/babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining",
|
||||
].map(src => ({
|
||||
src,
|
||||
format: "cjs",
|
||||
|
||||
@ -166,7 +166,9 @@ declare module "@babel/helper-function-name" {
|
||||
}
|
||||
|
||||
declare module "@babel/helper-split-export-declaration" {
|
||||
declare export default function splitExportDeclaration(exportDeclaration: any): any;
|
||||
declare export default function splitExportDeclaration(
|
||||
exportDeclaration: any
|
||||
): any;
|
||||
}
|
||||
|
||||
declare module "@babel/traverse" {
|
||||
@ -196,6 +198,13 @@ declare module "@babel/highlight" {
|
||||
/**
|
||||
* Highlight `code`.
|
||||
*/
|
||||
declare export default function highlight(code: string, options?: Options): string;
|
||||
declare export { getChalk, shouldHighlight };
|
||||
declare export default function highlight(
|
||||
code: string,
|
||||
options?: Options
|
||||
): string;
|
||||
declare export { getChalk, shouldHighlight };
|
||||
}
|
||||
|
||||
declare module "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
src
|
||||
test
|
||||
*.log
|
||||
@ -0,0 +1,19 @@
|
||||
# @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining
|
||||
|
||||
> Transform optional chaining operators to workaround a [v8 bug](https://crbug.com/v8/11558).
|
||||
|
||||
See our website [@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining](https://babeljs.io/docs/en/babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining --dev
|
||||
```
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining",
|
||||
"version": "7.13.8",
|
||||
"description": "Transform optional chaining operators to workaround https://crbug.com/v8/11558",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining"
|
||||
},
|
||||
"homepage": "https://babel.dev/docs/en/next/babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"exports": {
|
||||
".": [
|
||||
"./lib/index.js"
|
||||
]
|
||||
},
|
||||
"keywords": [
|
||||
"babel-plugin",
|
||||
"bugfix"
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "workspace:^7.13.0",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "workspace:^7.12.1",
|
||||
"@babel/plugin-proposal-optional-chaining": "workspace:^7.13.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "workspace:*",
|
||||
"@babel/helper-plugin-test-runner": "workspace:*",
|
||||
"@babel/traverse": "workspace:*"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
import { transform } from "@babel/plugin-proposal-optional-chaining";
|
||||
import { shouldTransform } from "./util";
|
||||
|
||||
export default declare(api => {
|
||||
api.assertVersion(7);
|
||||
|
||||
const noDocumentAll = api.assumption("noDocumentAll");
|
||||
const pureGetters = api.assumption("pureGetters");
|
||||
|
||||
return {
|
||||
name: "bugfix-v8-spread-parameters-in-optional-chaining",
|
||||
|
||||
visitor: {
|
||||
"OptionalCallExpression|OptionalMemberExpression"(path) {
|
||||
if (shouldTransform(path)) {
|
||||
transform(path, { noDocumentAll, pureGetters });
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
@ -0,0 +1,59 @@
|
||||
import { skipTransparentExprWrappers } from "@babel/helper-skip-transparent-expression-wrappers";
|
||||
import type { NodePath } from "@babel/traverse";
|
||||
import { types as t } from "@babel/core";
|
||||
// https://crbug.com/v8/11558
|
||||
|
||||
// check if there is a spread element followed by another argument.
|
||||
// (...[], 0) or (...[], ...[])
|
||||
|
||||
function matchAffectedArguments(argumentNodes) {
|
||||
const spreadIndex = argumentNodes.findIndex(node => t.isSpreadElement(node));
|
||||
return spreadIndex >= 0 && spreadIndex !== argumentNodes.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the optional chain is affected by https://crbug.com/v8/11558.
|
||||
* This routine MUST not manipulate NodePath
|
||||
*
|
||||
* @export
|
||||
* @param {(NodePath<t.OptionalMemberExpression | t.OptionalCallExpression>)} path
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function shouldTransform(
|
||||
path: NodePath<t.OptionalMemberExpression | t.OptionalCallExpression>,
|
||||
): boolean {
|
||||
let optionalPath = path;
|
||||
const chains = [];
|
||||
while (
|
||||
optionalPath.isOptionalMemberExpression() ||
|
||||
optionalPath.isOptionalCallExpression()
|
||||
) {
|
||||
const { node } = optionalPath;
|
||||
chains.push(node);
|
||||
|
||||
if (optionalPath.isOptionalMemberExpression()) {
|
||||
optionalPath = skipTransparentExprWrappers(optionalPath.get("object"));
|
||||
} else if (optionalPath.isOptionalCallExpression()) {
|
||||
optionalPath = skipTransparentExprWrappers(optionalPath.get("callee"));
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < chains.length; i++) {
|
||||
const node = chains[i];
|
||||
if (
|
||||
t.isOptionalCallExpression(node) &&
|
||||
matchAffectedArguments(node.arguments)
|
||||
) {
|
||||
// f?.(...[], 0)
|
||||
if (node.optional) {
|
||||
return true;
|
||||
}
|
||||
// o?.m(...[], 0)
|
||||
// when node.optional is false, chains[i + 1] is always well defined
|
||||
const callee = chains[i + 1];
|
||||
if (t.isOptionalMemberExpression(callee, { optional: true })) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
fn?.(...b, 1);
|
||||
|
||||
a?.b(...c, 1);
|
||||
|
||||
a?.b?.(...c, 1);
|
||||
|
||||
a.b?.(...c, 1);
|
||||
@ -0,0 +1,6 @@
|
||||
var _fn, _a, _a2, _a2$b, _a$b, _a3;
|
||||
|
||||
(_fn = fn) === null || _fn === void 0 ? void 0 : _fn(...b, 1);
|
||||
(_a = a) === null || _a === void 0 ? void 0 : _a.b(...c, 1);
|
||||
(_a2 = a) === null || _a2 === void 0 ? void 0 : (_a2$b = _a2.b) === null || _a2$b === void 0 ? void 0 : _a2$b.call(_a2, ...c, 1);
|
||||
(_a$b = (_a3 = a).b) === null || _a$b === void 0 ? void 0 : _a$b.call(_a3, ...c, 1);
|
||||
@ -0,0 +1,19 @@
|
||||
foo?.(...[], 1);
|
||||
|
||||
foo?.bar(...[], 1)
|
||||
|
||||
foo.bar?.(foo.bar, ...[], 1)
|
||||
|
||||
foo?.bar?.(foo.bar, ...[], 1)
|
||||
|
||||
foo?.(...[], 1).bar
|
||||
|
||||
foo?.(...[], 1)?.bar
|
||||
|
||||
foo.bar?.(...[], 1).baz
|
||||
|
||||
foo.bar?.(...[], 1)?.baz
|
||||
|
||||
foo?.bar?.(...[], 1).baz
|
||||
|
||||
foo?.bar?.(...[], 1)?.baz
|
||||
@ -0,0 +1,12 @@
|
||||
var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5, _foo6, _foo7, _foo$bar2, _foo8, _foo$bar3, _foo9, _foo$bar3$call, _foo10, _foo10$bar, _foo11, _foo11$bar, _foo11$bar$call;
|
||||
|
||||
(_foo = foo) === null || _foo === void 0 ? void 0 : _foo(...[], 1);
|
||||
(_foo2 = foo) === null || _foo2 === void 0 ? void 0 : _foo2.bar(...[], 1);
|
||||
(_foo$bar = (_foo3 = foo).bar) === null || _foo$bar === void 0 ? void 0 : _foo$bar.call(_foo3, foo.bar, ...[], 1);
|
||||
(_foo4 = foo) === null || _foo4 === void 0 ? void 0 : (_foo4$bar = _foo4.bar) === null || _foo4$bar === void 0 ? void 0 : _foo4$bar.call(_foo4, foo.bar, ...[], 1);
|
||||
(_foo5 = foo) === null || _foo5 === void 0 ? void 0 : _foo5(...[], 1).bar;
|
||||
(_foo6 = foo) === null || _foo6 === void 0 ? void 0 : (_foo7 = _foo6(...[], 1)) === null || _foo7 === void 0 ? void 0 : _foo7.bar;
|
||||
(_foo$bar2 = (_foo8 = foo).bar) === null || _foo$bar2 === void 0 ? void 0 : _foo$bar2.call(_foo8, ...[], 1).baz;
|
||||
(_foo$bar3 = (_foo9 = foo).bar) === null || _foo$bar3 === void 0 ? void 0 : (_foo$bar3$call = _foo$bar3.call(_foo9, ...[], 1)) === null || _foo$bar3$call === void 0 ? void 0 : _foo$bar3$call.baz;
|
||||
(_foo10 = foo) === null || _foo10 === void 0 ? void 0 : (_foo10$bar = _foo10.bar) === null || _foo10$bar === void 0 ? void 0 : _foo10$bar.call(_foo10, ...[], 1).baz;
|
||||
(_foo11 = foo) === null || _foo11 === void 0 ? void 0 : (_foo11$bar = _foo11.bar) === null || _foo11$bar === void 0 ? void 0 : (_foo11$bar$call = _foo11$bar.call(_foo11, ...[], 1)) === null || _foo11$bar$call === void 0 ? void 0 : _foo11$bar$call.baz;
|
||||
@ -0,0 +1,5 @@
|
||||
fn?.(...b, 1);
|
||||
|
||||
a?.b(...c, 1);
|
||||
|
||||
a?.b?.(...c, 1);
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": [
|
||||
"proposal-optional-chaining", "bugfix-v8-spread-parameters-in-optional-chaining"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
var _fn, _a, _a2, _a2$b;
|
||||
|
||||
(_fn = fn) === null || _fn === void 0 ? void 0 : _fn(...b, 1);
|
||||
(_a = a) === null || _a === void 0 ? void 0 : _a.b(...c, 1);
|
||||
(_a2 = a) === null || _a2 === void 0 ? void 0 : (_a2$b = _a2.b) === null || _a2$b === void 0 ? void 0 : _a2$b.call(_a2, ...c, 1);
|
||||
@ -0,0 +1,7 @@
|
||||
fn?.(...b, 1);
|
||||
|
||||
a?.b(...c, 1);
|
||||
|
||||
a?.b?.(...c, 1);
|
||||
|
||||
a.b?.(...c, 1);
|
||||
@ -0,0 +1,6 @@
|
||||
var _fn, _a, _a2, _a2$b, _a$b, _a3;
|
||||
|
||||
(_fn = fn) === null || _fn === void 0 ? void 0 : _fn(...b, 1);
|
||||
(_a = a) === null || _a === void 0 ? void 0 : _a.b(...c, 1);
|
||||
(_a2 = a) === null || _a2 === void 0 ? void 0 : (_a2$b = _a2.b) === null || _a2$b === void 0 ? void 0 : _a2$b.call(_a2, ...c, 1);
|
||||
(_a$b = (_a3 = a).b) === null || _a$b === void 0 ? void 0 : _a$b.call(_a3, ...c, 1);
|
||||
@ -0,0 +1,13 @@
|
||||
class C {
|
||||
#m;
|
||||
constructor() {
|
||||
const o = null;
|
||||
const n = this;
|
||||
const p = o?.#m(...c, 1);
|
||||
const q = n?.#m?.(...c, 1);
|
||||
expect(p).toBe(undefined);
|
||||
expect(q).toBe(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
new C();
|
||||
@ -0,0 +1,9 @@
|
||||
class C {
|
||||
#m;
|
||||
constructor() {
|
||||
const o = null;
|
||||
const n = this;
|
||||
const p = o?.#m(...c, 1);
|
||||
const q = n?.#m?.(...c, 1);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": [
|
||||
"proposal-class-properties", "proposal-private-methods", "proposal-optional-chaining", "bugfix-v8-spread-parameters-in-optional-chaining"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
var _m = new WeakMap();
|
||||
|
||||
class C {
|
||||
constructor() {
|
||||
var _babelHelpers$classPr;
|
||||
|
||||
_m.set(this, {
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
|
||||
const o = null;
|
||||
const n = this;
|
||||
const p = o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldGet(o, _m).call(o, ...c, 1);
|
||||
const q = n === null || n === void 0 ? void 0 : (_babelHelpers$classPr = babelHelpers.classPrivateFieldGet(n, _m)) === null || _babelHelpers$classPr === void 0 ? void 0 : _babelHelpers$classPr.call(n, ...c, 1);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
class C {
|
||||
#m;
|
||||
constructor() {
|
||||
const o = null;
|
||||
const n = this;
|
||||
const p = o?.#m(...c, 1);
|
||||
const q = n?.#m?.(...c, 1);
|
||||
expect(p).toBe(undefined);
|
||||
expect(q).toBe(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
new C();
|
||||
@ -0,0 +1,9 @@
|
||||
class C {
|
||||
#m;
|
||||
constructor() {
|
||||
const o = null;
|
||||
const n = this;
|
||||
const p = o?.#m(...c, 1);
|
||||
const q = n?.#m?.(...c, 1);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": [
|
||||
"proposal-class-properties", "proposal-private-methods", "bugfix-v8-spread-parameters-in-optional-chaining"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
var _m = new WeakMap();
|
||||
|
||||
class C {
|
||||
constructor() {
|
||||
var _babelHelpers$classPr;
|
||||
|
||||
_m.set(this, {
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
|
||||
const o = null;
|
||||
const n = this;
|
||||
const p = o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldGet(o, _m).call(o, ...c, 1);
|
||||
const q = n === null || n === void 0 ? void 0 : (_babelHelpers$classPr = babelHelpers.classPrivateFieldGet(n, _m)) === null || _babelHelpers$classPr === void 0 ? void 0 : _babelHelpers$classPr.call(n, ...c, 1);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
class C {
|
||||
#m;
|
||||
constructor() {
|
||||
const o = null;
|
||||
const n = this;
|
||||
const p = o?.#m(...c, 1);
|
||||
const q = n?.#m?.(...c, 1);
|
||||
expect(p).toBe(undefined);
|
||||
expect(q).toBe(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
new C;
|
||||
@ -0,0 +1,9 @@
|
||||
class C {
|
||||
#m;
|
||||
constructor() {
|
||||
const o = null;
|
||||
const n = this;
|
||||
const p = o?.#m(...c, 1);
|
||||
const q = n?.#m?.(...c, 1);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
{
|
||||
"minNodeVersion": "14.0.0",
|
||||
"parserOpts": {
|
||||
"plugins": [
|
||||
"classPrivateMethods",
|
||||
"classPrivateProperties",
|
||||
"classProperties"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
class C {
|
||||
#m;
|
||||
|
||||
constructor() {
|
||||
var _n$m;
|
||||
|
||||
const o = null;
|
||||
const n = this;
|
||||
const p = o === null || o === void 0 ? void 0 : o.#m(...c, 1);
|
||||
const q = n === null || n === void 0 ? void 0 : (_n$m = n.#m) === null || _n$m === void 0 ? void 0 : _n$m.call(n, ...c, 1);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"plugins": ["bugfix-v8-spread-parameters-in-optional-chaining"]
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
import runner from "@babel/helper-plugin-test-runner";
|
||||
|
||||
runner(import.meta.url);
|
||||
@ -0,0 +1,115 @@
|
||||
import { parseSync, traverse } from "@babel/core";
|
||||
import { shouldTransform } from "../src/util.ts";
|
||||
|
||||
function getPath(input, parserOpts = {}) {
|
||||
let targetPath;
|
||||
traverse(
|
||||
parseSync(input, {
|
||||
parserOpts: {
|
||||
plugins: [
|
||||
"classPrivateMethods",
|
||||
"classPrivateProperties",
|
||||
"classProperties",
|
||||
...(parserOpts.plugins || []),
|
||||
],
|
||||
...parserOpts,
|
||||
},
|
||||
filename: "example.js",
|
||||
}),
|
||||
{
|
||||
"OptionalMemberExpression|OptionalCallExpression"(path) {
|
||||
targetPath = path;
|
||||
path.stop();
|
||||
},
|
||||
noScope: true,
|
||||
},
|
||||
);
|
||||
return targetPath;
|
||||
}
|
||||
|
||||
describe("shouldTransform", () => {
|
||||
const positiveCases = [
|
||||
"fn?.(...[], 0)",
|
||||
"fn?.(...[], ...[])",
|
||||
"fn?.(0, ...[], ...[])",
|
||||
"a?.b(...[], 0)",
|
||||
"a?.[b](...[], 0)",
|
||||
"a?.b?.(...[], 0)",
|
||||
"fn?.(0, ...[], 0)",
|
||||
"a?.b?.(0, ...[], 0)",
|
||||
"(a?.b)?.(...[], 0)",
|
||||
"a?.b.c?.(...[], 0)",
|
||||
"class C { #c; p = obj?.#c(...[], 0) }",
|
||||
"class C { #c; p = obj.#c?.(...[], 0) }",
|
||||
];
|
||||
|
||||
const negativeCases = [
|
||||
"a?.b",
|
||||
"fn?.(1)",
|
||||
"fn?.(...[])",
|
||||
"fn?.(1, ...[])",
|
||||
"a?.b(...[])",
|
||||
"a?.()(...[], 1)", // optional call under optional call is not affected
|
||||
"(a?.b)(...[], 1)", // not an optional call
|
||||
"a?.b.c(...[], 1)",
|
||||
"a?.[fn?.(...[], 0)]", // optional chain in property will be handled when traversed
|
||||
"a?.(fn?.(...[], 0))", // optional chain in arguments will be handled when traversed
|
||||
"class C { #c; p = obj?.#c(...[]) }",
|
||||
];
|
||||
|
||||
const typescriptPositiveCases = [
|
||||
"(a?.(...[], 0) as any)?.b",
|
||||
"(a?.(...[], 0) as any)?.()",
|
||||
];
|
||||
|
||||
const typescriptNegativeCases = ["(a?.b as any)(...[], 0)"];
|
||||
|
||||
describe("default parser options", () => {
|
||||
test.each(positiveCases)(
|
||||
"shouldTransform(a?.b in %p) should return true",
|
||||
input => {
|
||||
expect(shouldTransform(getPath(input))).toBe(true);
|
||||
},
|
||||
);
|
||||
test.each(negativeCases)(
|
||||
"shouldTransform(a?.b in %p) should return false",
|
||||
input => {
|
||||
expect(shouldTransform(getPath(input))).toBe(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("createParenthesizedExpressions", () => {
|
||||
test.each(positiveCases)(
|
||||
"shouldTransform(a?.b in %p with { createParenthesizedExpressions: true }) should return true",
|
||||
input => {
|
||||
const parserOpts = { createParenthesizedExpressions: true };
|
||||
expect(shouldTransform(getPath(input, parserOpts))).toBe(true);
|
||||
},
|
||||
);
|
||||
test.each(negativeCases)(
|
||||
"shouldTransform(a?.b in %p with { createParenthesizedExpressions: true }) should return false",
|
||||
input => {
|
||||
const parserOpts = { createParenthesizedExpressions: true };
|
||||
expect(shouldTransform(getPath(input, parserOpts))).toBe(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("plugins: [typescript]", () => {
|
||||
test.each(positiveCases.concat(typescriptPositiveCases))(
|
||||
"shouldTransform(a?.b in %p with { plugins: [typescript] }) should return true",
|
||||
input => {
|
||||
const parserOpts = { plugins: ["typescript"] };
|
||||
expect(shouldTransform(getPath(input, parserOpts))).toBe(true);
|
||||
},
|
||||
);
|
||||
test.each(negativeCases.concat(typescriptNegativeCases))(
|
||||
"shouldTransform(a?.b in %p with { plugins: [typescript] }) should return false",
|
||||
input => {
|
||||
const parserOpts = { plugins: ["typescript"] };
|
||||
expect(shouldTransform(getPath(input, parserOpts))).toBe(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,13 +1,6 @@
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
import {
|
||||
isTransparentExprWrapper,
|
||||
skipTransparentExprWrappers,
|
||||
} from "@babel/helper-skip-transparent-expression-wrappers";
|
||||
import syntaxOptionalChaining from "@babel/plugin-syntax-optional-chaining";
|
||||
import { types as t, template } from "@babel/core";
|
||||
import { willPathCastToBoolean, findOutermostTransparentParent } from "./util";
|
||||
|
||||
const { ast } = template.expression;
|
||||
import { transform } from "./transform";
|
||||
|
||||
export default declare((api, options) => {
|
||||
api.assertVersion(7);
|
||||
@ -16,229 +9,16 @@ export default declare((api, options) => {
|
||||
const noDocumentAll = api.assumption("noDocumentAll") ?? loose;
|
||||
const pureGetters = api.assumption("pureGetters") ?? loose;
|
||||
|
||||
function isSimpleMemberExpression(expression) {
|
||||
expression = skipTransparentExprWrappers(expression);
|
||||
return (
|
||||
t.isIdentifier(expression) ||
|
||||
t.isSuper(expression) ||
|
||||
(t.isMemberExpression(expression) &&
|
||||
!expression.computed &&
|
||||
isSimpleMemberExpression(expression.object))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a given optional chain `path` needs to be memoized
|
||||
* @param {NodePath} path
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function needsMemoize(path) {
|
||||
let optionalPath = path;
|
||||
const { scope } = path;
|
||||
while (
|
||||
optionalPath.isOptionalMemberExpression() ||
|
||||
optionalPath.isOptionalCallExpression()
|
||||
) {
|
||||
const { node } = optionalPath;
|
||||
const childKey = optionalPath.isOptionalMemberExpression()
|
||||
? "object"
|
||||
: "callee";
|
||||
const childPath = skipTransparentExprWrappers(optionalPath.get(childKey));
|
||||
if (node.optional) {
|
||||
return !scope.isStatic(childPath.node);
|
||||
}
|
||||
|
||||
optionalPath = childPath;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: "proposal-optional-chaining",
|
||||
inherits: syntaxOptionalChaining,
|
||||
|
||||
visitor: {
|
||||
"OptionalCallExpression|OptionalMemberExpression"(path) {
|
||||
const { scope } = path;
|
||||
// maybeWrapped points to the outermost transparent expression wrapper
|
||||
// or the path itself
|
||||
const maybeWrapped = findOutermostTransparentParent(path);
|
||||
const { parentPath } = maybeWrapped;
|
||||
const willReplacementCastToBoolean = willPathCastToBoolean(
|
||||
maybeWrapped,
|
||||
);
|
||||
let isDeleteOperation = false;
|
||||
const parentIsCall =
|
||||
parentPath.isCallExpression({ callee: maybeWrapped.node }) &&
|
||||
// note that the first condition must implies that `path.optional` is `true`,
|
||||
// otherwise the parentPath should be an OptionalCallExpressioin
|
||||
path.isOptionalMemberExpression();
|
||||
|
||||
const optionals = [];
|
||||
|
||||
let optionalPath = path;
|
||||
// Replace `function (a, x = a.b?.c) {}` to `function (a, x = (() => a.b?.c)() ){}`
|
||||
// so the temporary variable can be injected in correct scope
|
||||
if (scope.path.isPattern() && needsMemoize(optionalPath)) {
|
||||
path.replaceWith(template.ast`(() => ${path.node})()`);
|
||||
// The injected optional chain will be queued and eventually transformed when visited
|
||||
return;
|
||||
}
|
||||
while (
|
||||
optionalPath.isOptionalMemberExpression() ||
|
||||
optionalPath.isOptionalCallExpression()
|
||||
) {
|
||||
const { node } = optionalPath;
|
||||
if (node.optional) {
|
||||
optionals.push(node);
|
||||
}
|
||||
|
||||
if (optionalPath.isOptionalMemberExpression()) {
|
||||
optionalPath.node.type = "MemberExpression";
|
||||
optionalPath = skipTransparentExprWrappers(
|
||||
optionalPath.get("object"),
|
||||
);
|
||||
} else if (optionalPath.isOptionalCallExpression()) {
|
||||
optionalPath.node.type = "CallExpression";
|
||||
optionalPath = skipTransparentExprWrappers(
|
||||
optionalPath.get("callee"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let replacementPath = path;
|
||||
if (parentPath.isUnaryExpression({ operator: "delete" })) {
|
||||
replacementPath = parentPath;
|
||||
isDeleteOperation = true;
|
||||
}
|
||||
for (let i = optionals.length - 1; i >= 0; i--) {
|
||||
const node = optionals[i];
|
||||
|
||||
const isCall = t.isCallExpression(node);
|
||||
const replaceKey = isCall ? "callee" : "object";
|
||||
|
||||
const chainWithTypes = node[replaceKey];
|
||||
let chain = chainWithTypes;
|
||||
|
||||
while (isTransparentExprWrapper(chain)) {
|
||||
chain = chain.expression;
|
||||
}
|
||||
|
||||
let ref;
|
||||
let check;
|
||||
if (isCall && t.isIdentifier(chain, { name: "eval" })) {
|
||||
check = ref = chain;
|
||||
// `eval?.()` is an indirect eval call transformed to `(0,eval)()`
|
||||
node[replaceKey] = t.sequenceExpression([t.numericLiteral(0), ref]);
|
||||
} else if (pureGetters && isCall && isSimpleMemberExpression(chain)) {
|
||||
// If we assume getters are pure (avoiding a Function#call) and we are at the call,
|
||||
// we can avoid a needless memoize. We only do this if the callee is a simple member
|
||||
// expression, to avoid multiple calls to nested call expressions.
|
||||
check = ref = chainWithTypes;
|
||||
} else {
|
||||
ref = scope.maybeGenerateMemoised(chain);
|
||||
if (ref) {
|
||||
check = t.assignmentExpression(
|
||||
"=",
|
||||
t.cloneNode(ref),
|
||||
// Here `chainWithTypes` MUST NOT be cloned because it could be
|
||||
// updated when generating the memoised context of a call
|
||||
// expression
|
||||
chainWithTypes,
|
||||
);
|
||||
|
||||
node[replaceKey] = ref;
|
||||
} else {
|
||||
check = ref = chainWithTypes;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure call expressions have the proper `this`
|
||||
// `foo.bar()` has context `foo`.
|
||||
if (isCall && t.isMemberExpression(chain)) {
|
||||
if (pureGetters && isSimpleMemberExpression(chain)) {
|
||||
// To avoid a Function#call, we can instead re-grab the property from the context object.
|
||||
// `a.?b.?()` translates roughly to `_a.b != null && _a.b()`
|
||||
node.callee = chainWithTypes;
|
||||
} else {
|
||||
// Otherwise, we need to memoize the context object, and change the call into a Function#call.
|
||||
// `a.?b.?()` translates roughly to `(_b = _a.b) != null && _b.call(_a)`
|
||||
const { object } = chain;
|
||||
let context = scope.maybeGenerateMemoised(object);
|
||||
if (context) {
|
||||
chain.object = t.assignmentExpression("=", context, object);
|
||||
} else if (t.isSuper(object)) {
|
||||
context = t.thisExpression();
|
||||
} else {
|
||||
context = object;
|
||||
}
|
||||
|
||||
node.arguments.unshift(t.cloneNode(context));
|
||||
node.callee = t.memberExpression(
|
||||
node.callee,
|
||||
t.identifier("call"),
|
||||
);
|
||||
}
|
||||
}
|
||||
let replacement = replacementPath.node;
|
||||
// Ensure (a?.b)() has proper `this`
|
||||
// The `parentIsCall` is constant within loop, we should check i === 0
|
||||
// to ensure that it is only applied to the first optional chain element
|
||||
// i.e. `?.b` in `(a?.b.c)()`
|
||||
if (i === 0 && parentIsCall) {
|
||||
// `(a?.b)()` to `(a == null ? undefined : a.b.bind(a))()`
|
||||
const object = skipTransparentExprWrappers(
|
||||
replacementPath.get("object"),
|
||||
).node;
|
||||
let baseRef;
|
||||
if (!pureGetters || !isSimpleMemberExpression(object)) {
|
||||
// memoize the context object when getters are not always pure
|
||||
// or the object is not a simple member expression
|
||||
// `(a?.b.c)()` to `(a == null ? undefined : (_a$b = a.b).c.bind(_a$b))()`
|
||||
baseRef = scope.maybeGenerateMemoised(object);
|
||||
if (baseRef) {
|
||||
replacement.object = t.assignmentExpression(
|
||||
"=",
|
||||
baseRef,
|
||||
object,
|
||||
);
|
||||
}
|
||||
}
|
||||
replacement = t.callExpression(
|
||||
t.memberExpression(replacement, t.identifier("bind")),
|
||||
[t.cloneNode(baseRef ?? object)],
|
||||
);
|
||||
}
|
||||
|
||||
if (willReplacementCastToBoolean) {
|
||||
// `if (a?.b) {}` transformed to `if (a != null && a.b) {}`
|
||||
// we don't need to return `void 0` because the returned value will
|
||||
// eveutally cast to boolean.
|
||||
const nonNullishCheck = noDocumentAll
|
||||
? ast`${t.cloneNode(check)} != null`
|
||||
: ast`
|
||||
${t.cloneNode(check)} !== null && ${t.cloneNode(ref)} !== void 0`;
|
||||
replacementPath.replaceWith(
|
||||
t.logicalExpression("&&", nonNullishCheck, replacement),
|
||||
);
|
||||
replacementPath = skipTransparentExprWrappers(
|
||||
replacementPath.get("right"),
|
||||
);
|
||||
} else {
|
||||
const nullishCheck = noDocumentAll
|
||||
? ast`${t.cloneNode(check)} == null`
|
||||
: ast`
|
||||
${t.cloneNode(check)} === null || ${t.cloneNode(ref)} === void 0`;
|
||||
|
||||
const returnValue = isDeleteOperation ? ast`true` : ast`void 0`;
|
||||
replacementPath.replaceWith(
|
||||
t.conditionalExpression(nullishCheck, returnValue, replacement),
|
||||
);
|
||||
replacementPath = skipTransparentExprWrappers(
|
||||
replacementPath.get("alternate"),
|
||||
);
|
||||
}
|
||||
}
|
||||
transform(path, { noDocumentAll, pureGetters });
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export { transform };
|
||||
|
||||
@ -0,0 +1,219 @@
|
||||
import { types as t, template } from "@babel/core";
|
||||
import {
|
||||
isTransparentExprWrapper,
|
||||
skipTransparentExprWrappers,
|
||||
} from "@babel/helper-skip-transparent-expression-wrappers";
|
||||
import { willPathCastToBoolean, findOutermostTransparentParent } from "./util";
|
||||
|
||||
const { ast } = template.expression;
|
||||
|
||||
function isSimpleMemberExpression(expression) {
|
||||
expression = skipTransparentExprWrappers(expression);
|
||||
return (
|
||||
t.isIdentifier(expression) ||
|
||||
t.isSuper(expression) ||
|
||||
(t.isMemberExpression(expression) &&
|
||||
!expression.computed &&
|
||||
isSimpleMemberExpression(expression.object))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a given optional chain `path` needs to be memoized
|
||||
* @param {NodePath} path
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function needsMemoize(path) {
|
||||
let optionalPath = path;
|
||||
const { scope } = path;
|
||||
while (
|
||||
optionalPath.isOptionalMemberExpression() ||
|
||||
optionalPath.isOptionalCallExpression()
|
||||
) {
|
||||
const { node } = optionalPath;
|
||||
const childKey = optionalPath.isOptionalMemberExpression()
|
||||
? "object"
|
||||
: "callee";
|
||||
const childPath = skipTransparentExprWrappers(optionalPath.get(childKey));
|
||||
if (node.optional) {
|
||||
return !scope.isStatic(childPath.node);
|
||||
}
|
||||
|
||||
optionalPath = childPath;
|
||||
}
|
||||
}
|
||||
|
||||
export function transform(
|
||||
path: NodePath<t.OptionalCallExpression | t.OptionalMemberExpression>,
|
||||
{
|
||||
pureGetters,
|
||||
noDocumentAll,
|
||||
}: { pureGetters: boolean, noDocumentAll: boolean },
|
||||
) {
|
||||
const { scope } = path;
|
||||
// maybeWrapped points to the outermost transparent expression wrapper
|
||||
// or the path itself
|
||||
const maybeWrapped = findOutermostTransparentParent(path);
|
||||
const { parentPath } = maybeWrapped;
|
||||
const willReplacementCastToBoolean = willPathCastToBoolean(maybeWrapped);
|
||||
let isDeleteOperation = false;
|
||||
const parentIsCall =
|
||||
parentPath.isCallExpression({ callee: maybeWrapped.node }) &&
|
||||
// note that the first condition must implies that `path.optional` is `true`,
|
||||
// otherwise the parentPath should be an OptionalCallExpression
|
||||
path.isOptionalMemberExpression();
|
||||
|
||||
const optionals = [];
|
||||
|
||||
let optionalPath = path;
|
||||
// Replace `function (a, x = a.b?.c) {}` to `function (a, x = (() => a.b?.c)() ){}`
|
||||
// so the temporary variable can be injected in correct scope
|
||||
if (scope.path.isPattern() && needsMemoize(optionalPath)) {
|
||||
path.replaceWith(template.ast`(() => ${path.node})()`);
|
||||
// The injected optional chain will be queued and eventually transformed when visited
|
||||
return;
|
||||
}
|
||||
while (
|
||||
optionalPath.isOptionalMemberExpression() ||
|
||||
optionalPath.isOptionalCallExpression()
|
||||
) {
|
||||
const { node } = optionalPath;
|
||||
if (node.optional) {
|
||||
optionals.push(node);
|
||||
}
|
||||
|
||||
if (optionalPath.isOptionalMemberExpression()) {
|
||||
optionalPath.node.type = "MemberExpression";
|
||||
optionalPath = skipTransparentExprWrappers(optionalPath.get("object"));
|
||||
} else if (optionalPath.isOptionalCallExpression()) {
|
||||
optionalPath.node.type = "CallExpression";
|
||||
optionalPath = skipTransparentExprWrappers(optionalPath.get("callee"));
|
||||
}
|
||||
}
|
||||
|
||||
let replacementPath = path;
|
||||
if (parentPath.isUnaryExpression({ operator: "delete" })) {
|
||||
replacementPath = parentPath;
|
||||
isDeleteOperation = true;
|
||||
}
|
||||
for (let i = optionals.length - 1; i >= 0; i--) {
|
||||
const node = optionals[i];
|
||||
|
||||
const isCall = t.isCallExpression(node);
|
||||
const replaceKey = isCall ? "callee" : "object";
|
||||
|
||||
const chainWithTypes = node[replaceKey];
|
||||
let chain = chainWithTypes;
|
||||
|
||||
while (isTransparentExprWrapper(chain)) {
|
||||
chain = chain.expression;
|
||||
}
|
||||
|
||||
let ref;
|
||||
let check;
|
||||
if (isCall && t.isIdentifier(chain, { name: "eval" })) {
|
||||
check = ref = chain;
|
||||
// `eval?.()` is an indirect eval call transformed to `(0,eval)()`
|
||||
node[replaceKey] = t.sequenceExpression([t.numericLiteral(0), ref]);
|
||||
} else if (pureGetters && isCall && isSimpleMemberExpression(chain)) {
|
||||
// If we assume getters are pure (avoiding a Function#call) and we are at the call,
|
||||
// we can avoid a needless memoize. We only do this if the callee is a simple member
|
||||
// expression, to avoid multiple calls to nested call expressions.
|
||||
check = ref = chainWithTypes;
|
||||
} else {
|
||||
ref = scope.maybeGenerateMemoised(chain);
|
||||
if (ref) {
|
||||
check = t.assignmentExpression(
|
||||
"=",
|
||||
t.cloneNode(ref),
|
||||
// Here `chainWithTypes` MUST NOT be cloned because it could be
|
||||
// updated when generating the memoised context of a call
|
||||
// expression
|
||||
chainWithTypes,
|
||||
);
|
||||
|
||||
node[replaceKey] = ref;
|
||||
} else {
|
||||
check = ref = chainWithTypes;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure call expressions have the proper `this`
|
||||
// `foo.bar()` has context `foo`.
|
||||
if (isCall && t.isMemberExpression(chain)) {
|
||||
if (pureGetters && isSimpleMemberExpression(chain)) {
|
||||
// To avoid a Function#call, we can instead re-grab the property from the context object.
|
||||
// `a.?b.?()` translates roughly to `_a.b != null && _a.b()`
|
||||
node.callee = chainWithTypes;
|
||||
} else {
|
||||
// Otherwise, we need to memoize the context object, and change the call into a Function#call.
|
||||
// `a.?b.?()` translates roughly to `(_b = _a.b) != null && _b.call(_a)`
|
||||
const { object } = chain;
|
||||
let context = scope.maybeGenerateMemoised(object);
|
||||
if (context) {
|
||||
chain.object = t.assignmentExpression("=", context, object);
|
||||
} else if (t.isSuper(object)) {
|
||||
context = t.thisExpression();
|
||||
} else {
|
||||
context = object;
|
||||
}
|
||||
|
||||
node.arguments.unshift(t.cloneNode(context));
|
||||
node.callee = t.memberExpression(node.callee, t.identifier("call"));
|
||||
}
|
||||
}
|
||||
let replacement = replacementPath.node;
|
||||
// Ensure (a?.b)() has proper `this`
|
||||
// The `parentIsCall` is constant within loop, we should check i === 0
|
||||
// to ensure that it is only applied to the first optional chain element
|
||||
// i.e. `?.b` in `(a?.b.c)()`
|
||||
if (i === 0 && parentIsCall) {
|
||||
// `(a?.b)()` to `(a == null ? undefined : a.b.bind(a))()`
|
||||
const object = skipTransparentExprWrappers(replacementPath.get("object"))
|
||||
.node;
|
||||
let baseRef;
|
||||
if (!pureGetters || !isSimpleMemberExpression(object)) {
|
||||
// memoize the context object when getters are not always pure
|
||||
// or the object is not a simple member expression
|
||||
// `(a?.b.c)()` to `(a == null ? undefined : (_a$b = a.b).c.bind(_a$b))()`
|
||||
baseRef = scope.maybeGenerateMemoised(object);
|
||||
if (baseRef) {
|
||||
replacement.object = t.assignmentExpression("=", baseRef, object);
|
||||
}
|
||||
}
|
||||
replacement = t.callExpression(
|
||||
t.memberExpression(replacement, t.identifier("bind")),
|
||||
[t.cloneNode(baseRef ?? object)],
|
||||
);
|
||||
}
|
||||
|
||||
if (willReplacementCastToBoolean) {
|
||||
// `if (a?.b) {}` transformed to `if (a != null && a.b) {}`
|
||||
// we don't need to return `void 0` because the returned value will
|
||||
// eveutally cast to boolean.
|
||||
const nonNullishCheck = noDocumentAll
|
||||
? ast`${t.cloneNode(check)} != null`
|
||||
: ast`
|
||||
${t.cloneNode(check)} !== null && ${t.cloneNode(ref)} !== void 0`;
|
||||
replacementPath.replaceWith(
|
||||
t.logicalExpression("&&", nonNullishCheck, replacement),
|
||||
);
|
||||
replacementPath = skipTransparentExprWrappers(
|
||||
replacementPath.get("right"),
|
||||
);
|
||||
} else {
|
||||
const nullishCheck = noDocumentAll
|
||||
? ast`${t.cloneNode(check)} == null`
|
||||
: ast`
|
||||
${t.cloneNode(check)} === null || ${t.cloneNode(ref)} === void 0`;
|
||||
|
||||
const returnValue = isDeleteOperation ? ast`true` : ast`void 0`;
|
||||
replacementPath.replaceWith(
|
||||
t.conditionalExpression(nullishCheck, returnValue, replacement),
|
||||
);
|
||||
replacementPath = skipTransparentExprWrappers(
|
||||
replacementPath.get("alternate"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20,6 +20,7 @@
|
||||
"@babel/helper-compilation-targets": "workspace:^7.13.10",
|
||||
"@babel/helper-plugin-utils": "workspace:^7.13.0",
|
||||
"@babel/helper-validator-option": "workspace:^7.12.17",
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "workspace:^7.13.8",
|
||||
"@babel/plugin-proposal-async-generator-functions": "workspace:^7.13.8",
|
||||
"@babel/plugin-proposal-class-properties": "workspace:^7.13.0",
|
||||
"@babel/plugin-proposal-dynamic-import": "workspace:^7.13.8",
|
||||
|
||||
@ -65,6 +65,7 @@ import bugfixEdgeFunctionName from "@babel/preset-modules/lib/plugins/transform-
|
||||
import bugfixTaggedTemplateCaching from "@babel/preset-modules/lib/plugins/transform-tagged-template-caching";
|
||||
import bugfixSafariBlockShadowing from "@babel/preset-modules/lib/plugins/transform-safari-block-shadowing";
|
||||
import bugfixSafariForShadowing from "@babel/preset-modules/lib/plugins/transform-safari-for-shadowing";
|
||||
import bugfixV8SpreadParametersInOptionalChaining from "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining";
|
||||
|
||||
export default {
|
||||
"bugfix/transform-async-arrows-in-class": bugfixAsyncArrowsInClass,
|
||||
@ -73,6 +74,7 @@ export default {
|
||||
"bugfix/transform-safari-block-shadowing": bugfixSafariBlockShadowing,
|
||||
"bugfix/transform-safari-for-shadowing": bugfixSafariForShadowing,
|
||||
"bugfix/transform-tagged-template-caching": bugfixTaggedTemplateCaching,
|
||||
"bugfix/transform-v8-spread-parameters-in-optional-chaining": bugfixV8SpreadParametersInOptionalChaining,
|
||||
"proposal-async-generator-functions": proposalAsyncGeneratorFunctions,
|
||||
"proposal-class-properties": proposalClassProperties,
|
||||
"proposal-dynamic-import": proposalDynamicImport,
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
fn?.();
|
||||
|
||||
fn?.(...[], 0);
|
||||
@ -0,0 +1,12 @@
|
||||
{
|
||||
"validateLogs": true,
|
||||
"presets": [
|
||||
["env", {
|
||||
"debug": true,
|
||||
"bugfixes": false,
|
||||
"targets": {
|
||||
"chrome": "89"
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
var _fn, _fn2;
|
||||
|
||||
(_fn = fn) === null || _fn === void 0 ? void 0 : _fn();
|
||||
(_fn2 = fn) === null || _fn2 === void 0 ? void 0 : _fn2(...[], 0);
|
||||
@ -0,0 +1,22 @@
|
||||
@babel/preset-env: `DEBUG` option
|
||||
|
||||
Using targets:
|
||||
{
|
||||
"chrome": "89"
|
||||
}
|
||||
|
||||
Using modules transform: auto
|
||||
|
||||
Using plugins:
|
||||
syntax-numeric-separator { "chrome":"89" }
|
||||
syntax-nullish-coalescing-operator { "chrome":"89" }
|
||||
proposal-optional-chaining { "chrome":"89" }
|
||||
syntax-json-strings { "chrome":"89" }
|
||||
syntax-optional-catch-binding { "chrome":"89" }
|
||||
syntax-async-generators { "chrome":"89" }
|
||||
syntax-object-rest-spread { "chrome":"89" }
|
||||
transform-modules-commonjs { "chrome":"89" }
|
||||
proposal-dynamic-import { "chrome":"89" }
|
||||
proposal-export-namespace-from {}
|
||||
|
||||
Using polyfills: No polyfills were added, since the `useBuiltIns` option was not set.
|
||||
@ -0,0 +1,3 @@
|
||||
fn?.();
|
||||
|
||||
fn?.(...[], 0);
|
||||
@ -0,0 +1,12 @@
|
||||
{
|
||||
"validateLogs": true,
|
||||
"presets": [
|
||||
["env", {
|
||||
"debug": true,
|
||||
"bugfixes": true,
|
||||
"targets": {
|
||||
"chrome": "89"
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
var _fn;
|
||||
|
||||
fn?.();
|
||||
(_fn = fn) === null || _fn === void 0 ? void 0 : _fn(...[], 0);
|
||||
@ -0,0 +1,23 @@
|
||||
@babel/preset-env: `DEBUG` option
|
||||
|
||||
Using targets:
|
||||
{
|
||||
"chrome": "89"
|
||||
}
|
||||
|
||||
Using modules transform: auto
|
||||
|
||||
Using plugins:
|
||||
syntax-numeric-separator { "chrome":"89" }
|
||||
syntax-nullish-coalescing-operator { "chrome":"89" }
|
||||
syntax-optional-chaining { "chrome":"89" }
|
||||
syntax-json-strings { "chrome":"89" }
|
||||
syntax-optional-catch-binding { "chrome":"89" }
|
||||
syntax-async-generators { "chrome":"89" }
|
||||
syntax-object-rest-spread { "chrome":"89" }
|
||||
bugfix/transform-v8-spread-parameters-in-optional-chaining { "chrome":"89" }
|
||||
transform-modules-commonjs { "chrome":"89" }
|
||||
proposal-dynamic-import { "chrome":"89" }
|
||||
proposal-export-namespace-from {}
|
||||
|
||||
Using polyfills: No polyfills were added, since the `useBuiltIns` option was not set.
|
||||
16
yarn.lock
16
yarn.lock
@ -973,6 +973,21 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@workspace:^7.13.8, @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@workspace:packages/babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@workspace:packages/babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining"
|
||||
dependencies:
|
||||
"@babel/core": "workspace:*"
|
||||
"@babel/helper-plugin-test-runner": "workspace:*"
|
||||
"@babel/helper-plugin-utils": "workspace:^7.13.0"
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "workspace:^7.12.1"
|
||||
"@babel/plugin-proposal-optional-chaining": "workspace:^7.13.8"
|
||||
"@babel/traverse": "workspace:*"
|
||||
peerDependencies:
|
||||
"@babel/core": ^7.13.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@babel/plugin-codemod-object-assign-to-object-spread@workspace:codemods/babel-plugin-codemod-object-assign-to-object-spread":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@babel/plugin-codemod-object-assign-to-object-spread@workspace:codemods/babel-plugin-codemod-object-assign-to-object-spread"
|
||||
@ -3068,6 +3083,7 @@ __metadata:
|
||||
"@babel/helper-plugin-test-runner": "workspace:*"
|
||||
"@babel/helper-plugin-utils": "workspace:^7.13.0"
|
||||
"@babel/helper-validator-option": "workspace:^7.12.17"
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "workspace:^7.13.8"
|
||||
"@babel/plugin-proposal-async-generator-functions": "workspace:^7.13.8"
|
||||
"@babel/plugin-proposal-class-properties": "workspace:^7.13.0"
|
||||
"@babel/plugin-proposal-dynamic-import": "workspace:^7.13.8"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user