From e143e58545630917263f3dba649b66e87f780f34 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Fri, 29 May 2020 09:26:52 +0800 Subject: [PATCH] JS: clean up babel plugins (#8435) --- src/language-js/parser-babel.js | 12 +- .../__snapshots__/jsfmt.spec.js.snap | 1529 +++++++++++++++++ tests/js/babel-plugins/async-generators.js | 6 + tests/js/babel-plugins/bigint.js | 128 ++ tests/js/babel-plugins/class-properties.js | 26 + tests/js/babel-plugins/decorators.js | 29 + tests/js/babel-plugins/do-expressions.js | 11 + tests/js/babel-plugins/dynamic-import.js | 8 + tests/js/babel-plugins/export-default-from.js | 4 + .../js/babel-plugins/export-namespace-from.js | 3 + tests/js/babel-plugins/flow.js | 3 + tests/js/babel-plugins/function-bind.js | 17 + tests/js/babel-plugins/function-sent.js | 10 + tests/js/babel-plugins/import-meta.js | 18 + tests/js/babel-plugins/jsfmt.spec.js | 4 + tests/js/babel-plugins/jsx.js | 6 + .../logical-assignment-operators.js | 7 + .../nullish-coalescing-operator.js | 3 + tests/js/babel-plugins/numeric-separator.js | 36 + tests/js/babel-plugins/object-rest-spread.js | 9 + .../babel-plugins/optional-catch-binding.js | 15 + tests/js/babel-plugins/optional-chaining.js | 76 + tests/js/babel-plugins/partial-application.js | 21 + .../babel-plugins/pipeline-operator-fsharp.js | 34 + .../pipeline-operator-minimal.js | 12 + .../babel-plugins/pipeline-operator-smart.js | 13 + .../js/babel-plugins/private-fields-in-in.js | 55 + tests/js/babel-plugins/private-methods.js | 18 + tests/js/babel-plugins/throw-expressions.js | 5 + tests/js/babel-plugins/typescript.js | 3 + tests/js/babel-plugins/v8intrinsic.js | 25 + .../js/__snapshots__/jsfmt.spec.js.snap | 46 + tests/misc/errors/js/jsfmt.spec.js | 34 +- 33 files changed, 2216 insertions(+), 10 deletions(-) create mode 100644 tests/js/babel-plugins/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/js/babel-plugins/async-generators.js create mode 100644 tests/js/babel-plugins/bigint.js create mode 100644 tests/js/babel-plugins/class-properties.js create mode 100644 tests/js/babel-plugins/decorators.js create mode 100644 tests/js/babel-plugins/do-expressions.js create mode 100644 tests/js/babel-plugins/dynamic-import.js create mode 100644 tests/js/babel-plugins/export-default-from.js create mode 100644 tests/js/babel-plugins/export-namespace-from.js create mode 100644 tests/js/babel-plugins/flow.js create mode 100644 tests/js/babel-plugins/function-bind.js create mode 100644 tests/js/babel-plugins/function-sent.js create mode 100644 tests/js/babel-plugins/import-meta.js create mode 100644 tests/js/babel-plugins/jsfmt.spec.js create mode 100644 tests/js/babel-plugins/jsx.js create mode 100644 tests/js/babel-plugins/logical-assignment-operators.js create mode 100644 tests/js/babel-plugins/nullish-coalescing-operator.js create mode 100644 tests/js/babel-plugins/numeric-separator.js create mode 100644 tests/js/babel-plugins/object-rest-spread.js create mode 100644 tests/js/babel-plugins/optional-catch-binding.js create mode 100644 tests/js/babel-plugins/optional-chaining.js create mode 100644 tests/js/babel-plugins/partial-application.js create mode 100644 tests/js/babel-plugins/pipeline-operator-fsharp.js create mode 100644 tests/js/babel-plugins/pipeline-operator-minimal.js create mode 100644 tests/js/babel-plugins/pipeline-operator-smart.js create mode 100644 tests/js/babel-plugins/private-fields-in-in.js create mode 100644 tests/js/babel-plugins/private-methods.js create mode 100644 tests/js/babel-plugins/throw-expressions.js create mode 100644 tests/js/babel-plugins/typescript.js create mode 100644 tests/js/babel-plugins/v8intrinsic.js diff --git a/src/language-js/parser-babel.js b/src/language-js/parser-babel.js index 030a61d7e..588c6cab5 100644 --- a/src/language-js/parser-babel.js +++ b/src/language-js/parser-babel.js @@ -16,22 +16,16 @@ function babelOptions({ sourceType, extraPlugins = [] }) { errorRecovery: true, createParenthesizedExpressions: true, plugins: [ + // When adding a plugin, please add a test in `tests/js/babel-plugins`, + // To remove plugins, remove it here and run `yarn test tests/js/babel-plugins` to verify + "doExpressions", - "objectRestSpread", "classProperties", "exportDefaultFrom", - "exportNamespaceFrom", - "asyncGenerators", "functionBind", "functionSent", - "dynamicImport", "numericSeparator", - "importMeta", - "optionalCatchBinding", - "optionalChaining", "classPrivateProperties", - "nullishCoalescingOperator", - "bigInt", "throwExpressions", "logicalAssignment", "classPrivateMethods", diff --git a/tests/js/babel-plugins/__snapshots__/jsfmt.spec.js.snap b/tests/js/babel-plugins/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..914731348 --- /dev/null +++ b/tests/js/babel-plugins/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,1529 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`async-generators.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-async-generator-functions + +async function* agf() { + await 1; + yield 2; +} + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-async-generator-functions + +async function* agf() { + await 1; + yield 2; +} + +================================================================================ +`; + +exports[`bigint.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://github.com/tc39/proposal-bigint + +const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER); +// ↪ 9007199254740991 + +const maxPlusOne = previousMaxSafe + 1n; +// ↪ 9007199254740992n + +const theFuture = previousMaxSafe + 2n; +// ↪ 9007199254740993n, this works now! + +const multi = previousMaxSafe * 2n; +// ↪ 18014398509481982n + +// \`–\` is not minus sign, +// SIC https://github.com/tc39/proposal-bigint#operators +// const subtr = multi – 10n; +// ↪ 18014398509481972n + +const mod = multi % 10n; +// ↪ 2n + +const bigN = 2n ** 54n; +// ↪ 18014398509481984n + +bigN * -1n +// ↪ –18014398509481984n + +0n === 0 +// ↪ false + +0n == 0 +// ↪ true + +1n < 2 +// ↪ true + +2n > 1 +// ↪ true + +2 > 2 +// ↪ false + +2n > 2 +// ↪ false + +2n >= 2 +// ↪ true + +const mixed = [4n, 6, -12n, 10, 4, 0, 0n]; +// ↪ [4n, 6, -12n, 10, 4, 0, 0n] + +mixed.sort(); +// ↪ [-12n, 0, 0n, 10, 4n, 4, 6] + +if (0n) { + console.log('Hello from the if!'); +} else { + console.log('Hello from the else!'); +} + +// ↪ "Hello from the else!" + +0n || 12n +// ↪ 12n + +0n && 12n +// ↪ 0n + +Boolean(0n) +// ↪ false + +Boolean(12n) +// ↪ true + +!12n +// ↪ false + +!0n +// ↪ true + +const view = new BigInt64Array(4); +// ↪ [0n, 0n, 0n, 0n] +view.length; +// ↪ 4 +view[0]; +// ↪ 0n +view[0] = 42n; +view[0]; +// ↪ 42n + +// Highest possible BigInt value that can be represented as a +// signed 64-bit integer. +const max = 2n ** (64n - 1n) - 1n; +view[0] = max; +view[0]; +// ↪ 9_223_372_036_854_775_807n +view[0] = max + 1n; +view[0]; +// ↪ -9_223_372_036_854_775_808n +// ^ negative because of overflow + +1n + 2 +// ↪ TypeError: Cannot mix BigInt and other types, use explicit conversions + +1n * 2 +// ↪ TypeError: Cannot mix BigInt and other types, use explicit conversions + ++1n +// ↪ TypeError: Cannot convert a BigInt value to a number + +Number(1n) +// ↪ 1 + +1n + '2' +// ↪ "12" + +'2' + 1n +// ↪ "21" + +const badPrecision = BigInt(9007199254740993); +// ↪9007199254740992n + +const goodPrecision = BigInt('9007199254740993'); +// ↪9007199254740993n + +const alsoGoodPrecision = 9007199254740993n; +// ↪9007199254740993n + +=====================================output===================================== +// https://github.com/tc39/proposal-bigint + +const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER); +// ↪ 9007199254740991 + +const maxPlusOne = previousMaxSafe + 1n; +// ↪ 9007199254740992n + +const theFuture = previousMaxSafe + 2n; +// ↪ 9007199254740993n, this works now! + +const multi = previousMaxSafe * 2n; +// ↪ 18014398509481982n + +// \`–\` is not minus sign, +// SIC https://github.com/tc39/proposal-bigint#operators +// const subtr = multi – 10n; +// ↪ 18014398509481972n + +const mod = multi % 10n; +// ↪ 2n + +const bigN = 2n ** 54n; +// ↪ 18014398509481984n + +bigN * -1n; +// ↪ –18014398509481984n + +0n === 0; +// ↪ false + +0n == 0; +// ↪ true + +1n < 2; +// ↪ true + +2n > 1; +// ↪ true + +2 > 2; +// ↪ false + +2n > 2; +// ↪ false + +2n >= 2; +// ↪ true + +const mixed = [4n, 6, -12n, 10, 4, 0, 0n]; +// ↪ [4n, 6, -12n, 10, 4, 0, 0n] + +mixed.sort(); +// ↪ [-12n, 0, 0n, 10, 4n, 4, 6] + +if (0n) { + console.log("Hello from the if!"); +} else { + console.log("Hello from the else!"); +} + +// ↪ "Hello from the else!" + +0n || 12n; +// ↪ 12n + +0n && 12n; +// ↪ 0n + +Boolean(0n); +// ↪ false + +Boolean(12n); +// ↪ true + +!12n; +// ↪ false + +!0n; +// ↪ true + +const view = new BigInt64Array(4); +// ↪ [0n, 0n, 0n, 0n] +view.length; +// ↪ 4 +view[0]; +// ↪ 0n +view[0] = 42n; +view[0]; +// ↪ 42n + +// Highest possible BigInt value that can be represented as a +// signed 64-bit integer. +const max = 2n ** (64n - 1n) - 1n; +view[0] = max; +view[0]; +// ↪ 9_223_372_036_854_775_807n +view[0] = max + 1n; +view[0]; +// ↪ -9_223_372_036_854_775_808n +// ^ negative because of overflow + +1n + 2; +// ↪ TypeError: Cannot mix BigInt and other types, use explicit conversions + +1n * 2 + + // ↪ TypeError: Cannot mix BigInt and other types, use explicit conversions + + 1n; +// ↪ TypeError: Cannot convert a BigInt value to a number + +Number(1n); +// ↪ 1 + +1n + "2"; +// ↪ "12" + +"2" + 1n; +// ↪ "21" + +const badPrecision = BigInt(9007199254740993); +// ↪9007199254740992n + +const goodPrecision = BigInt("9007199254740993"); +// ↪9007199254740993n + +const alsoGoodPrecision = 9007199254740993n; +// ↪9007199254740993n + +================================================================================ +`; + +exports[`class-properties.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-class-properties + +class Bork { + //Property initializer syntax + instanceProperty = "bork"; + boundFunction = () => { + return this.instanceProperty; + }; + + //Static class properties + static staticProperty = "babelIsCool"; + static staticFunction = function() { + return Bork.staticProperty; + }; + } + + let myBork = new Bork; + + //Property initializers are not on the prototype. + console.log(myBork.__proto__.boundFunction); // > undefined + + //Bound functions are bound to the class instance. + console.log(myBork.boundFunction.call(undefined)); // > "bork" + + //Static function exists on the class. + console.log(Bork.staticFunction()); // > "babelIsCool" + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-class-properties + +class Bork { + //Property initializer syntax + instanceProperty = "bork"; + boundFunction = () => { + return this.instanceProperty; + }; + + //Static class properties + static staticProperty = "babelIsCool"; + static staticFunction = function () { + return Bork.staticProperty; + }; +} + +let myBork = new Bork(); + +//Property initializers are not on the prototype. +console.log(myBork.__proto__.boundFunction); // > undefined + +//Bound functions are bound to the class instance. +console.log(myBork.boundFunction.call(undefined)); // > "bork" + +//Static function exists on the class. +console.log(Bork.staticFunction()); // > "babelIsCool" + +================================================================================ +`; + +exports[`decorators.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-decorators + +@annotation +class MyClass { } + +function annotation(target) { + target.annotated = true; +} + +@isTestable(true) +class MyClass { } + +function isTestable(value) { + return function decorator(target) { + target.isTestable = value; + } +} + +class C { + @enumerable(false) + method() { } +} + +function enumerable(value) { + return function (target, key, descriptor) { + descriptor.enumerable = value; + return descriptor; + } +} + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-decorators + +@annotation +class MyClass {} + +function annotation(target) { + target.annotated = true; +} + +@isTestable(true) +class MyClass {} + +function isTestable(value) { + return function decorator(target) { + target.isTestable = value; + }; +} + +class C { + @enumerable(false) + method() {} +} + +function enumerable(value) { + return function (target, key, descriptor) { + descriptor.enumerable = value; + return descriptor; + }; +} + +================================================================================ +`; + +exports[`do-expressions.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-do-expressions + +let a = do { + if(x > 10) { + 'big'; + } else { + 'small'; + } +}; +// is equivalent to: +let a = x > 10 ? 'big' : 'small'; + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-do-expressions + +let a = do { + if (x > 10) { + ("big"); + } else { + ("small"); + } +}; +// is equivalent to: +let a = x > 10 ? "big" : "small"; + +================================================================================ +`; + +exports[`dynamic-import.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-syntax-dynamic-import + +// There is no example code on babel website + +import('./prettier.mjs'); +import(prettier); +import('./prettier.mjs').then(module => console.log(module)); +import(prettier).then(module => console.log(module)); + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-syntax-dynamic-import + +// There is no example code on babel website + +import("./prettier.mjs"); +import(prettier); +import("./prettier.mjs").then((module) => console.log(module)); +import(prettier).then((module) => console.log(module)); + +================================================================================ +`; + +exports[`export-default-from.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-export-default-from + + +export v from 'mod'; + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-export-default-from + +export v from "mod"; + +================================================================================ +`; + +exports[`export-namespace-from.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-export-namespace-from + +export * as ns from 'mod'; + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-export-namespace-from + +export * as ns from "mod"; + +================================================================================ +`; + +exports[`flow.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-preset-flow + +function foo(one: any, two: number, three?): string {} + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-preset-flow + +function foo(one: any, two: number, three?): string {} + +================================================================================ +`; + +exports[`function-bind.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-function-bind + +obj::func +// is equivalent to: +func.bind(obj) + +::obj.func +// is equivalent to: +obj.func.bind(obj) + +obj::func(val) +// is equivalent to: +func.call(obj, val) + +::obj.func(val) +// is equivalent to: +obj.func.call(obj, val) + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-function-bind + +obj::func; +// is equivalent to: +func.bind(obj)::obj.func; +// is equivalent to: +obj.func.bind(obj); + +obj::func(val); +// is equivalent to: +func + .call(obj, val) + + ::obj.func(val); +// is equivalent to: +obj.func.call(obj, val); + +================================================================================ +`; + +exports[`function-sent.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-function-sent + +function* generator() { + console.log("Sent", function.sent); + console.log("Yield", yield); +} + +const iterator = generator(); +iterator.next(1); // Logs "Sent 1" +iterator.next(2); // Logs "Yield 2" + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-function-sent + +function* generator() { + console.log("Sent", function.sent); + console.log("Yield", yield); +} + +const iterator = generator(); +iterator.next(1); // Logs "Sent 1" +iterator.next(2); // Logs "Yield 2" + +================================================================================ +`; + +exports[`import-meta.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-syntax-import-meta + +// Enabled by default https://github.com/babel/babel/pull/11406 + +// from https://github.com/tc39/proposal-import-meta + +(async () => { + const response = await fetch(new URL("../hamsters.jpg", import.meta.url)); + const blob = await response.blob(); + + const size = import.meta.scriptElement.dataset.size || 300; + + const image = new Image(); + image.src = URL.createObjectURL(blob); + image.width = image.height = size; + + document.body.appendChild(image); +})(); + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-syntax-import-meta + +// Enabled by default https://github.com/babel/babel/pull/11406 + +// from https://github.com/tc39/proposal-import-meta + +(async () => { + const response = await fetch(new URL("../hamsters.jpg", import.meta.url)); + const blob = await response.blob(); + + const size = import.meta.scriptElement.dataset.size || 300; + + const image = new Image(); + image.src = URL.createObjectURL(blob); + image.width = image.height = size; + + document.body.appendChild(image); +})(); + +================================================================================ +`; + +exports[`jsx.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-syntax-jsx + +var profile =
+ +

{[user.firstName, user.lastName].join(' ')}

+
; + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-syntax-jsx + +var profile = ( +
+ +

{[user.firstName, user.lastName].join(" ")}

+
+); + +================================================================================ +`; + +exports[`logical-assignment-operators.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-logical-assignment-operators + +a ||= b; +obj.a.b ||= c; + +a &&= b; +obj.a.b &&= c; + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-logical-assignment-operators + +a ||= b; +obj.a.b ||= c; + +a &&= b; +obj.a.b &&= c; + +================================================================================ +`; + +exports[`nullish-coalescing-operator.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-nullish-coalescing-operator + +var foo = object.foo ?? "default"; + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-nullish-coalescing-operator + +var foo = object.foo ?? "default"; + +================================================================================ +`; + +exports[`numeric-separator.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-numeric-separator + +let budget = 1_000_000_000_000; + +// What is the value of \`budget\`? It's 1 trillion! +// +// Let's confirm: +console.log(budget === 10 ** 12); // true + +let nibbles = 0b1010_0001_1000_0101; + +// Is bit 7 on? It sure is! +// 0b1010_0001_1000_0101 +// ^ +// +// We can double check: +console.log(!!(nibbles & (1 << 7))); // true + +// Messages are sent as 24 bit values, but should be +// treated as 3 distinct bytes: +let message = 0xa0_b0_c0; + +// What's the value of the upper most byte? It's A0, or 160. +// We can confirm that: +let a = (message >> 16) & 0xff; +console.log(a.toString(16), a); // a0, 160 + +// What's the value of the middle byte? It's B0, or 176. +// Let's just make sure... +let b = (message >> 8) & 0xff; +console.log(b.toString(16), b); // b0, 176 + +// What's the value of the lower most byte? It's C0, or 192. +// Again, let's prove that: +let c = message & 0xff; +console.log(c.toString(16), b); // c0, 192 + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-numeric-separator + +let budget = 1_000_000_000_000; + +// What is the value of \`budget\`? It's 1 trillion! +// +// Let's confirm: +console.log(budget === 10 ** 12); // true + +let nibbles = 0b1010_0001_1000_0101; + +// Is bit 7 on? It sure is! +// 0b1010_0001_1000_0101 +// ^ +// +// We can double check: +console.log(!!(nibbles & (1 << 7))); // true + +// Messages are sent as 24 bit values, but should be +// treated as 3 distinct bytes: +let message = 0xa0_b0_c0; + +// What's the value of the upper most byte? It's A0, or 160. +// We can confirm that: +let a = (message >> 16) & 0xff; +console.log(a.toString(16), a); // a0, 160 + +// What's the value of the middle byte? It's B0, or 176. +// Let's just make sure... +let b = (message >> 8) & 0xff; +console.log(b.toString(16), b); // b0, 176 + +// What's the value of the lower most byte? It's C0, or 192. +// Again, let's prove that: +let c = message & 0xff; +console.log(c.toString(16), b); // c0, 192 + +================================================================================ +`; + +exports[`object-rest-spread.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-transform-object-rest-spread + +let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; +console.log(x); // 1 +console.log(y); // 2 +console.log(z); // { a: 3, b: 4 } + +let n = { x, y, ...z }; +console.log(n); // { x: 1, y: 2, a: 3, b: 4 } + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-transform-object-rest-spread + +let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; +console.log(x); // 1 +console.log(y); // 2 +console.log(z); // { a: 3, b: 4 } + +let n = { x, y, ...z }; +console.log(n); // { x: 1, y: 2, a: 3, b: 4 } + +================================================================================ +`; + +exports[`optional-catch-binding.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-optional-catch-binding + +try { + throw 0; +} catch { + doSomethingWhichDoesNotCareAboutTheValueThrown(); +} + +try { + throw 0; +} catch { + doSomethingWhichDoesNotCareAboutTheValueThrown(); +} finally { + doSomeCleanup(); +} + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-optional-catch-binding + +try { + throw 0; +} catch { + doSomethingWhichDoesNotCareAboutTheValueThrown(); +} + +try { + throw 0; +} catch { + doSomethingWhichDoesNotCareAboutTheValueThrown(); +} finally { + doSomeCleanup(); +} + +================================================================================ +`; + +exports[`optional-chaining.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining + +const obj = { + foo: { + bar: { + baz: 42, + }, + }, +}; + +const baz = obj?.foo?.bar?.baz; // 42 + +const safe = obj?.qux?.baz; // undefined + +// Optional chaining and normal chaining can be intermixed +obj?.foo.bar?.baz; // Only access \`foo\` if \`obj\` exists, and \`baz\` if + // \`bar\` exists + +// Example usage with bracket notation: +obj?.['foo']?.bar?.baz // 42 + +const obj = { + foo: { + bar: { + baz() { + return 42; + }, + }, + }, +}; + +const baz = obj?.foo?.bar?.baz(); // 42 + +const safe = obj?.qux?.baz(); // undefined +const safe2 = obj?.foo.bar.qux?.(); // undefined + +const willThrow = obj?.foo.bar.qux(); // Error: not a function + +// Top function can be called directly, too. +function test() { + return 42; +} +test?.(); // 42 + +exists?.(); // undefined + +const obj = { + foo: { + bar: { + baz: class { + }, + }, + }, +}; + +const baz = new obj?.foo?.bar?.baz(); // baz instance + +const safe = new obj?.qux?.baz(); // undefined +const safe2 = new obj?.foo.bar.qux?.(); // undefined + +const willThrow = new obj?.foo.bar.qux(); // Error: not a constructor + +// Top classes can be called directly, too. +class Test { +} +new Test?.(); // test instance + +new exists?.(); // undefined + +const obj = { + foo: { + bar: {} + }, +}; + +const ret = delete obj?.foo?.bar?.baz; // true + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining + +const obj = { + foo: { + bar: { + baz: 42, + }, + }, +}; + +const baz = obj?.foo?.bar?.baz; // 42 + +const safe = obj?.qux?.baz; // undefined + +// Optional chaining and normal chaining can be intermixed +obj?.foo.bar?.baz; // Only access \`foo\` if \`obj\` exists, and \`baz\` if +// \`bar\` exists + +// Example usage with bracket notation: +obj?.["foo"]?.bar?.baz; // 42 + +const obj = { + foo: { + bar: { + baz() { + return 42; + }, + }, + }, +}; + +const baz = obj?.foo?.bar?.baz(); // 42 + +const safe = obj?.qux?.baz(); // undefined +const safe2 = obj?.foo.bar.qux?.(); // undefined + +const willThrow = obj?.foo.bar.qux(); // Error: not a function + +// Top function can be called directly, too. +function test() { + return 42; +} +test?.(); // 42 + +exists?.(); // undefined + +const obj = { + foo: { + bar: { + baz: class {}, + }, + }, +}; + +const baz = new (obj?.foo?.bar?.baz)(); // baz instance + +const safe = new (obj?.qux?.baz)(); // undefined +const safe2 = new (obj?.foo.bar.qux)()?.(); // undefined + +const willThrow = new (obj?.foo.bar.qux)(); // Error: not a constructor + +// Top classes can be called directly, too. +class Test {} +new Test(); // test instance + +new exists(); // undefined + +const obj = { + foo: { + bar: {}, + }, +}; + +const ret = delete obj?.foo?.bar?.baz; // true + +================================================================================ +`; + +exports[`partial-application.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-partial-application + +function add(x, y) { return x + y; } + +const addOne = add(1, ?); // apply from the left +addOne(2); // 3 + +const addTen = add(?, 10); // apply from the right +addTen(2); // 12 + +let newScore = player.score + |> add(7, ?) + |> clamp(0, 100, ?); // shallow stack, the pipe to \`clamp\` is the same frame as the pipe to \`add\`. + +f(x, ?) // partial application from left +f(?, x) // partial application from right +f(?, x, ?) // partial application for any arg +o.f(x, ?) // partial application from left +o.f(?, x) // partial application from right +o.f(?, x, ?) // partial application for any arg +super.f(?) // partial application allowed for call on |SuperProperty| + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-partial-application + +function add(x, y) { + return x + y; +} + +const addOne = add(1, ?); // apply from the left +addOne(2); // 3 + +const addTen = add(?, 10); // apply from the right +addTen(2); // 12 + +let newScore = player.score |> add(7, ?) |> clamp(0, 100, ?); // shallow stack, the pipe to \`clamp\` is the same frame as the pipe to \`add\`. + +f(x, ?); // partial application from left +f(?, x); // partial application from right +f(?, x, ?); // partial application for any arg +o.f(x, ?); // partial application from left +o.f(?, x); // partial application from right +o.f(?, x, ?); // partial application for any arg +super.f(?); // partial application allowed for call on |SuperProperty| + +================================================================================ +`; + +exports[`pipeline-operator-fsharp.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator +// https://github.com/valtech-nyc/proposal-fsharp-pipelines + +promise + |> await + |> x => doubleSay(x, ', ') + |> capitalize + |> x => x + '!' + |> x => new User.Message(x) + |> x => stream.write(x) + |> await + |> console.log; + +const result = exclaim(capitalize(doubleSay("hello"))); +result //=> "Hello, hello!" + +const result = "hello" + |> doubleSay + |> capitalize + |> exclaim; + +result //=> "Hello, hello!" + +const person = { score: 25 }; + +const newScore = person.score + |> double + |> n => add(7, n) + |> n => boundScore(0, 100, n); + +newScore //=> 57 + +// As opposed to: +let newScore = boundScore(0, 100, add(7, double(person.score))); + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator +// https://github.com/valtech-nyc/proposal-fsharp-pipelines + +promise + |> await + |> (x) => doubleSay(x, ", ") + |> capitalize + |> (x) => x + "!" + |> (x) => new User.Message(x) + |> (x) => stream.write(x) + |> await + |> console.log; + +const result = exclaim(capitalize(doubleSay("hello"))); +result; //=> "Hello, hello!" + +const result = "hello" |> doubleSay |> capitalize |> exclaim; + +result; //=> "Hello, hello!" + +const person = { score: 25 }; + +const newScore = + person.score |> double |> (n) => add(7, n) |> (n) => boundScore(0, 100, n); + +newScore; //=> 57 + +// As opposed to: +let newScore = boundScore(0, 100, add(7, double(person.score))); + +================================================================================ +`; + +exports[`pipeline-operator-minimal.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator +// https://github.com/tc39/proposal-pipeline-operator/ + +let result = exclaim(capitalize(doubleSay("hello"))); +result //=> "Hello, hello!" + +let result = "hello" + |> doubleSay + |> capitalize + |> exclaim; + +result //=> "Hello, hello!" + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator +// https://github.com/tc39/proposal-pipeline-operator/ + +let result = exclaim(capitalize(doubleSay("hello"))); +result; //=> "Hello, hello!" + +let result = "hello" |> doubleSay |> capitalize |> exclaim; + +result; //=> "Hello, hello!" + +================================================================================ +`; + +exports[`pipeline-operator-smart.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator +// https://github.com/js-choi/proposal-smart-pipelines + +value +|> await # +|> doubleSay(#, ', ') +|> capitalize // This is a function call. +|> # + '!' +|> new User.Message(#) +|> await # +|> console.log; // This is a method call. + +// (The # token isn't final; it might instead be @ or ? or %.) + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator +// https://github.com/js-choi/proposal-smart-pipelines + +value +|> await # +|> doubleSay(#, ", ") +|> capitalize // This is a function call. + |> # + "!" + |> new User.Message(#) + |> await # + |> console.log; // This is a method call. + +// (The # token isn't final; it might instead be @ or ? or %.) + +================================================================================ +`; + +exports[`private-fields-in-in.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://github.com/tc39/proposal-private-fields-in-in + +class C { + #brand; + + static isC(obj) { + try { + obj.#brand; + return true; + } catch { + return false; + } + } +} + +class C { + #data = null; // populated later + + get #getter() { + if (!this.#data) { + throw new Error('no data yet!'); + } + return this.#data; + } + + static isC(obj) { + try { + obj.#getter; + return true; + } catch { + return false; // oops! might have gotten here because \`#getter\` threw :-( + } + } +} + +class C { + #brand; + + #method() {} + + get #getter() {} + + static isC(obj) { + return #brand in obj && #method in obj && #getter in obj; + } +} + +// Invalid https://github.com/tc39/proposal-private-fields-in-in#try-statement +// class C { +// #brand; + +// static isC(obj) { +// return try obj.#brand; +// } +// } + +=====================================output===================================== +// https://github.com/tc39/proposal-private-fields-in-in + +class C { + #brand; + + static isC(obj) { + try { + obj.#brand; + return true; + } catch { + return false; + } + } +} + +class C { + #data = null; // populated later + + get #getter() { + if (!this.#data) { + throw new Error("no data yet!"); + } + return this.#data; + } + + static isC(obj) { + try { + obj.#getter; + return true; + } catch { + return false; // oops! might have gotten here because \`#getter\` threw :-( + } + } +} + +class C { + #brand; + + #method() {} + + get #getter() {} + + static isC(obj) { + return #brand in obj && #method in obj && #getter in obj; + } +} + +// Invalid https://github.com/tc39/proposal-private-fields-in-in#try-statement +// class C { +// #brand; + +// static isC(obj) { +// return try obj.#brand; +// } +// } + +================================================================================ +`; + +exports[`private-methods.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-private-methods + +// Test for \`classPrivateProperties\` and \`classPrivateMethods\` + +class Counter extends HTMLElement { + #xValue = 0; + + get #x() { return this.#xValue; } + set #x(value) { + this.#xValue = value; + window.requestAnimationFrame( + this.#render.bind(this)); + } + + #clicked() { + this.#x++; + } +} + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-private-methods + +// Test for \`classPrivateProperties\` and \`classPrivateMethods\` + +class Counter extends HTMLElement { + #xValue = 0; + + get #x() { + return this.#xValue; + } + set #x(value) { + this.#xValue = value; + window.requestAnimationFrame(this.#render.bind(this)); + } + + #clicked() { + this.#x++; + } +} + +================================================================================ +`; + +exports[`throw-expressions.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-throw-expressions + +function test(param = throw new Error('required!')) { + const test = param === true || throw new Error('Falsy!'); +} + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-plugin-proposal-throw-expressions + +function test(param = throw new Error("required!")) { + const test = param === true || throw new Error("Falsy!"); +} + +================================================================================ +`; + +exports[`typescript.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://babeljs.io/docs/en/babel-preset-typescript + +const x: number = 0; + +=====================================output===================================== +// https://babeljs.io/docs/en/babel-preset-typescript + +const x: number = 0; + +================================================================================ +`; + +exports[`v8intrinsic.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "babel-flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +// https://github.com/babel/babel/pull/10148 + +%DebugPrint(foo); + + +// Invalid code https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/in-bind-expression/options.json +// ::%DebugPrint(null) + +// Invalid code https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/in-member-expression/options.json +// a.%DebugPrint(); + +// Invalid code https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/not-in-call-expression/options.json +// const i = %DebugPrint; +// i(foo); + +// https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/not-in-call-expression/options.json +// %DebugPrint?.(null) + +new %DebugPrint(null); + +function *foo() { + yield %StringParseInt("42", 10) +} + +foo%bar() + +=====================================output===================================== +// https://github.com/babel/babel/pull/10148 + +%DebugPrint(foo); + +// Invalid code https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/in-bind-expression/options.json +// ::%DebugPrint(null) + +// Invalid code https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/in-member-expression/options.json +// a.%DebugPrint(); + +// Invalid code https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/not-in-call-expression/options.json +// const i = %DebugPrint; +// i(foo); + +// https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/not-in-call-expression/options.json +// %DebugPrint?.(null) + +new %DebugPrint(null); + +function* foo() { + yield %StringParseInt("42", 10); +} + +foo % bar(); + +================================================================================ +`; diff --git a/tests/js/babel-plugins/async-generators.js b/tests/js/babel-plugins/async-generators.js new file mode 100644 index 000000000..d3fc2cdac --- /dev/null +++ b/tests/js/babel-plugins/async-generators.js @@ -0,0 +1,6 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-async-generator-functions + +async function* agf() { + await 1; + yield 2; +} diff --git a/tests/js/babel-plugins/bigint.js b/tests/js/babel-plugins/bigint.js new file mode 100644 index 000000000..4f410f310 --- /dev/null +++ b/tests/js/babel-plugins/bigint.js @@ -0,0 +1,128 @@ +// https://github.com/tc39/proposal-bigint + +const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER); +// ↪ 9007199254740991 + +const maxPlusOne = previousMaxSafe + 1n; +// ↪ 9007199254740992n + +const theFuture = previousMaxSafe + 2n; +// ↪ 9007199254740993n, this works now! + +const multi = previousMaxSafe * 2n; +// ↪ 18014398509481982n + +// `–` is not minus sign, +// SIC https://github.com/tc39/proposal-bigint#operators +// const subtr = multi – 10n; +// ↪ 18014398509481972n + +const mod = multi % 10n; +// ↪ 2n + +const bigN = 2n ** 54n; +// ↪ 18014398509481984n + +bigN * -1n +// ↪ –18014398509481984n + +0n === 0 +// ↪ false + +0n == 0 +// ↪ true + +1n < 2 +// ↪ true + +2n > 1 +// ↪ true + +2 > 2 +// ↪ false + +2n > 2 +// ↪ false + +2n >= 2 +// ↪ true + +const mixed = [4n, 6, -12n, 10, 4, 0, 0n]; +// ↪ [4n, 6, -12n, 10, 4, 0, 0n] + +mixed.sort(); +// ↪ [-12n, 0, 0n, 10, 4n, 4, 6] + +if (0n) { + console.log('Hello from the if!'); +} else { + console.log('Hello from the else!'); +} + +// ↪ "Hello from the else!" + +0n || 12n +// ↪ 12n + +0n && 12n +// ↪ 0n + +Boolean(0n) +// ↪ false + +Boolean(12n) +// ↪ true + +!12n +// ↪ false + +!0n +// ↪ true + +const view = new BigInt64Array(4); +// ↪ [0n, 0n, 0n, 0n] +view.length; +// ↪ 4 +view[0]; +// ↪ 0n +view[0] = 42n; +view[0]; +// ↪ 42n + +// Highest possible BigInt value that can be represented as a +// signed 64-bit integer. +const max = 2n ** (64n - 1n) - 1n; +view[0] = max; +view[0]; +// ↪ 9_223_372_036_854_775_807n +view[0] = max + 1n; +view[0]; +// ↪ -9_223_372_036_854_775_808n +// ^ negative because of overflow + +1n + 2 +// ↪ TypeError: Cannot mix BigInt and other types, use explicit conversions + +1n * 2 +// ↪ TypeError: Cannot mix BigInt and other types, use explicit conversions + ++1n +// ↪ TypeError: Cannot convert a BigInt value to a number + +Number(1n) +// ↪ 1 + +1n + '2' +// ↪ "12" + +'2' + 1n +// ↪ "21" + +const badPrecision = BigInt(9007199254740993); +// ↪9007199254740992n + +const goodPrecision = BigInt('9007199254740993'); +// ↪9007199254740993n + +const alsoGoodPrecision = 9007199254740993n; +// ↪9007199254740993n diff --git a/tests/js/babel-plugins/class-properties.js b/tests/js/babel-plugins/class-properties.js new file mode 100644 index 000000000..916c975a6 --- /dev/null +++ b/tests/js/babel-plugins/class-properties.js @@ -0,0 +1,26 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-class-properties + +class Bork { + //Property initializer syntax + instanceProperty = "bork"; + boundFunction = () => { + return this.instanceProperty; + }; + + //Static class properties + static staticProperty = "babelIsCool"; + static staticFunction = function() { + return Bork.staticProperty; + }; + } + + let myBork = new Bork; + + //Property initializers are not on the prototype. + console.log(myBork.__proto__.boundFunction); // > undefined + + //Bound functions are bound to the class instance. + console.log(myBork.boundFunction.call(undefined)); // > "bork" + + //Static function exists on the class. + console.log(Bork.staticFunction()); // > "babelIsCool" diff --git a/tests/js/babel-plugins/decorators.js b/tests/js/babel-plugins/decorators.js new file mode 100644 index 000000000..03744072c --- /dev/null +++ b/tests/js/babel-plugins/decorators.js @@ -0,0 +1,29 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-decorators + +@annotation +class MyClass { } + +function annotation(target) { + target.annotated = true; +} + +@isTestable(true) +class MyClass { } + +function isTestable(value) { + return function decorator(target) { + target.isTestable = value; + } +} + +class C { + @enumerable(false) + method() { } +} + +function enumerable(value) { + return function (target, key, descriptor) { + descriptor.enumerable = value; + return descriptor; + } +} diff --git a/tests/js/babel-plugins/do-expressions.js b/tests/js/babel-plugins/do-expressions.js new file mode 100644 index 000000000..5a139893d --- /dev/null +++ b/tests/js/babel-plugins/do-expressions.js @@ -0,0 +1,11 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-do-expressions + +let a = do { + if(x > 10) { + 'big'; + } else { + 'small'; + } +}; +// is equivalent to: +let a = x > 10 ? 'big' : 'small'; diff --git a/tests/js/babel-plugins/dynamic-import.js b/tests/js/babel-plugins/dynamic-import.js new file mode 100644 index 000000000..bd361096d --- /dev/null +++ b/tests/js/babel-plugins/dynamic-import.js @@ -0,0 +1,8 @@ +// https://babeljs.io/docs/en/babel-plugin-syntax-dynamic-import + +// There is no example code on babel website + +import('./prettier.mjs'); +import(prettier); +import('./prettier.mjs').then(module => console.log(module)); +import(prettier).then(module => console.log(module)); diff --git a/tests/js/babel-plugins/export-default-from.js b/tests/js/babel-plugins/export-default-from.js new file mode 100644 index 000000000..67a17a8e4 --- /dev/null +++ b/tests/js/babel-plugins/export-default-from.js @@ -0,0 +1,4 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-export-default-from + + +export v from 'mod'; diff --git a/tests/js/babel-plugins/export-namespace-from.js b/tests/js/babel-plugins/export-namespace-from.js new file mode 100644 index 000000000..9c06dcd5b --- /dev/null +++ b/tests/js/babel-plugins/export-namespace-from.js @@ -0,0 +1,3 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-export-namespace-from + +export * as ns from 'mod'; diff --git a/tests/js/babel-plugins/flow.js b/tests/js/babel-plugins/flow.js new file mode 100644 index 000000000..44c0e3305 --- /dev/null +++ b/tests/js/babel-plugins/flow.js @@ -0,0 +1,3 @@ +// https://babeljs.io/docs/en/babel-preset-flow + +function foo(one: any, two: number, three?): string {} diff --git a/tests/js/babel-plugins/function-bind.js b/tests/js/babel-plugins/function-bind.js new file mode 100644 index 000000000..41075578e --- /dev/null +++ b/tests/js/babel-plugins/function-bind.js @@ -0,0 +1,17 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-function-bind + +obj::func +// is equivalent to: +func.bind(obj) + +::obj.func +// is equivalent to: +obj.func.bind(obj) + +obj::func(val) +// is equivalent to: +func.call(obj, val) + +::obj.func(val) +// is equivalent to: +obj.func.call(obj, val) diff --git a/tests/js/babel-plugins/function-sent.js b/tests/js/babel-plugins/function-sent.js new file mode 100644 index 000000000..f7fc8e3d0 --- /dev/null +++ b/tests/js/babel-plugins/function-sent.js @@ -0,0 +1,10 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-function-sent + +function* generator() { + console.log("Sent", function.sent); + console.log("Yield", yield); +} + +const iterator = generator(); +iterator.next(1); // Logs "Sent 1" +iterator.next(2); // Logs "Yield 2" diff --git a/tests/js/babel-plugins/import-meta.js b/tests/js/babel-plugins/import-meta.js new file mode 100644 index 000000000..3ef8f5a96 --- /dev/null +++ b/tests/js/babel-plugins/import-meta.js @@ -0,0 +1,18 @@ +// https://babeljs.io/docs/en/babel-plugin-syntax-import-meta + +// Enabled by default https://github.com/babel/babel/pull/11406 + +// from https://github.com/tc39/proposal-import-meta + +(async () => { + const response = await fetch(new URL("../hamsters.jpg", import.meta.url)); + const blob = await response.blob(); + + const size = import.meta.scriptElement.dataset.size || 300; + + const image = new Image(); + image.src = URL.createObjectURL(blob); + image.width = image.height = size; + + document.body.appendChild(image); +})(); diff --git a/tests/js/babel-plugins/jsfmt.spec.js b/tests/js/babel-plugins/jsfmt.spec.js new file mode 100644 index 000000000..0fa97049b --- /dev/null +++ b/tests/js/babel-plugins/jsfmt.spec.js @@ -0,0 +1,4 @@ +// Only testing babel parsing +// Do not add extra parsers here + +run_spec(__dirname, ["babel", "babel-ts", "babel-flow"]); diff --git a/tests/js/babel-plugins/jsx.js b/tests/js/babel-plugins/jsx.js new file mode 100644 index 000000000..f5a6d9ef0 --- /dev/null +++ b/tests/js/babel-plugins/jsx.js @@ -0,0 +1,6 @@ +// https://babeljs.io/docs/en/babel-plugin-syntax-jsx + +var profile =
+ +

{[user.firstName, user.lastName].join(' ')}

+
; diff --git a/tests/js/babel-plugins/logical-assignment-operators.js b/tests/js/babel-plugins/logical-assignment-operators.js new file mode 100644 index 000000000..43e332171 --- /dev/null +++ b/tests/js/babel-plugins/logical-assignment-operators.js @@ -0,0 +1,7 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-logical-assignment-operators + +a ||= b; +obj.a.b ||= c; + +a &&= b; +obj.a.b &&= c; diff --git a/tests/js/babel-plugins/nullish-coalescing-operator.js b/tests/js/babel-plugins/nullish-coalescing-operator.js new file mode 100644 index 000000000..b786659b2 --- /dev/null +++ b/tests/js/babel-plugins/nullish-coalescing-operator.js @@ -0,0 +1,3 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-nullish-coalescing-operator + +var foo = object.foo ?? "default"; diff --git a/tests/js/babel-plugins/numeric-separator.js b/tests/js/babel-plugins/numeric-separator.js new file mode 100644 index 000000000..3ea80118c --- /dev/null +++ b/tests/js/babel-plugins/numeric-separator.js @@ -0,0 +1,36 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-numeric-separator + +let budget = 1_000_000_000_000; + +// What is the value of `budget`? It's 1 trillion! +// +// Let's confirm: +console.log(budget === 10 ** 12); // true + +let nibbles = 0b1010_0001_1000_0101; + +// Is bit 7 on? It sure is! +// 0b1010_0001_1000_0101 +// ^ +// +// We can double check: +console.log(!!(nibbles & (1 << 7))); // true + +// Messages are sent as 24 bit values, but should be +// treated as 3 distinct bytes: +let message = 0xa0_b0_c0; + +// What's the value of the upper most byte? It's A0, or 160. +// We can confirm that: +let a = (message >> 16) & 0xff; +console.log(a.toString(16), a); // a0, 160 + +// What's the value of the middle byte? It's B0, or 176. +// Let's just make sure... +let b = (message >> 8) & 0xff; +console.log(b.toString(16), b); // b0, 176 + +// What's the value of the lower most byte? It's C0, or 192. +// Again, let's prove that: +let c = message & 0xff; +console.log(c.toString(16), b); // c0, 192 diff --git a/tests/js/babel-plugins/object-rest-spread.js b/tests/js/babel-plugins/object-rest-spread.js new file mode 100644 index 000000000..6e9ffbff0 --- /dev/null +++ b/tests/js/babel-plugins/object-rest-spread.js @@ -0,0 +1,9 @@ +// https://babeljs.io/docs/en/babel-plugin-transform-object-rest-spread + +let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; +console.log(x); // 1 +console.log(y); // 2 +console.log(z); // { a: 3, b: 4 } + +let n = { x, y, ...z }; +console.log(n); // { x: 1, y: 2, a: 3, b: 4 } diff --git a/tests/js/babel-plugins/optional-catch-binding.js b/tests/js/babel-plugins/optional-catch-binding.js new file mode 100644 index 000000000..1327104c7 --- /dev/null +++ b/tests/js/babel-plugins/optional-catch-binding.js @@ -0,0 +1,15 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-optional-catch-binding + +try { + throw 0; +} catch { + doSomethingWhichDoesNotCareAboutTheValueThrown(); +} + +try { + throw 0; +} catch { + doSomethingWhichDoesNotCareAboutTheValueThrown(); +} finally { + doSomeCleanup(); +} diff --git a/tests/js/babel-plugins/optional-chaining.js b/tests/js/babel-plugins/optional-chaining.js new file mode 100644 index 000000000..c80d98d69 --- /dev/null +++ b/tests/js/babel-plugins/optional-chaining.js @@ -0,0 +1,76 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining + +const obj = { + foo: { + bar: { + baz: 42, + }, + }, +}; + +const baz = obj?.foo?.bar?.baz; // 42 + +const safe = obj?.qux?.baz; // undefined + +// Optional chaining and normal chaining can be intermixed +obj?.foo.bar?.baz; // Only access `foo` if `obj` exists, and `baz` if + // `bar` exists + +// Example usage with bracket notation: +obj?.['foo']?.bar?.baz // 42 + +const obj = { + foo: { + bar: { + baz() { + return 42; + }, + }, + }, +}; + +const baz = obj?.foo?.bar?.baz(); // 42 + +const safe = obj?.qux?.baz(); // undefined +const safe2 = obj?.foo.bar.qux?.(); // undefined + +const willThrow = obj?.foo.bar.qux(); // Error: not a function + +// Top function can be called directly, too. +function test() { + return 42; +} +test?.(); // 42 + +exists?.(); // undefined + +const obj = { + foo: { + bar: { + baz: class { + }, + }, + }, +}; + +const baz = new obj?.foo?.bar?.baz(); // baz instance + +const safe = new obj?.qux?.baz(); // undefined +const safe2 = new obj?.foo.bar.qux?.(); // undefined + +const willThrow = new obj?.foo.bar.qux(); // Error: not a constructor + +// Top classes can be called directly, too. +class Test { +} +new Test?.(); // test instance + +new exists?.(); // undefined + +const obj = { + foo: { + bar: {} + }, +}; + +const ret = delete obj?.foo?.bar?.baz; // true diff --git a/tests/js/babel-plugins/partial-application.js b/tests/js/babel-plugins/partial-application.js new file mode 100644 index 000000000..6aaee6003 --- /dev/null +++ b/tests/js/babel-plugins/partial-application.js @@ -0,0 +1,21 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-partial-application + +function add(x, y) { return x + y; } + +const addOne = add(1, ?); // apply from the left +addOne(2); // 3 + +const addTen = add(?, 10); // apply from the right +addTen(2); // 12 + +let newScore = player.score + |> add(7, ?) + |> clamp(0, 100, ?); // shallow stack, the pipe to `clamp` is the same frame as the pipe to `add`. + +f(x, ?) // partial application from left +f(?, x) // partial application from right +f(?, x, ?) // partial application for any arg +o.f(x, ?) // partial application from left +o.f(?, x) // partial application from right +o.f(?, x, ?) // partial application for any arg +super.f(?) // partial application allowed for call on |SuperProperty| diff --git a/tests/js/babel-plugins/pipeline-operator-fsharp.js b/tests/js/babel-plugins/pipeline-operator-fsharp.js new file mode 100644 index 000000000..66a798372 --- /dev/null +++ b/tests/js/babel-plugins/pipeline-operator-fsharp.js @@ -0,0 +1,34 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator +// https://github.com/valtech-nyc/proposal-fsharp-pipelines + +promise + |> await + |> x => doubleSay(x, ', ') + |> capitalize + |> x => x + '!' + |> x => new User.Message(x) + |> x => stream.write(x) + |> await + |> console.log; + +const result = exclaim(capitalize(doubleSay("hello"))); +result //=> "Hello, hello!" + +const result = "hello" + |> doubleSay + |> capitalize + |> exclaim; + +result //=> "Hello, hello!" + +const person = { score: 25 }; + +const newScore = person.score + |> double + |> n => add(7, n) + |> n => boundScore(0, 100, n); + +newScore //=> 57 + +// As opposed to: +let newScore = boundScore(0, 100, add(7, double(person.score))); diff --git a/tests/js/babel-plugins/pipeline-operator-minimal.js b/tests/js/babel-plugins/pipeline-operator-minimal.js new file mode 100644 index 000000000..e15c9d95c --- /dev/null +++ b/tests/js/babel-plugins/pipeline-operator-minimal.js @@ -0,0 +1,12 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator +// https://github.com/tc39/proposal-pipeline-operator/ + +let result = exclaim(capitalize(doubleSay("hello"))); +result //=> "Hello, hello!" + +let result = "hello" + |> doubleSay + |> capitalize + |> exclaim; + +result //=> "Hello, hello!" diff --git a/tests/js/babel-plugins/pipeline-operator-smart.js b/tests/js/babel-plugins/pipeline-operator-smart.js new file mode 100644 index 000000000..f28849169 --- /dev/null +++ b/tests/js/babel-plugins/pipeline-operator-smart.js @@ -0,0 +1,13 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator +// https://github.com/js-choi/proposal-smart-pipelines + +value +|> await # +|> doubleSay(#, ', ') +|> capitalize // This is a function call. +|> # + '!' +|> new User.Message(#) +|> await # +|> console.log; // This is a method call. + +// (The # token isn't final; it might instead be @ or ? or %.) diff --git a/tests/js/babel-plugins/private-fields-in-in.js b/tests/js/babel-plugins/private-fields-in-in.js new file mode 100644 index 000000000..aba8e5df9 --- /dev/null +++ b/tests/js/babel-plugins/private-fields-in-in.js @@ -0,0 +1,55 @@ +// https://github.com/tc39/proposal-private-fields-in-in + +class C { + #brand; + + static isC(obj) { + try { + obj.#brand; + return true; + } catch { + return false; + } + } +} + +class C { + #data = null; // populated later + + get #getter() { + if (!this.#data) { + throw new Error('no data yet!'); + } + return this.#data; + } + + static isC(obj) { + try { + obj.#getter; + return true; + } catch { + return false; // oops! might have gotten here because `#getter` threw :-( + } + } +} + +class C { + #brand; + + #method() {} + + get #getter() {} + + static isC(obj) { + return #brand in obj && #method in obj && #getter in obj; + } +} + +// Invalid https://github.com/tc39/proposal-private-fields-in-in#try-statement +// class C { +// #brand; + +// static isC(obj) { +// return try obj.#brand; +// } +// } diff --git a/tests/js/babel-plugins/private-methods.js b/tests/js/babel-plugins/private-methods.js new file mode 100644 index 000000000..e1486a9fb --- /dev/null +++ b/tests/js/babel-plugins/private-methods.js @@ -0,0 +1,18 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-private-methods + +// Test for `classPrivateProperties` and `classPrivateMethods` + +class Counter extends HTMLElement { + #xValue = 0; + + get #x() { return this.#xValue; } + set #x(value) { + this.#xValue = value; + window.requestAnimationFrame( + this.#render.bind(this)); + } + + #clicked() { + this.#x++; + } +} diff --git a/tests/js/babel-plugins/throw-expressions.js b/tests/js/babel-plugins/throw-expressions.js new file mode 100644 index 000000000..3cded2284 --- /dev/null +++ b/tests/js/babel-plugins/throw-expressions.js @@ -0,0 +1,5 @@ +// https://babeljs.io/docs/en/babel-plugin-proposal-throw-expressions + +function test(param = throw new Error('required!')) { + const test = param === true || throw new Error('Falsy!'); +} diff --git a/tests/js/babel-plugins/typescript.js b/tests/js/babel-plugins/typescript.js new file mode 100644 index 000000000..7fe7124f3 --- /dev/null +++ b/tests/js/babel-plugins/typescript.js @@ -0,0 +1,3 @@ +// https://babeljs.io/docs/en/babel-preset-typescript + +const x: number = 0; diff --git a/tests/js/babel-plugins/v8intrinsic.js b/tests/js/babel-plugins/v8intrinsic.js new file mode 100644 index 000000000..600086f28 --- /dev/null +++ b/tests/js/babel-plugins/v8intrinsic.js @@ -0,0 +1,25 @@ +// https://github.com/babel/babel/pull/10148 + +%DebugPrint(foo); + + +// Invalid code https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/in-bind-expression/options.json +// ::%DebugPrint(null) + +// Invalid code https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/in-member-expression/options.json +// a.%DebugPrint(); + +// Invalid code https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/not-in-call-expression/options.json +// const i = %DebugPrint; +// i(foo); + +// https://github.com/JLHwung/babel/blob/c1a3cbfd65e08b7013fd6f8c62add8cb10b4b169/packages/babel-parser/test/fixtures/v8intrinsic/_errors/not-in-call-expression/options.json +// %DebugPrint?.(null) + +new %DebugPrint(null); + +function *foo() { + yield %StringParseInt("42", 10) +} + +foo%bar() diff --git a/tests/misc/errors/js/__snapshots__/jsfmt.spec.js.snap b/tests/misc/errors/js/__snapshots__/jsfmt.spec.js.snap index 8b11758f0..463aec104 100644 --- a/tests/misc/errors/js/__snapshots__/jsfmt.spec.js.snap +++ b/tests/misc/errors/js/__snapshots__/jsfmt.spec.js.snap @@ -22,6 +22,52 @@ exports[`partial-template-strings.js error test 1`] = ` 9 | Diagnostics.name_not_found(\\"foo\\"); // \\"'foo' not found.\\"" `; +exports[`snippet: #0 error test 1`] = ` +"Binding should be performed on object property. (1:3) +> 1 | ::%DebugPrint(null) + | ^" +`; + +exports[`snippet: #1 error test 1`] = ` +"Unexpected token (1:3) +> 1 | a.%DebugPrint(); + | ^" +`; + +exports[`snippet: #2 error test 1`] = ` +"Unexpected token (1:11) +> 1 | const i = %DebugPrint; + | ^" +`; + +exports[`snippet: #3 error test 1`] = ` +"Unexpected token (1:7) +> 1 | f(x + ?) + | ^" +`; + +exports[`snippet: #4 error test 1`] = ` +"Unexpected token (1:5) +> 1 | x + ? + | ^" +`; + +exports[`snippet: #5 error test 1`] = ` +"Unexpected token (1:1) +> 1 | ?.f() + | ^" +`; + +exports[`snippet: #6 error test 1`] = ` +"Unexpected token (5:12) + 3 | + 4 | static isC(obj) { +> 5 | return try obj.#brand; + | ^ + 6 | } + 7 | }" +`; + exports[`static-import-source-should-not-has-extra-token.js error test 1`] = ` "Unexpected token (4:16) 2 | // be parenthesized, that accidentally break #8016 diff --git a/tests/misc/errors/js/jsfmt.spec.js b/tests/misc/errors/js/jsfmt.spec.js index 8382eddeb..bba1febe0 100644 --- a/tests/misc/errors/js/jsfmt.spec.js +++ b/tests/misc/errors/js/jsfmt.spec.js @@ -1 +1,33 @@ -run_spec(__dirname, ["babel"]); +const { outdent } = require("outdent"); + +run_spec( + { + dirname: __dirname, + snippets: [ + // v8intrinsic + "::%DebugPrint(null)", + "a.%DebugPrint();", + "const i = %DebugPrint;", + + // partialApplication + // https://babeljs.io/docs/en/babel-plugin-proposal-partial-application#invalid-usage + "f(x + ?)", // `?` not in top-level Arguments of call + "x + ?", // `?` not in top-level Arguments of call + "?.f()", // `?` not in top-level Arguments of call + // "new f(?)", // `?` not supported in `new` + // "super(?)", // `?` not supported in |SuperCall| + + // https://github.com/tc39/proposal-private-fields-in-in#try-statement + outdent` + class C { + #brand; + + static isC(obj) { + return try obj.#brand; + } + } + `, + ], + }, + ["babel"] +);