diff --git a/.changeset/dry-numbers-end.md b/.changeset/dry-numbers-end.md new file mode 100644 index 000000000000..11163d998fb3 --- /dev/null +++ b/.changeset/dry-numbers-end.md @@ -0,0 +1,6 @@ +--- +swc_core: patch +swc_ecma_compat_es2015: patch +--- + +fix(es/compat): Fix generator transform for compound assignments, for-in, and labeled break diff --git a/crates/swc/tests/exec/issues-11xxx/11046/.swcrc b/crates/swc/tests/exec/issues-11xxx/11046/.swcrc new file mode 100644 index 000000000000..8c891e9f8253 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11046/.swcrc @@ -0,0 +1,10 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "externalHelpers": false, + "target": "es5" + }, + "isModule": true +} diff --git a/crates/swc/tests/exec/issues-11xxx/11046/exec.js b/crates/swc/tests/exec/issues-11xxx/11046/exec.js new file mode 100644 index 000000000000..86b50f629430 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11046/exec.js @@ -0,0 +1,37 @@ +function* traverse(obj) { + for (let key of Object.keys(obj)) { + if (typeof obj[key] === "object") yield* traverse(obj[key]); + else yield obj[key]; + } +} + +const obj = { + data: [1, 2, 3], + nested: { + fieldName: "Nested", + fieldData: "Data", + }, + get dataGenerator() { + return traverse(this); + }, +}; + +function func1() { + const generator = (function () { + return obj.dataGenerator; + })(); + + const res = []; + for (let value of generator) { + res.push(value); + } + + return res; +} + +function main() { + const res = func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/exec/issues-11xxx/11047/.swcrc b/crates/swc/tests/exec/issues-11xxx/11047/.swcrc new file mode 100644 index 000000000000..8c891e9f8253 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11047/.swcrc @@ -0,0 +1,10 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "externalHelpers": false, + "target": "es5" + }, + "isModule": true +} diff --git a/crates/swc/tests/exec/issues-11xxx/11047/exec.js b/crates/swc/tests/exec/issues-11xxx/11047/exec.js new file mode 100644 index 000000000000..35ce21d6ccb2 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11047/exec.js @@ -0,0 +1,22 @@ +const timeoutPromise = (timeout) => new Promise(resolve => setTimeout(resolve, timeout)); + +const processArray = async (array) => { + const [first, , third = 'default', ...rest] = array; + + if (!first || !third) { + return; + } + await timeoutPromise(10); + return {first, third, rest}; +} + +const func1 = async () => { + return await processArray(['value1', 'value2', , 'value4', 'value5', 'value6', 'value7']); +} + +const main = async () => { + const res = await func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/exec/issues-11xxx/11048/.swcrc b/crates/swc/tests/exec/issues-11xxx/11048/.swcrc new file mode 100644 index 000000000000..8c891e9f8253 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11048/.swcrc @@ -0,0 +1,10 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "externalHelpers": false, + "target": "es5" + }, + "isModule": true +} diff --git a/crates/swc/tests/exec/issues-11xxx/11048/exec.js b/crates/swc/tests/exec/issues-11xxx/11048/exec.js new file mode 100644 index 000000000000..0ff17fdf6a77 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11048/exec.js @@ -0,0 +1,29 @@ +async function fetchResource() { + let data = ["John", , 25, "Developer", "California"]; + await new Promise(resolve => setTimeout(resolve, 500)); + return data; +} + +function func1() { + return new Promise(async (resolve, reject) => { + let resource = await fetchResource(); + const [firstName, middleName = "N/A", age, ...rest] = resource; + + const result = (function() { + if(firstName) { + return {firstName, middleName, age, occupation: rest[0], location: rest[1]}; + } else { + return {error: "Failed to fetch resource"}; + } + })(); + + resolve(result); + }); +} + +async function main() { + const res = await func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/exec/issues-11xxx/11049/.swcrc b/crates/swc/tests/exec/issues-11xxx/11049/.swcrc new file mode 100644 index 000000000000..8c891e9f8253 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11049/.swcrc @@ -0,0 +1,10 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "externalHelpers": false, + "target": "es5" + }, + "isModule": true +} diff --git a/crates/swc/tests/exec/issues-11xxx/11049/exec.js b/crates/swc/tests/exec/issues-11xxx/11049/exec.js new file mode 100644 index 000000000000..a090292bc13f --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11049/exec.js @@ -0,0 +1,33 @@ +class MyClass { + constructor(data) { + if (!(data instanceof Array)) + throw new Error('Invalid instance'); + this.data = data; + } +} + +function fetchPromise(idx) { + return new Promise((resolve, reject) => { + setTimeout(() => resolve(idx), 10) + }) +} + +async function func1() { + const arr = [1, [2, 3], , 4]; + let [a, [ , b], ,d] = arr; + + const instance = new MyClass([a, b, d]) + + a = await fetchPromise(instance.data[0]) || fetchPromise(0); + b = await fetchPromise(instance.data[1]) || fetchPromise(0); + d = await fetchPromise(instance.data[2]) || fetchPromise(0); + + return [a, b, d]; +} + +function main() { + const res = func1(); + res.then(value => console.log(value)); +} + +main(); diff --git a/crates/swc/tests/exec/issues-11xxx/11050/.swcrc b/crates/swc/tests/exec/issues-11xxx/11050/.swcrc new file mode 100644 index 000000000000..8c891e9f8253 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11050/.swcrc @@ -0,0 +1,10 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "externalHelpers": false, + "target": "es5" + }, + "isModule": true +} diff --git a/crates/swc/tests/exec/issues-11xxx/11050/exec.js b/crates/swc/tests/exec/issues-11xxx/11050/exec.js new file mode 100644 index 000000000000..f2e424b0c44b --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11050/exec.js @@ -0,0 +1,47 @@ +async function complexCompute(num) { + return new Promise(resolve => { + setTimeout(() => { + resolve(num * 2); + }, 2000); + }); +} + +async function func1() { + let result; + let val = 1; // initial assignment + val = val || await complexCompute(5); + + outer_block: { + if (!val) { + result = "No Value"; + break outer_block; + } + + if (val < 10) { + switch(val) { + case 1: + result = "One"; + break outer_block; + case 2: + let temp = await complexCompute(2); + if (temp > val) { + result = "Two Modified"; + break outer_block; + } + result = "Two"; + break outer_block; + } + } + + result = "Out of scope"; + } + + return result; +} + +async function main() { + let res = await func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/exec/issues-11xxx/11052/.swcrc b/crates/swc/tests/exec/issues-11xxx/11052/.swcrc new file mode 100644 index 000000000000..8c891e9f8253 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11052/.swcrc @@ -0,0 +1,10 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "externalHelpers": false, + "target": "es5" + }, + "isModule": true +} diff --git a/crates/swc/tests/exec/issues-11xxx/11052/exec.js b/crates/swc/tests/exec/issues-11xxx/11052/exec.js new file mode 100644 index 000000000000..91623a4e3d16 --- /dev/null +++ b/crates/swc/tests/exec/issues-11xxx/11052/exec.js @@ -0,0 +1,41 @@ +class MyClass { + constructor() { + this._value = null; + } + + async fetchData() { + return new Promise(resolve => { + setTimeout(() => { + resolve('Data fetched'); + }, 10); + }); + } + + async fetchMoreData() { + return new Promise(resolve => { + setTimeout(() => { + resolve('More data fetched'); + }, 10); + }); + } + async initializeData() { + this._value = await this.fetchData(); + this._value += ' ' + await this.fetchMoreData(); + } + get value() { + return this._value; + } + set value(newValue) { + this._value = newValue; + } +} +async function func1() { + let myClass = new MyClass(); + await myClass.initializeData(); + return myClass.value; +} +async function main() { + let res = await func1(); + console.log(res); +} +main(); diff --git a/crates/swc/tests/exec/issues-5xxx/5433/.swcrc b/crates/swc/tests/exec/issues-5xxx/5433/.swcrc new file mode 100644 index 000000000000..15388732732c --- /dev/null +++ b/crates/swc/tests/exec/issues-5xxx/5433/.swcrc @@ -0,0 +1,10 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "externalHelpers": false, + "target": "es5" + }, + "isModule": true +} \ No newline at end of file diff --git a/crates/swc/tests/exec/issues-5xxx/5433/index.js b/crates/swc/tests/exec/issues-5xxx/5433/index.js new file mode 100644 index 000000000000..c9fca4ce9881 --- /dev/null +++ b/crates/swc/tests/exec/issues-5xxx/5433/index.js @@ -0,0 +1,8 @@ +function* v0() { + try { + var v1 = yield v2(); + } catch (v9) { + } +} +var v1 = ({ foo: 10 }); +const v3 = JSON.stringify(v1, v0); diff --git a/crates/swc/tests/fixture/issues-110xx/11046/es5/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11046/es5/input/.swcrc new file mode 100644 index 000000000000..282824fcd095 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11046/es5/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es5" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11046/es5/input/index.js b/crates/swc/tests/fixture/issues-110xx/11046/es5/input/index.js new file mode 100644 index 000000000000..86b50f629430 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11046/es5/input/index.js @@ -0,0 +1,37 @@ +function* traverse(obj) { + for (let key of Object.keys(obj)) { + if (typeof obj[key] === "object") yield* traverse(obj[key]); + else yield obj[key]; + } +} + +const obj = { + data: [1, 2, 3], + nested: { + fieldName: "Nested", + fieldData: "Data", + }, + get dataGenerator() { + return traverse(this); + }, +}; + +function func1() { + const generator = (function () { + return obj.dataGenerator; + })(); + + const res = []; + for (let value of generator) { + res.push(value); + } + + return res; +} + +function main() { + const res = func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11046/es5/output/index.js b/crates/swc/tests/fixture/issues-110xx/11046/es5/output/index.js new file mode 100644 index 000000000000..8581d78d1368 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11046/es5/output/index.js @@ -0,0 +1,132 @@ +import { _ as _type_of } from "@swc/helpers/_/_type_of"; +import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator"; +import { _ as _ts_values } from "@swc/helpers/_/_ts_values"; +function traverse(obj) { + var _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, key, err; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined; + _state.label = 1; + case 1: + _state.trys.push([ + 1, + 8, + 9, + 10 + ]); + _iterator = Object.keys(obj)[Symbol.iterator](); + _state.label = 2; + case 2: + if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [ + 3, + 7 + ]; + key = _step.value; + if (!(_type_of(obj[key]) === "object")) return [ + 3, + 4 + ]; + return [ + 5, + _ts_values(traverse(obj[key])) + ]; + case 3: + _state.sent(); + return [ + 3, + 6 + ]; + case 4: + return [ + 4, + obj[key] + ]; + case 5: + _state.sent(); + _state.label = 6; + case 6: + _iteratorNormalCompletion = true; + return [ + 3, + 2 + ]; + case 7: + return [ + 3, + 10 + ]; + case 8: + err = _state.sent(); + _didIteratorError = true; + _iteratorError = err; + return [ + 3, + 10 + ]; + case 9: + try { + if (!_iteratorNormalCompletion && _iterator.return != null) { + _iterator.return(); + } + } finally{ + if (_didIteratorError) { + throw _iteratorError; + } + } + return [ + 7 + ]; + case 10: + return [ + 2 + ]; + } + }); +} +var obj = { + data: [ + 1, + 2, + 3 + ], + nested: { + fieldName: "Nested", + fieldData: "Data" + }, + get dataGenerator () { + return traverse(this); + } +}; +function func1() { + var generator = function() { + return obj.dataGenerator; + }(); + var res = []; + var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined; + try { + for(var _iterator = generator[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){ + var value = _step.value; + res.push(value); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally{ + try { + if (!_iteratorNormalCompletion && _iterator.return != null) { + _iterator.return(); + } + } finally{ + if (_didIteratorError) { + throw _iteratorError; + } + } + } + return res; +} +function main() { + var res = func1(); + console.log(res); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11047/es2017/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11047/es2017/input/.swcrc new file mode 100644 index 000000000000..903652fe1b8e --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11047/es2017/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es2017" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11047/es2017/input/index.js b/crates/swc/tests/fixture/issues-110xx/11047/es2017/input/index.js new file mode 100644 index 000000000000..35ce21d6ccb2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11047/es2017/input/index.js @@ -0,0 +1,22 @@ +const timeoutPromise = (timeout) => new Promise(resolve => setTimeout(resolve, timeout)); + +const processArray = async (array) => { + const [first, , third = 'default', ...rest] = array; + + if (!first || !third) { + return; + } + await timeoutPromise(10); + return {first, third, rest}; +} + +const func1 = async () => { + return await processArray(['value1', 'value2', , 'value4', 'value5', 'value6', 'value7']); +} + +const main = async () => { + const res = await func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11047/es2017/output/index.js b/crates/swc/tests/fixture/issues-110xx/11047/es2017/output/index.js new file mode 100644 index 000000000000..31845e0a0886 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11047/es2017/output/index.js @@ -0,0 +1,29 @@ +const timeoutPromise = (timeout)=>new Promise((resolve)=>setTimeout(resolve, timeout)); +const processArray = async (array)=>{ + const [first, , third = 'default', ...rest] = array; + if (!first || !third) { + return; + } + await timeoutPromise(10); + return { + first, + third, + rest + }; +}; +const func1 = async ()=>{ + return await processArray([ + 'value1', + 'value2', + , + 'value4', + 'value5', + 'value6', + 'value7' + ]); +}; +const main = async ()=>{ + const res = await func1(); + console.log(res); +}; +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11047/es5/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11047/es5/input/.swcrc new file mode 100644 index 000000000000..282824fcd095 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11047/es5/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es5" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11047/es5/input/index.js b/crates/swc/tests/fixture/issues-110xx/11047/es5/input/index.js new file mode 100644 index 000000000000..35ce21d6ccb2 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11047/es5/input/index.js @@ -0,0 +1,22 @@ +const timeoutPromise = (timeout) => new Promise(resolve => setTimeout(resolve, timeout)); + +const processArray = async (array) => { + const [first, , third = 'default', ...rest] = array; + + if (!first || !third) { + return; + } + await timeoutPromise(10); + return {first, third, rest}; +} + +const func1 = async () => { + return await processArray(['value1', 'value2', , 'value4', 'value5', 'value6', 'value7']); +} + +const main = async () => { + const res = await func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11047/es5/output/index.js b/crates/swc/tests/fixture/issues-110xx/11047/es5/output/index.js new file mode 100644 index 000000000000..37c3f40944a9 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11047/es5/output/index.js @@ -0,0 +1,85 @@ +import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator"; +import { _ as _to_array } from "@swc/helpers/_/_to_array"; +import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator"; +var timeoutPromise = function(timeout) { + return new Promise(function(resolve) { + return setTimeout(resolve, timeout); + }); +}; +var processArray = function(array) { + return _async_to_generator(function() { + var _array, first, tmp, third, rest; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + _array = _to_array(array), first = _array[0], tmp = _array[2], third = tmp === void 0 ? 'default' : tmp, rest = _array.slice(3); + if (!first || !third) { + return [ + 2 + ]; + } + return [ + 4, + timeoutPromise(10) + ]; + case 1: + _state.sent(); + return [ + 2, + { + first: first, + third: third, + rest: rest + } + ]; + } + }); + })(); +}; +var func1 = function() { + return _async_to_generator(function() { + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + return [ + 4, + processArray([ + 'value1', + 'value2', + , + 'value4', + 'value5', + 'value6', + 'value7' + ]) + ]; + case 1: + return [ + 2, + _state.sent() + ]; + } + }); + })(); +}; +var main = function() { + return _async_to_generator(function() { + var res; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + return [ + 4, + func1() + ]; + case 1: + res = _state.sent(); + console.log(res); + return [ + 2 + ]; + } + }); + })(); +}; +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11048/es2017/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11048/es2017/input/.swcrc new file mode 100644 index 000000000000..903652fe1b8e --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11048/es2017/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es2017" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11048/es2017/input/index.js b/crates/swc/tests/fixture/issues-110xx/11048/es2017/input/index.js new file mode 100644 index 000000000000..0ff17fdf6a77 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11048/es2017/input/index.js @@ -0,0 +1,29 @@ +async function fetchResource() { + let data = ["John", , 25, "Developer", "California"]; + await new Promise(resolve => setTimeout(resolve, 500)); + return data; +} + +function func1() { + return new Promise(async (resolve, reject) => { + let resource = await fetchResource(); + const [firstName, middleName = "N/A", age, ...rest] = resource; + + const result = (function() { + if(firstName) { + return {firstName, middleName, age, occupation: rest[0], location: rest[1]}; + } else { + return {error: "Failed to fetch resource"}; + } + })(); + + resolve(result); + }); +} + +async function main() { + const res = await func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11048/es2017/output/index.js b/crates/swc/tests/fixture/issues-110xx/11048/es2017/output/index.js new file mode 100644 index 000000000000..fec928e4afd1 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11048/es2017/output/index.js @@ -0,0 +1,38 @@ +async function fetchResource() { + let data = [ + "John", + , + 25, + "Developer", + "California" + ]; + await new Promise((resolve)=>setTimeout(resolve, 500)); + return data; +} +function func1() { + return new Promise(async (resolve, reject)=>{ + let resource = await fetchResource(); + const [firstName, middleName = "N/A", age, ...rest] = resource; + const result = function() { + if (firstName) { + return { + firstName, + middleName, + age, + occupation: rest[0], + location: rest[1] + }; + } else { + return { + error: "Failed to fetch resource" + }; + } + }(); + resolve(result); + }); +} +async function main() { + const res = await func1(); + console.log(res); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11048/es5/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11048/es5/input/.swcrc new file mode 100644 index 000000000000..282824fcd095 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11048/es5/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es5" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11048/es5/input/index.js b/crates/swc/tests/fixture/issues-110xx/11048/es5/input/index.js new file mode 100644 index 000000000000..0ff17fdf6a77 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11048/es5/input/index.js @@ -0,0 +1,29 @@ +async function fetchResource() { + let data = ["John", , 25, "Developer", "California"]; + await new Promise(resolve => setTimeout(resolve, 500)); + return data; +} + +function func1() { + return new Promise(async (resolve, reject) => { + let resource = await fetchResource(); + const [firstName, middleName = "N/A", age, ...rest] = resource; + + const result = (function() { + if(firstName) { + return {firstName, middleName, age, occupation: rest[0], location: rest[1]}; + } else { + return {error: "Failed to fetch resource"}; + } + })(); + + resolve(result); + }); +} + +async function main() { + const res = await func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11048/es5/output/index.js b/crates/swc/tests/fixture/issues-110xx/11048/es5/output/index.js new file mode 100644 index 000000000000..277f9e432198 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11048/es5/output/index.js @@ -0,0 +1,91 @@ +import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator"; +import { _ as _to_array } from "@swc/helpers/_/_to_array"; +import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator"; +function fetchResource() { + return _async_to_generator(function() { + var data; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + data = [ + "John", + , + 25, + "Developer", + "California" + ]; + return [ + 4, + new Promise(function(resolve) { + return setTimeout(resolve, 500); + }) + ]; + case 1: + _state.sent(); + return [ + 2, + data + ]; + } + }); + })(); +} +function func1() { + return new Promise(function(resolve, reject) { + return _async_to_generator(function() { + var resource, _resource, firstName, tmp, middleName, age, rest, result; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + return [ + 4, + fetchResource() + ]; + case 1: + resource = _state.sent(); + _resource = _to_array(resource), firstName = _resource[0], tmp = _resource[1], middleName = tmp === void 0 ? "N/A" : tmp, age = _resource[2], rest = _resource.slice(3); + result = function() { + if (firstName) { + return { + firstName: firstName, + middleName: middleName, + age: age, + occupation: rest[0], + location: rest[1] + }; + } else { + return { + error: "Failed to fetch resource" + }; + } + }(); + resolve(result); + return [ + 2 + ]; + } + }); + })(); + }); +} +function main() { + return _async_to_generator(function() { + var res; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + return [ + 4, + func1() + ]; + case 1: + res = _state.sent(); + console.log(res); + return [ + 2 + ]; + } + }); + })(); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11049/es2017/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11049/es2017/input/.swcrc new file mode 100644 index 000000000000..903652fe1b8e --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11049/es2017/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es2017" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11049/es2017/input/index.js b/crates/swc/tests/fixture/issues-110xx/11049/es2017/input/index.js new file mode 100644 index 000000000000..a090292bc13f --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11049/es2017/input/index.js @@ -0,0 +1,33 @@ +class MyClass { + constructor(data) { + if (!(data instanceof Array)) + throw new Error('Invalid instance'); + this.data = data; + } +} + +function fetchPromise(idx) { + return new Promise((resolve, reject) => { + setTimeout(() => resolve(idx), 10) + }) +} + +async function func1() { + const arr = [1, [2, 3], , 4]; + let [a, [ , b], ,d] = arr; + + const instance = new MyClass([a, b, d]) + + a = await fetchPromise(instance.data[0]) || fetchPromise(0); + b = await fetchPromise(instance.data[1]) || fetchPromise(0); + d = await fetchPromise(instance.data[2]) || fetchPromise(0); + + return [a, b, d]; +} + +function main() { + const res = func1(); + res.then(value => console.log(value)); +} + +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11049/es2017/output/index.js b/crates/swc/tests/fixture/issues-110xx/11049/es2017/output/index.js new file mode 100644 index 000000000000..4aab83a2bf4d --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11049/es2017/output/index.js @@ -0,0 +1,41 @@ +class MyClass { + constructor(data){ + if (!(data instanceof Array)) throw new Error('Invalid instance'); + this.data = data; + } +} +function fetchPromise(idx) { + return new Promise((resolve, reject)=>{ + setTimeout(()=>resolve(idx), 10); + }); +} +async function func1() { + const arr = [ + 1, + [ + 2, + 3 + ], + , + 4 + ]; + let [a, [, b], , d] = arr; + const instance = new MyClass([ + a, + b, + d + ]); + a = await fetchPromise(instance.data[0]) || fetchPromise(0); + b = await fetchPromise(instance.data[1]) || fetchPromise(0); + d = await fetchPromise(instance.data[2]) || fetchPromise(0); + return [ + a, + b, + d + ]; +} +function main() { + const res = func1(); + res.then((value)=>console.log(value)); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11049/es5/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11049/es5/input/.swcrc new file mode 100644 index 000000000000..282824fcd095 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11049/es5/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es5" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11049/es5/input/index.js b/crates/swc/tests/fixture/issues-110xx/11049/es5/input/index.js new file mode 100644 index 000000000000..a090292bc13f --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11049/es5/input/index.js @@ -0,0 +1,33 @@ +class MyClass { + constructor(data) { + if (!(data instanceof Array)) + throw new Error('Invalid instance'); + this.data = data; + } +} + +function fetchPromise(idx) { + return new Promise((resolve, reject) => { + setTimeout(() => resolve(idx), 10) + }) +} + +async function func1() { + const arr = [1, [2, 3], , 4]; + let [a, [ , b], ,d] = arr; + + const instance = new MyClass([a, b, d]) + + a = await fetchPromise(instance.data[0]) || fetchPromise(0); + b = await fetchPromise(instance.data[1]) || fetchPromise(0); + d = await fetchPromise(instance.data[2]) || fetchPromise(0); + + return [a, b, d]; +} + +function main() { + const res = func1(); + res.then(value => console.log(value)); +} + +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11049/es5/output/index.js b/crates/swc/tests/fixture/issues-110xx/11049/es5/output/index.js new file mode 100644 index 000000000000..ab45f786e35a --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11049/es5/output/index.js @@ -0,0 +1,76 @@ +import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator"; +import { _ as _class_call_check } from "@swc/helpers/_/_class_call_check"; +import { _ as _instanceof } from "@swc/helpers/_/_instanceof"; +import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array"; +import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator"; +var MyClass = function MyClass(data) { + "use strict"; + _class_call_check(this, MyClass); + if (!_instanceof(data, Array)) throw new Error('Invalid instance'); + this.data = data; +}; +function fetchPromise(idx) { + return new Promise(function(resolve, reject) { + setTimeout(function() { + return resolve(idx); + }, 10); + }); +} +function func1() { + return _async_to_generator(function() { + var arr, _arr, a, _arr_, b, d, instance; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + arr = [ + 1, + [ + 2, + 3 + ], + , + 4 + ]; + _arr = _sliced_to_array(arr, 4), a = _arr[0], _arr_ = _sliced_to_array(_arr[1], 2), b = _arr_[1], d = _arr[3]; + instance = new MyClass([ + a, + b, + d + ]); + return [ + 4, + fetchPromise(instance.data[0]) + ]; + case 1: + a = _state.sent() || fetchPromise(0); + return [ + 4, + fetchPromise(instance.data[1]) + ]; + case 2: + b = _state.sent() || fetchPromise(0); + return [ + 4, + fetchPromise(instance.data[2]) + ]; + case 3: + d = _state.sent() || fetchPromise(0); + return [ + 2, + [ + a, + b, + d + ] + ]; + } + }); + })(); +} +function main() { + var res = func1(); + res.then(function(value) { + return console.log(value); + }); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11050/es2017/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11050/es2017/input/.swcrc new file mode 100644 index 000000000000..903652fe1b8e --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11050/es2017/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es2017" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11050/es2017/input/index.js b/crates/swc/tests/fixture/issues-110xx/11050/es2017/input/index.js new file mode 100644 index 000000000000..f2e424b0c44b --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11050/es2017/input/index.js @@ -0,0 +1,47 @@ +async function complexCompute(num) { + return new Promise(resolve => { + setTimeout(() => { + resolve(num * 2); + }, 2000); + }); +} + +async function func1() { + let result; + let val = 1; // initial assignment + val = val || await complexCompute(5); + + outer_block: { + if (!val) { + result = "No Value"; + break outer_block; + } + + if (val < 10) { + switch(val) { + case 1: + result = "One"; + break outer_block; + case 2: + let temp = await complexCompute(2); + if (temp > val) { + result = "Two Modified"; + break outer_block; + } + result = "Two"; + break outer_block; + } + } + + result = "Out of scope"; + } + + return result; +} + +async function main() { + let res = await func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11050/es2017/output/index.js b/crates/swc/tests/fixture/issues-110xx/11050/es2017/output/index.js new file mode 100644 index 000000000000..ba29553b0f9a --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11050/es2017/output/index.js @@ -0,0 +1,40 @@ +async function complexCompute(num) { + return new Promise((resolve)=>{ + setTimeout(()=>{ + resolve(num * 2); + }, 2000); + }); +} +async function func1() { + let result; + let val = 1; // initial assignment + val = val || await complexCompute(5); + outer_block: { + if (!val) { + result = "No Value"; + break outer_block; + } + if (val < 10) { + switch(val){ + case 1: + result = "One"; + break outer_block; + case 2: + let temp = await complexCompute(2); + if (temp > val) { + result = "Two Modified"; + break outer_block; + } + result = "Two"; + break outer_block; + } + } + result = "Out of scope"; + } + return result; +} +async function main() { + let res = await func1(); + console.log(res); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11050/es5/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11050/es5/input/.swcrc new file mode 100644 index 000000000000..282824fcd095 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11050/es5/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es5" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11050/es5/input/index.js b/crates/swc/tests/fixture/issues-110xx/11050/es5/input/index.js new file mode 100644 index 000000000000..f2e424b0c44b --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11050/es5/input/index.js @@ -0,0 +1,47 @@ +async function complexCompute(num) { + return new Promise(resolve => { + setTimeout(() => { + resolve(num * 2); + }, 2000); + }); +} + +async function func1() { + let result; + let val = 1; // initial assignment + val = val || await complexCompute(5); + + outer_block: { + if (!val) { + result = "No Value"; + break outer_block; + } + + if (val < 10) { + switch(val) { + case 1: + result = "One"; + break outer_block; + case 2: + let temp = await complexCompute(2); + if (temp > val) { + result = "Two Modified"; + break outer_block; + } + result = "Two"; + break outer_block; + } + } + + result = "Out of scope"; + } + + return result; +} + +async function main() { + let res = await func1(); + console.log(res); +} + +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11050/es5/output/index.js b/crates/swc/tests/fixture/issues-110xx/11050/es5/output/index.js new file mode 100644 index 000000000000..9377a3540d51 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11050/es5/output/index.js @@ -0,0 +1,122 @@ +import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator"; +import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator"; +function complexCompute(num) { + return _async_to_generator(function() { + return _ts_generator(this, function(_state) { + return [ + 2, + new Promise(function(resolve) { + setTimeout(function() { + resolve(num * 2); + }, 2000); + }) + ]; + }); + })(); +} +function func1() { + return _async_to_generator(function() { + var result, val, _tmp, temp; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + val = 1; // initial assignment + _tmp = val; + if (_tmp) return [ + 3, + 2 + ]; + return [ + 4, + complexCompute(5) + ]; + case 1: + _tmp = _state.sent(); + _state.label = 2; + case 2: + val = _tmp; + if (!val) { + result = "No Value"; + return [ + 3, + 7 + ]; + } + if (!(val < 10)) return [ + 3, + 6 + ]; + switch(val){ + case 1: + return [ + 3, + 3 + ]; + case 2: + return [ + 3, + 4 + ]; + } + return [ + 3, + 6 + ]; + case 3: + result = "One"; + return [ + 3, + 7 + ]; + case 4: + return [ + 4, + complexCompute(2) + ]; + case 5: + temp = _state.sent(); + if (temp > val) { + result = "Two Modified"; + return [ + 3, + 7 + ]; + } + result = "Two"; + return [ + 3, + 7 + ]; + case 6: + result = "Out of scope"; + _state.label = 7; + case 7: + return [ + 2, + result + ]; + } + }); + })(); +} +function main() { + return _async_to_generator(function() { + var res; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + return [ + 4, + func1() + ]; + case 1: + res = _state.sent(); + console.log(res); + return [ + 2 + ]; + } + }); + })(); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11052/es2017/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11052/es2017/input/.swcrc new file mode 100644 index 000000000000..903652fe1b8e --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11052/es2017/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es2017" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11052/es2017/input/index.js b/crates/swc/tests/fixture/issues-110xx/11052/es2017/input/index.js new file mode 100644 index 000000000000..91623a4e3d16 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11052/es2017/input/index.js @@ -0,0 +1,41 @@ +class MyClass { + constructor() { + this._value = null; + } + + async fetchData() { + return new Promise(resolve => { + setTimeout(() => { + resolve('Data fetched'); + }, 10); + }); + } + + async fetchMoreData() { + return new Promise(resolve => { + setTimeout(() => { + resolve('More data fetched'); + }, 10); + }); + } + async initializeData() { + this._value = await this.fetchData(); + this._value += ' ' + await this.fetchMoreData(); + } + get value() { + return this._value; + } + set value(newValue) { + this._value = newValue; + } +} +async function func1() { + let myClass = new MyClass(); + await myClass.initializeData(); + return myClass.value; +} +async function main() { + let res = await func1(); + console.log(res); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11052/es2017/output/index.js b/crates/swc/tests/fixture/issues-110xx/11052/es2017/output/index.js new file mode 100644 index 000000000000..ca35e14f351f --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11052/es2017/output/index.js @@ -0,0 +1,39 @@ +class MyClass { + async fetchData() { + return new Promise((resolve)=>{ + setTimeout(()=>{ + resolve('Data fetched'); + }, 10); + }); + } + async fetchMoreData() { + return new Promise((resolve)=>{ + setTimeout(()=>{ + resolve('More data fetched'); + }, 10); + }); + } + async initializeData() { + this._value = await this.fetchData(); + this._value += ' ' + await this.fetchMoreData(); + } + get value() { + return this._value; + } + set value(newValue) { + this._value = newValue; + } + constructor(){ + this._value = null; + } +} +async function func1() { + let myClass = new MyClass(); + await myClass.initializeData(); + return myClass.value; +} +async function main() { + let res = await func1(); + console.log(res); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11052/es5/input/.swcrc b/crates/swc/tests/fixture/issues-110xx/11052/es5/input/.swcrc new file mode 100644 index 000000000000..282824fcd095 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11052/es5/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript" + }, + "target": "es5" + } +} diff --git a/crates/swc/tests/fixture/issues-110xx/11052/es5/input/index.js b/crates/swc/tests/fixture/issues-110xx/11052/es5/input/index.js new file mode 100644 index 000000000000..91623a4e3d16 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11052/es5/input/index.js @@ -0,0 +1,41 @@ +class MyClass { + constructor() { + this._value = null; + } + + async fetchData() { + return new Promise(resolve => { + setTimeout(() => { + resolve('Data fetched'); + }, 10); + }); + } + + async fetchMoreData() { + return new Promise(resolve => { + setTimeout(() => { + resolve('More data fetched'); + }, 10); + }); + } + async initializeData() { + this._value = await this.fetchData(); + this._value += ' ' + await this.fetchMoreData(); + } + get value() { + return this._value; + } + set value(newValue) { + this._value = newValue; + } +} +async function func1() { + let myClass = new MyClass(); + await myClass.initializeData(); + return myClass.value; +} +async function main() { + let res = await func1(); + console.log(res); +} +main(); diff --git a/crates/swc/tests/fixture/issues-110xx/11052/es5/output/index.js b/crates/swc/tests/fixture/issues-110xx/11052/es5/output/index.js new file mode 100644 index 000000000000..700f4e44b107 --- /dev/null +++ b/crates/swc/tests/fixture/issues-110xx/11052/es5/output/index.js @@ -0,0 +1,131 @@ +import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator"; +import { _ as _class_call_check } from "@swc/helpers/_/_class_call_check"; +import { _ as _create_class } from "@swc/helpers/_/_create_class"; +import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator"; +var MyClass = /*#__PURE__*/ function() { + "use strict"; + function MyClass() { + _class_call_check(this, MyClass); + this._value = null; + } + _create_class(MyClass, [ + { + key: "fetchData", + value: function fetchData() { + return _async_to_generator(function() { + return _ts_generator(this, function(_state) { + return [ + 2, + new Promise(function(resolve) { + setTimeout(function() { + resolve('Data fetched'); + }, 10); + }) + ]; + }); + })(); + } + }, + { + key: "fetchMoreData", + value: function fetchMoreData() { + return _async_to_generator(function() { + return _ts_generator(this, function(_state) { + return [ + 2, + new Promise(function(resolve) { + setTimeout(function() { + resolve('More data fetched'); + }, 10); + }) + ]; + }); + })(); + } + }, + { + key: "initializeData", + value: function initializeData() { + return _async_to_generator(function() { + var _, _1, _2, _3; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + _ = this; + return [ + 4, + this.fetchData() + ]; + case 1: + _._value = _state.sent(); + _1 = this; + _2 = _1._value; + _3 = ' '; + return [ + 4, + this.fetchMoreData() + ]; + case 2: + _1._value = _2 + (_3 + _state.sent()); + return [ + 2 + ]; + } + }); + }).call(this); + } + }, + { + key: "value", + get: function get() { + return this._value; + }, + set: function set(newValue) { + this._value = newValue; + } + } + ]); + return MyClass; +}(); +function func1() { + return _async_to_generator(function() { + var myClass; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + myClass = new MyClass(); + return [ + 4, + myClass.initializeData() + ]; + case 1: + _state.sent(); + return [ + 2, + myClass.value + ]; + } + }); + })(); +} +function main() { + return _async_to_generator(function() { + var res; + return _ts_generator(this, function(_state) { + switch(_state.label){ + case 0: + return [ + 4, + func1() + ]; + case 1: + res = _state.sent(); + console.log(res); + return [ + 2 + ]; + } + }); + })(); +} +main(); diff --git a/crates/swc/tests/stacktrace/issue-622/output/stacks.txt b/crates/swc/tests/stacktrace/issue-622/output/stacks.txt index 2671883d522b..f6688cb6ce75 100644 --- a/crates/swc/tests/stacktrace/issue-622/output/stacks.txt +++ b/crates/swc/tests/stacktrace/issue-622/output/stacks.txt @@ -5,8 +5,8 @@ $DIR/tests/stacktrace/issue-622/input/index.js:3 ReferenceError: call is not defined at foo ($DIR/tests/stacktrace/issue-622/input/index.js:3:9) - at step ($DIR/tests/stacktrace/issue-622/input/_exec.js:105:23) - at Object.next ($DIR/tests/stacktrace/issue-622/input/_exec.js:46:20) + at step ($DIR/tests/stacktrace/issue-622/input/_exec.js:113:23) + at Object. ($DIR/tests/stacktrace/issue-622/input/_exec.js:54:20) at asyncGeneratorStep ($DIR/tests/stacktrace/issue-622/input/_exec.js:4:28) at _next ($DIR/tests/stacktrace/issue-622/input/_exec.js:22:17) at $DIR/tests/stacktrace/issue-622/input/_exec.js:27:13 diff --git a/crates/swc/tests/vercel/full/next-31419/1/output/index.js b/crates/swc/tests/vercel/full/next-31419/1/output/index.js index 91bda3745529..be01c1884fdb 100644 --- a/crates/swc/tests/vercel/full/next-31419/1/output/index.js +++ b/crates/swc/tests/vercel/full/next-31419/1/output/index.js @@ -7,7 +7,7 @@ Promise.all(assignAll).then(function(t) { return e(this, function(a) { switch(a.label){ case 0: - for(c in r = function(r) { + for(o in r = function(r) { var n; return e(this, function(e) { switch(e.label){ @@ -24,21 +24,25 @@ Promise.all(assignAll).then(function(t) { ]; } }); - }, s = 'DELETE FROM "TABLE" WHERE "UUID" IN ( ', i = [], t)i.push(c); - o = 0, a.label = 1; + }, s = 'DELETE FROM "TABLE" WHERE "UUID" IN ( ', c = [], i = t)c.push(o); + u = 0, a.label = 1; case 1: - if (!(o < i.length)) return [ + if (!(u < c.length)) return [ 3, 4 ]; - return u = i[o], [ + if (!((o = c[u]) in i)) return [ + 3, + 3 + ]; + return [ 5, - n(r(u)) + n(r(o)) ]; case 2: a.sent(), a.label = 3; case 3: - return o++, [ + return u++, [ 3, 1 ]; diff --git a/crates/swc_ecma_compat_es2015/src/generator.rs b/crates/swc_ecma_compat_es2015/src/generator.rs index f64636044670..e548358334b9 100644 --- a/crates/swc_ecma_compat_es2015/src/generator.rs +++ b/crates/swc_ecma_compat_es2015/src/generator.rs @@ -633,16 +633,52 @@ impl VisitMut for Generator { } } if node.op != op!("=") { - let left_of_right = + // Compound assignment: a.b += yield -> a.b = cache(a.b) + yield + // Following TypeScript's visitRightAssociativeBinaryExpression + + // 1. Save target (the already-processed left, e.g., _._value) + let target = node.left.clone(); + + // 2. Cache target's value for right-side computation + let cached_value = self.cache_expression(node.left.take().expect_simple().into()); + // 3. Visit right expression node.right.visit_mut_with(self); + // 4. Convert compound assignment to normal assignment + let bin_op = match node.op { + op!("+=") => op!(bin, "+"), + op!("-=") => op!(bin, "-"), + op!("*=") => op!("*"), + op!("/=") => op!("/"), + op!("%=") => op!("%"), + op!("**=") => op!("**"), + op!("<<=") => op!("<<"), + op!(">>=") => op!(">>"), + op!(">>>=") => op!(">>>"), + op!("&=") => op!("&"), + op!("|=") => op!("|"), + op!("^=") => op!("^"), + op!("&&=") => op!("&&"), + op!("||=") => op!("||"), + op!("??=") => op!("??"), + _ => { + unreachable!("unknown compound assignment operator") + } + }; + *e = AssignExpr { - span: node.right.span(), - op: node.op, - left: left_of_right.into(), - right: node.right.take(), + span: node.span, + op: op!("="), + left: target, + right: BinExpr { + span: DUMMY_SP, + op: bin_op, + left: cached_value.into(), + right: node.right.take(), + } + .into(), } .into(); } else { @@ -1784,39 +1820,46 @@ impl Generator { // } // // [intermediate] - // .local _a, _b, _i - // _a = []; - // for (_b in o) _a.push(_b); + // .local _obj, _keys, _key, _i + // _obj = o; + // _keys = []; + // for (_key in _obj) _keys.push(_key); // _i = 0; // .loop incrementLabel, endLoopLabel // .mark conditionLabel - // .brfalse endLoopLabel, (_i < _a.length) - // p = _a[_i]; + // .brfalse endLoopLabel, (_i < _keys.length) + // _key = _keys[_i]; + // .brfalse incrementLabel, (_key in _obj) + // p = _key; // /*body*/ // .mark incrementLabel - // _b++; + // _i++; // .br conditionLabel // .endloop // .mark endLoopLabel + let obj = self.declare_local(None); let keys_array = self.declare_local(None); let key = self.declare_local(None); let keys_index = private_ident!("_i"); self.hoist_variable_declaration(&keys_index); + // Cache the object expression + node.right.visit_mut_with(self); + self.emit_assignment(obj.clone().into(), node.right.take(), None); + self.emit_assignment( keys_array.clone().into(), Box::new(ArrayLit::dummy().into()), None, ); - node.right.visit_mut_with(self); self.emit_stmt( ForInStmt { span: DUMMY_SP, left: ForHead::Pat(key.clone().into()), - right: node.right.take(), + right: Box::new(obj.clone().into()), body: Box::new(Stmt::Expr(ExprStmt { span: DUMMY_SP, expr: CallExpr { @@ -1825,7 +1868,7 @@ impl Generator { .clone() .make_member(quote_ident!("push")) .as_callee(), - args: vec![key.as_arg()], + args: vec![key.clone().as_arg()], ..Default::default() } .into(), @@ -1850,6 +1893,30 @@ impl Generator { None, ); + // Assign current key from array + self.emit_assignment( + key.clone().into(), + MemberExpr { + span: DUMMY_SP, + obj: Box::new(keys_array.into()), + prop: MemberProp::Computed(ComputedPropName { + span: DUMMY_SP, + expr: Box::new(keys_index.clone().into()), + }), + } + .into(), + None, + ); + + // Check if key still exists in object (handles property deletion during + // iteration) + self.emit_break_when_false( + increment_label, + Box::new(key.clone().make_bin(op!("in"), obj)), + None, + ); + + // Assign to user's variable let variable = match node.left { ForHead::VarDecl(initializer) => { for variable in initializer.decls.iter() { @@ -1872,19 +1939,7 @@ impl Generator { #[cfg(swc_ast_unknown)] _ => panic!("unable to access unknown nodes"), }; - self.emit_assignment( - variable.try_into().unwrap(), - MemberExpr { - span: DUMMY_SP, - obj: Box::new(keys_array.into()), - prop: MemberProp::Computed(ComputedPropName { - span: DUMMY_SP, - expr: Box::new(keys_index.clone().into()), - }), - } - .into(), - None, - ); + self.emit_assignment(variable.try_into().unwrap(), Box::new(key.into()), None); self.transform_and_emit_embedded_stmt(*node.body); self.mark_label(increment_label); @@ -2604,19 +2659,21 @@ impl Generator { if let Some(block_stack) = &self.block_stack { if let Some(label_text) = label_text { - for i in (0..=block_stack.len() - 1).rev() { - let block = &block_stack[i]; - if (self.supports_labeled_break_or_continue(&block.borrow()) - && block.borrow().label_text().unwrap() == label_text) - || (self.supports_unlabeled_break(&block.borrow()) - && self.has_immediate_containing_labeled_block(&label_text, i - 1)) + // For labeled break, only match LabeledBlocks. + // Unlike `continue`, we don't need to check Switch/Loop blocks + // because LabeledBlocks always have break_label, and the labeled + // break should exit to the LabeledBlock's end, not the inner + // Switch/Loop's end. + for block in block_stack.iter().rev() { + if self.supports_labeled_break_or_continue(&block.borrow()) + && block.borrow().label_text().unwrap() == label_text { return block.borrow().break_label().unwrap(); } } } else { - for i in (0..=block_stack.len() - 1).rev() { - let block = &block_stack[i]; + // For unlabeled break, match the innermost Switch/Loop + for block in block_stack.iter().rev() { if self.supports_unlabeled_break(&block.borrow()) { return block.borrow().break_label().unwrap(); } diff --git a/crates/swc_ecma_transforms_base/src/helpers/_ts_generator.js b/crates/swc_ecma_transforms_base/src/helpers/_ts_generator.js index 01f63af2c35c..4fde75b49dca 100644 --- a/crates/swc_ecma_transforms_base/src/helpers/_ts_generator.js +++ b/crates/swc_ecma_transforms_base/src/helpers/_ts_generator.js @@ -1,6 +1,6 @@ function _ts_generator(thisArg, body) { - var f, y, t, _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g; + var f, y, t, _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty; + return d(g, "next", { value: verb(0) }), d(g, "throw", { value: verb(1) }), d(g, "return", { value: verb(2) }), typeof Symbol === "function" && d(g, Symbol.iterator, { value: function () { return this; } }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); diff --git a/crates/swc_node_bundler/tests/pass/issue-1328/case1/output/entry.js b/crates/swc_node_bundler/tests/pass/issue-1328/case1/output/entry.js index f6e45c89f326..4e6ff8faa342 100644 --- a/crates/swc_node_bundler/tests/pass/issue-1328/case1/output/entry.js +++ b/crates/swc_node_bundler/tests/pass/issue-1328/case1/output/entry.js @@ -36,9 +36,17 @@ function _ts_generator(thisArg, body) { }, trys: [], ops: [] - }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { - return this; + }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty; + return d(g, "next", { + value: verb(0) + }), d(g, "throw", { + value: verb(1) + }), d(g, "return", { + value: verb(2) + }), typeof Symbol === "function" && d(g, Symbol.iterator, { + value: function() { + return this; + } }), g; function verb(n) { return function(v) { diff --git a/crates/swc_node_bundler/tests/pass/regenerator/1/output/entry.js b/crates/swc_node_bundler/tests/pass/regenerator/1/output/entry.js index 69f21a67e57c..2a5e70f996f9 100644 --- a/crates/swc_node_bundler/tests/pass/regenerator/1/output/entry.js +++ b/crates/swc_node_bundler/tests/pass/regenerator/1/output/entry.js @@ -36,9 +36,17 @@ function _ts_generator(thisArg, body) { }, trys: [], ops: [] - }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { - return this; + }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty; + return d(g, "next", { + value: verb(0) + }), d(g, "throw", { + value: verb(1) + }), d(g, "return", { + value: verb(2) + }), typeof Symbol === "function" && d(g, Symbol.iterator, { + value: function() { + return this; + } }), g; function verb(n) { return function(v) { diff --git a/packages/core/__tests__/transform/preset-env_test.mjs b/packages/core/__tests__/transform/preset-env_test.mjs index 1e7d657506f8..ceedc7a09afa 100644 --- a/packages/core/__tests__/transform/preset-env_test.mjs +++ b/packages/core/__tests__/transform/preset-env_test.mjs @@ -65,9 +65,17 @@ it("should respect custom transform option", async () => { }, trys: [], ops: [] - }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { - return this; + }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty; + return d(g, "next", { + value: verb(0) + }), d(g, "throw", { + value: verb(1) + }), d(g, "return", { + value: verb(2) + }), typeof Symbol === "function" && d(g, Symbol.iterator, { + value: function() { + return this; + } }), g; function verb(n) { return function(v) { diff --git a/packages/helpers/.dprint.json b/packages/helpers/.dprint.json index dabf9f514368..663ed34c33d6 100644 --- a/packages/helpers/.dprint.json +++ b/packages/helpers/.dprint.json @@ -14,10 +14,17 @@ "json": { "indentWidth": 4 }, - "includes": ["{esm,cjs,src}/*.{js,mjs,cjs}", "**/*.json"], - "excludes": ["**/node_modules", "**/*-lock.json"], + "includes": [ + "{esm,cjs,src}/*.{js,mjs,cjs}", + "**/*.json" + ], + "excludes": [ + "**/node_modules", + "**/*-lock.json", + "_ts_generator.{js,cjs}" + ], "plugins": [ "https://plugins.dprint.dev/typescript-0.84.0.wasm", "https://plugins.dprint.dev/json-0.17.1.wasm" ] -} +} \ No newline at end of file diff --git a/packages/helpers/esm/_ts_generator.js b/packages/helpers/esm/_ts_generator.js index dfe3b633c16f..6b7741299b06 100644 --- a/packages/helpers/esm/_ts_generator.js +++ b/packages/helpers/esm/_ts_generator.js @@ -1 +1,29 @@ -export { __generator as _ } from "tslib"; +function _ts_generator(thisArg, body) { + var f, y, t, _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty; + return d(g, "next", { value: verb(0) }), d(g, "throw", { value: verb(1) }), d(g, "return", { value: verb(2) }), typeof Symbol === "function" && d(g, Symbol.iterator, { value: function () { return this; } }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +} + +export { _ts_generator as _ }; diff --git a/packages/helpers/scripts/ast_grep.js b/packages/helpers/scripts/ast_grep.js index 30bc1c26b2c5..8bbaff5d5212 100644 --- a/packages/helpers/scripts/ast_grep.js +++ b/packages/helpers/scripts/ast_grep.js @@ -20,7 +20,8 @@ export function ast_grep() { const source = new MagicString(tree.root().text()); source.prepend(`"use strict";\n\n`); - if (filename.startsWith("_ts")) { + // We have forked _ts_generator from tslib + if (filename.startsWith("_ts") && filename !== "_ts_generator") { const match = tree.root().find(`export { $NAME as _ } from "tslib"`); if (match) { const name = match.getMatch("NAME").text();