diff --git a/CHANGELOG.md b/CHANGELOG.md index 681f4ff28..5d5b8c3c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,48 @@ # rollup changelog +## 4.27.2 + +_2024-11-15_ + +### Bug Fixes + +- Ensure unused variables in patterns are always deconflicted if rendered (#5728) + +### Pull Requests + +- [#5728](https://github.com/rollup/rollup/pull/5728): Fix more variable deconflicting issues (@lukastaegert) + +## 4.27.1 + +_2024-11-15_ + +### Bug Fixes + +- Fix some situations where parameter declarations could put Rollup into an infinite loop (#5727) + +### Pull Requests + +- [#5727](https://github.com/rollup/rollup/pull/5727): Debug out-of-memory issues with Rollup v4.27.0 (@lukastaegert) + +## 4.27.0 + +_2024-11-15_ + +### Features + +- Tree-shake unused properties in object literals (#5420) + +### Bug Fixes + +- Change hash length limit to 21 to avoid inconsistent hash length (#5423) + +### Pull Requests + +- [#5420](https://github.com/rollup/rollup/pull/5420): feat: implement object tree-shaking (@TrickyPi, @lukastaegert) +- [#5723](https://github.com/rollup/rollup/pull/5723): Reduce max hash size to 21 (@lukastaegert) +- [#5724](https://github.com/rollup/rollup/pull/5724): fix(deps): update swc monorepo (major) (@renovate[bot]) +- [#5725](https://github.com/rollup/rollup/pull/5725): chore(deps): lock file maintenance minor/patch updates (@renovate[bot]) + ## 4.26.0 _2024-11-13_ diff --git a/browser/package.json b/browser/package.json index 35079582a..501eaeefd 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "4.26.0", + "version": "4.27.2", "description": "Next-generation ES module bundler browser build", "main": "dist/rollup.browser.js", "module": "dist/es/rollup.browser.js", diff --git a/docs/migration/index.md b/docs/migration/index.md index 4ef18503f..97c24b0de 100644 --- a/docs/migration/index.md +++ b/docs/migration/index.md @@ -24,7 +24,11 @@ Rollup 现在包含了自动安装(和删除)的原生代码,如果你的 面向浏览器的构建(NPM 上的 `@rollup/browser`)现在依赖于一个需要提供的 WASM 文件。如果你正在使用 Vite 的浏览器构建,你需要将 `"@rollup/browser"` 添加到 `optimizeDeps.exclude` 中,否则 `npm run dev` 将因为 `.wasm` 文件的无效路径而失败(请参阅 [vitejs #14609](https://github.com/vitejs/vite/issues/14609))。否则,它应该可以正常工作,无需任何特定的干预。 +<<<<<<< HEAD 否则,一个明显的变化是 Rollup 现在在文件名中使用 url 安全的 base64 哈希,而不是旧的 base16 哈希。这提供了更多的哈希安全性,但意味着由于技术原因,哈希长度现在最多限制为 22 个字符。 +======= +Otherwise, an obvious change is that Rollup now uses url-safe base64 hashes in file names instead of the older base16 hashes. This provides more hash safety but means that hash length is now limited to at most 21 characters for technical reasons. +>>>>>>> a503a4dd9982bf20fd38aeb171882a27828906ae 当打包 CLI 应用程序时,如果输出 [`format`](../configuration-options/index.md#output-format) 为 `es` 或 `cjs`,Rollup 现在将自动保留入口文件中的 shebang 注释。以前,你需要通过插件添加注释。 diff --git a/package-lock.json b/package-lock.json index a60100adb..057313976 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rollup", - "version": "4.26.0", + "version": "4.27.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rollup", - "version": "4.26.0", + "version": "4.27.2", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -22,7 +22,7 @@ "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.34.2", "@eslint/js": "^9.14.0", - "@inquirer/prompts": "^7.0.1", + "@inquirer/prompts": "^7.1.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@mermaid-js/mermaid-cli": "^11.4.0", "@napi-rs/cli": "^2.18.4", @@ -58,7 +58,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-unicorn": "^56.0.0", - "eslint-plugin-vue": "^9.30.0", + "eslint-plugin-vue": "^9.31.0", "fixturify": "^3.0.0", "flru": "^1.0.2", "fs-extra": "^11.2.0", @@ -79,7 +79,7 @@ "pretty-bytes": "^6.1.1", "pretty-ms": "^9.1.0", "requirejs": "^2.3.7", - "rollup": "^4.24.4", + "rollup": "^4.25.0", "rollup-plugin-license": "^3.5.3", "rollup-plugin-string": "^3.0.0", "semver": "^7.6.3", @@ -91,8 +91,8 @@ "terser": "^5.36.0", "tslib": "^2.8.1", "typescript": "^5.6.3", - "typescript-eslint": "^8.13.0", - "vite": "^5.4.10", + "typescript-eslint": "^8.14.0", + "vite": "^5.4.11", "vitepress": "^1.5.0", "vue": "^3.5.12", "vue-tsc": "^2.1.10", @@ -108,37 +108,37 @@ } }, "node_modules/@algolia/autocomplete-core": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.6.tgz", - "integrity": "sha512-lkDoW4I7h2kKlIgf3pUt1LqvxyYKkVyiypoGLlUnhPSnCpmeOwudM6rNq6YYsCmdQtnDQoW5lUNNuj6ASg3qeg==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.17.6", - "@algolia/autocomplete-shared": "1.17.6" + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" } }, "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.6.tgz", - "integrity": "sha512-17NnaacuFzSWVuZu4NKzVeaFIe9Abpw8w+/gjc7xhZFtqj+GadufzodIdchwiB2eM2cDdiR3icW7gbNTB3K2YA==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/autocomplete-shared": "1.17.6" + "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.6.tgz", - "integrity": "sha512-Cvg5JENdSCMuClwhJ1ON1/jSuojaYMiUW2KePm18IkdCzPJj/NXojaOxw58RFtQFpJgfVW8h2E8mEoDtLlMdeA==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/autocomplete-shared": "1.17.6" + "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", @@ -146,9 +146,9 @@ } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.6.tgz", - "integrity": "sha512-aq/3V9E00Tw2GC/PqgyPGXtqJUlVc17v4cn1EUhSc+O/4zd04Uwb3UmPm8KDaYQQOrkt1lwvCj2vG2wRE5IKhw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -157,41 +157,41 @@ } }, "node_modules/@algolia/client-abtesting": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.13.0.tgz", - "integrity": "sha512-6CoQjlMi1pmQYMQO8tXfuGxSPf6iKX5FP9MuMe6IWmvC81wwTvOehnwchyBl2wuPVhcw2Ar53K53mQ60DAC64g==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.14.0.tgz", + "integrity": "sha512-HR4kbCmq4RO8vhafLrVcR11q3BvuPYA4o+Nn8hzJRgpDu2fauIlgIBgVDsoxaK90xuaPLSNdoT5tWXag+L8vCw==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.13.0.tgz", - "integrity": "sha512-pS3qyXiWTwKnrt/jE79fqkNqZp7kjsFNlJDcBGkSWid74DNc6DmArlkvPqyLxnoaYGjUGACT6g56n7E3mVV2TA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.14.0.tgz", + "integrity": "sha512-EnmouGUQdIvwmI8plglt3HP9hXwNNwCJshszfU/Hqi2n21//iwmWLmMb5gXDfiLhyMa6u8eya8c03QT79s3/tQ==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.13.0.tgz", - "integrity": "sha512-2SP6bGGWOTN920MLZv8s7yIR3OqY03vEe4U+vb2MGdL8a/8EQznF3L/nTC/rGf/hvEfZlX2tGFxPJaF2waravg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.14.0.tgz", + "integrity": "sha512-xYaswEqv+mTeazOJV0PELs4LYXaETYGwlntQxvOTHsICaj1e+ylKeMr+C+ZvN74RpCRDoEN3a2n33bRU9/MHTw==", "dev": true, "license": "MIT", "engines": { @@ -199,151 +199,151 @@ } }, "node_modules/@algolia/client-insights": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.13.0.tgz", - "integrity": "sha512-ldHTe+LVgC6L4Wr6doAQQ7Ku0jAdhaaPg1T+IHzmmiRZb2Uq5OsjW2yC65JifOmzPCiMkIZE2mGRpWgkn5ktlw==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.14.0.tgz", + "integrity": "sha512-1dWxjTmpNCgLWLl6GSAaOACs55JvioAIdno7jvq7KVfpLLXehHaSaiij8ssbbIM8HqHZPwC8ShaUHtSt2jLdBg==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.13.0.tgz", - "integrity": "sha512-RnCfOSN4OUJDuMNHFca2M8lY64Tmw0kQOZikge4TknTqHmlbKJb8IbJE7Rol79Z80W2Y+B1ydcjV7DPje4GMRA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.14.0.tgz", + "integrity": "sha512-HDOYm38nUwflxaemKrxlV91pYg3L9JkmLnuSQCJ7bzivqP+aBTZ8mGRvanFzwayNMRZWLuGsstJMpGET6FYaDQ==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.13.0.tgz", - "integrity": "sha512-pYo0jbLUtPDN1r341UHTaF2fgN5rbaZfDZqjPRKPM+FRlRmxFxqFQm1UUfpkSUWYGn7lECwDpbKYiKUf81MTwA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.14.0.tgz", + "integrity": "sha512-yDPf3E3MS2RUg1br7r1+PEqKOxUftxjLLtD35yW9voZ9oV45XZnAPnHCqgmyzjcK5/dM1dzXHhmZGf4VbjYn7Q==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.13.0.tgz", - "integrity": "sha512-s2ge3uZ6Zg2sPSFibqijgEYsuorxcc8KVHg3I95nOPHvFHdnBtSHymhZvq4sp/fu8ijt/Y8jLwkuqm5myn+2Sg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.14.0.tgz", + "integrity": "sha512-x5/GVLDyGad8aiWA/vfj8X4NXOZ3FlwXw/gb7t+Mxo3O0g3VxSFQdyrZ8Oduv/Y/Y8cxMVEOx1u3Azs6tlSZbg==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/ingestion": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.13.0.tgz", - "integrity": "sha512-fm5LEOe4FPDOc1D+M9stEs8hfcdmbdD+pt9og5shql6ueTZJANDbFoQhDOpiPJizR/ps1GwmjkWfUEywx3sV+Q==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.14.0.tgz", + "integrity": "sha512-HU9AoZDFMEIT/+xzIa9l1XkPRTH7S0jWbYWrNkeb/62TxQFvL5x/XYEa6Yf/WCFU6Qa0W+ivua8NDzxL15NVGQ==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.13.0.tgz", - "integrity": "sha512-e8Hshlnm2G5fapyUgWTBwhJ22yXcnLtPC4LWZKx7KOvv35GcdoHtlUBX94I/sWCJLraUr65JvR8qOo3LXC43dg==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.14.0.tgz", + "integrity": "sha512-tGKip5Dvusw8z4ajIJBBYxdPUOGIqV1CGat55eCaAmX97Oko2adIOq9MKvdC3d7SMuQt3j28QIHpV6wvihnsKA==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.13.0.tgz", - "integrity": "sha512-53/wW96oaj1FKMzGdFcZ/epygfTppLDUvgI1thLkd475EtVZCH3ZZVUNCEvf1AtnNyH1RnItkFzX8ayWCpx2PQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.14.0.tgz", + "integrity": "sha512-wXOWFG4L0Y/EyWKuDXQA7FoB7Ukuss+O8zaxZSlla4h19UGWak+22RcZ2eDFoAhVOJxC8RoLg9opMfDbZtPW9Q==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.13.0.tgz", - "integrity": "sha512-NV6oSCt5lFuzfsVQoSBpewEWf/h4ySr7pv2bfwu9yF/jc/g39pig8+YpuqsxlRWBm/lTGVA2V0Ai9ySwrNumIA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.14.0.tgz", + "integrity": "sha512-5zk1sol+WTDskAx1AMBGGDChCVBHuPTmclGZO844/ljqH7AcJpkFnfUeAMXfx2m4tW3Ax+M+uaC+XjVoQRb9Hg==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0" + "@algolia/client-common": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.13.0.tgz", - "integrity": "sha512-094bK4rumf+rXJazxv3mq6eKRM0ep5AxIo8T0YmOdldswQt79apeufFiPLN19nHEWH22xR2FelimD+T/wRSP+Q==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.14.0.tgz", + "integrity": "sha512-B9grYSKH34UlJPkUdds14I/m8Yp7/a4PbqRuZsrP1L4kBW2FGinMtpQOK3N6gEy8YkVNA1iKlTC24yro8z8a8A==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0" + "@algolia/client-common": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.13.0.tgz", - "integrity": "sha512-JY5xhEYMgki53Wm+A6R2jUpOUdD0zZnBq+PC5R1TGMNOYL1s6JjDrJeMsvaI2YWxYMUSoCnRoltN/yf9RI8n3A==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.14.0.tgz", + "integrity": "sha512-2EPhRqbxWzrsSXX0/70jIGtjQTj8VILi+uqmgBweyQIzCNlGoNbyMs+E7iwHVtUSrE/9IDd8rrewkVHOI6h2IQ==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.13.0" + "@algolia/client-common": "5.14.0" }, "engines": { "node": ">= 14.0.0" @@ -686,9 +686,9 @@ "license": "Apache-2.0" }, "node_modules/@codemirror/autocomplete": { - "version": "6.18.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.2.tgz", - "integrity": "sha512-wJGylKtMFR/Ds6Gh01+OovXE/pncPiKZNNBKuC39pKnH+XK5d9+WsNqcrdxPjFPFTigRBqse0rfxw9UxrfyhPg==", + "version": "6.18.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.3.tgz", + "integrity": "sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==", "dev": true, "license": "MIT", "dependencies": { @@ -792,33 +792,33 @@ } }, "node_modules/@docsearch/css": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.7.0.tgz", - "integrity": "sha512-1OorbTwi1eeDmr0v5t+ckSRlt1zM5GHjm92iIl3kUu7im3GHuP+csf6E0WBg8pdXQczTWP9J9+o9n+Vg6DH5cQ==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.0.tgz", + "integrity": "sha512-pieeipSOW4sQ0+bE5UFC51AOZp9NGxg89wAlZ1BAQFaiRAGK1IKUaPQ0UGZeNctJXyqZ1UvBtOQh2HH+U5GtmA==", "dev": true, "license": "MIT" }, "node_modules/@docsearch/js": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.7.0.tgz", - "integrity": "sha512-ScfqOIKrSr8SImbpxVaD59xc/bytbL8QEM2GUpe3aICmoICflWp5DyTRzAdFky16HY+yEOAVZXt3COXQ1NOCWw==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.0.tgz", + "integrity": "sha512-PVuV629f5UcYRtBWqK7ID6vNL5647+2ADJypwTjfeBIrJfwPuHtzLy39hMGMfFK+0xgRyhTR0FZ83EkdEraBlg==", "dev": true, "license": "MIT", "dependencies": { - "@docsearch/react": "3.7.0", + "@docsearch/react": "3.8.0", "preact": "^10.0.0" } }, "node_modules/@docsearch/react": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.7.0.tgz", - "integrity": "sha512-8e6tdDfkYoxafEEPuX5eE1h9cTkLvhe4KgoFkO5JCddXSQONnN1FHcDZRI4r8894eMpbYq6rdJF0dVYh8ikwNQ==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.0.tgz", + "integrity": "sha512-WnFK720+iwTVt94CxY3u+FgX6exb3BfN5kE9xUY6uuAH/9W/UFboBZFLlrw/zxFRHoHZCOXRtOylsXF+6LHI+Q==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/autocomplete-core": "1.17.6", - "@algolia/autocomplete-preset-algolia": "1.17.6", - "@docsearch/css": "3.7.0", + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.0", "algoliasearch": "^5.12.0" }, "peerDependencies": { @@ -1468,9 +1468,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", - "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1607,15 +1607,15 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.1.tgz", - "integrity": "sha512-ehJjmNPdguajc1hStvjN7DJNVjwG5LC1mgGMGFjCmdkn2fxB2GtULftMnlaqNmvMdPpqdaSoOFpl86VkLtG4pQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.2.tgz", + "integrity": "sha512-+gznPl8ip8P8HYHYecDtUtdsh1t2jvb+sWCD72GAiZ9m45RqwrLmReDaqdC0umQfamtFXVRoMVJ2/qINKGm9Tg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/figures": "^1.0.7", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.0", + "@inquirer/figures": "^1.0.8", + "@inquirer/type": "^3.0.1", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -1627,14 +1627,14 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.1.tgz", - "integrity": "sha512-6ycMm7k7NUApiMGfVc32yIPp28iPKxhGRMqoNDiUjq2RyTAkbs5Fx0TdzBqhabcKvniDdAAvHCmsRjnNfTsogw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.2.tgz", + "integrity": "sha512-KJLUHOaKnNCYzwVbryj3TNBxyZIrr56fR5N45v6K9IPrbT6B7DcudBMfylkV1A8PUdJE15mybkEQyp2/ZUpxUA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0" + "@inquirer/core": "^10.1.0", + "@inquirer/type": "^3.0.1" }, "engines": { "node": ">=18" @@ -1644,14 +1644,14 @@ } }, "node_modules/@inquirer/core": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.0.1.tgz", - "integrity": "sha512-KKTgjViBQUi3AAssqjUFMnMO3CM3qwCHvePV9EW+zTKGKafFGFF01sc1yOIYjLJ7QU52G/FbzKc+c01WLzXmVQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.0.tgz", + "integrity": "sha512-I+ETk2AL+yAVbvuKx5AJpQmoaWhpiTFOg/UJb7ZkMAK4blmtG8ATh5ct+T/8xNld0CZG/2UhtkdMwpgvld92XQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.7", - "@inquirer/type": "^3.0.0", + "@inquirer/figures": "^1.0.8", + "@inquirer/type": "^3.0.1", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -1665,14 +1665,14 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.0.1.tgz", - "integrity": "sha512-qAHHJ6hs343eNtCKgV2wV5CImFxYG8J1pl/YCeI5w9VoW7QpulRUU26+4NsMhjR6zDRjKBsH/rRjCIcaAOHsrg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.1.0.tgz", + "integrity": "sha512-K1gGWsxEqO23tVdp5MT3H799OZ4ER1za7Dlc8F4um0W7lwSv0KGR/YyrUEyimj0g7dXZd8XknM/5QA2/Uy+TbA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.0", + "@inquirer/type": "^3.0.1", "external-editor": "^3.1.0" }, "engines": { @@ -1683,14 +1683,14 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.1.tgz", - "integrity": "sha512-9anjpdc802YInXekwePsa5LWySzVMHbhVS6v6n5IJxrl8w09mODOeP69wZ1d0WrOvot2buQSmYp4lW/pq8y+zQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.2.tgz", + "integrity": "sha512-WdgCX1cUtinz+syKyZdJomovULYlKUWZbVYZzhf+ZeeYf4htAQ3jLymoNs3koIAKfZZl3HUBb819ClCBfyznaw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.0", + "@inquirer/type": "^3.0.1", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1701,9 +1701,9 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.7.tgz", - "integrity": "sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.8.tgz", + "integrity": "sha512-tKd+jsmhq21AP1LhexC0pPwsCxEhGgAkg28byjJAd+xhmIs8LUX8JbUc3vBf3PhLxWiB5EvyBE5X7JSPAqMAqg==", "dev": true, "license": "MIT", "engines": { @@ -1711,14 +1711,14 @@ } }, "node_modules/@inquirer/input": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.0.1.tgz", - "integrity": "sha512-m+SliZ2m43cDRIpAdQxfv5QOeAQCuhS8TGLvtzEP1An4IH1kBES4RLMRgE/fC+z29aN8qYG8Tq/eXQQKTYwqAg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.0.2.tgz", + "integrity": "sha512-yCLCraigU085EcdpIVEDgyfGv4vBiE4I+k1qRkc9C5dMjWF42ADMGy1RFU94+eZlz4YlkmFsiyHZy0W1wdhaNg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0" + "@inquirer/core": "^10.1.0", + "@inquirer/type": "^3.0.1" }, "engines": { "node": ">=18" @@ -1728,14 +1728,14 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.1.tgz", - "integrity": "sha512-gF3erqfm0snpwBjbyKXUUe17QJ7ebm49btXApajrM0rgCCoYX0o9W5NCuYNae87iPxaIJVjtuoQ42DX32IdbMA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.2.tgz", + "integrity": "sha512-MKQhYofdUNk7eqJtz52KvM1dH6R93OMrqHduXCvuefKrsiMjHiMwjc3NZw5Imm2nqY7gWd9xdhYrtcHMJQZUxA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0" + "@inquirer/core": "^10.1.0", + "@inquirer/type": "^3.0.1" }, "engines": { "node": ">=18" @@ -1745,14 +1745,14 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.1.tgz", - "integrity": "sha512-D7zUuX4l4ZpL3D7/SWu9ibijP09jigwHi/gfUHLx5GMS5oXzuMfPV2xPMG1tskco4enTx70HA0VtMXecerpvbg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.2.tgz", + "integrity": "sha512-tQXGSu7IO07gsYlGy3VgXRVsbOWqFBMbqAUrJSc1PDTQQ5Qdm+QVwkP0OC0jnUZ62D19iPgXOMO+tnWG+HhjNQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.0", + "@inquirer/type": "^3.0.1", "ansi-escapes": "^4.3.2" }, "engines": { @@ -1763,22 +1763,22 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.0.1.tgz", - "integrity": "sha512-cu2CpGC2hz7WTt2VBvdkzahDvYice6vYA/8Dm7Fy3tRNzKuQTF2EY3CV4H2GamveWE6tA2XzyXtbWX8+t4WMQg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.1.0.tgz", + "integrity": "sha512-5U/XiVRH2pp1X6gpNAjWOglMf38/Ys522ncEHIKT1voRUvSj/DQnR22OVxHnwu5S+rCFaUiPQ57JOtMFQayqYA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.0.1", - "@inquirer/confirm": "^5.0.1", - "@inquirer/editor": "^4.0.1", - "@inquirer/expand": "^4.0.1", - "@inquirer/input": "^4.0.1", - "@inquirer/number": "^3.0.1", - "@inquirer/password": "^4.0.1", - "@inquirer/rawlist": "^4.0.1", - "@inquirer/search": "^3.0.1", - "@inquirer/select": "^4.0.1" + "@inquirer/checkbox": "^4.0.2", + "@inquirer/confirm": "^5.0.2", + "@inquirer/editor": "^4.1.0", + "@inquirer/expand": "^4.0.2", + "@inquirer/input": "^4.0.2", + "@inquirer/number": "^3.0.2", + "@inquirer/password": "^4.0.2", + "@inquirer/rawlist": "^4.0.2", + "@inquirer/search": "^3.0.2", + "@inquirer/select": "^4.0.2" }, "engines": { "node": ">=18" @@ -1788,14 +1788,14 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.1.tgz", - "integrity": "sha512-0LuMOgaWs7W8JNcbiKkoFwyWFDEeCmLqDCygF0hidQUVa6J5grFVRZxrpompiWDFM49Km2rf7WoZwRo1uf1yWQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.2.tgz", + "integrity": "sha512-3XGcskMoVF8H0Dl1S5TSZ3rMPPBWXRcM0VeNVsS4ByWeWjSeb0lPqfnBg6N7T0608I1B2bSVnbi2cwCrmOD1Yw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.0", + "@inquirer/type": "^3.0.1", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1806,15 +1806,15 @@ } }, "node_modules/@inquirer/search": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.1.tgz", - "integrity": "sha512-ehMqjiO0pAf+KtdONKeCLVy4i3fy3feyRRhDrvzWhiwB8JccgKn7eHFr39l+Nx/FaZAhr0YxIJvkK5NuNvG+Ww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.2.tgz", + "integrity": "sha512-Zv4FC7w4dJ13BOJfKRQCICQfShinGjb1bCEIHxTSnjj2telu3+3RHwHubPG9HyD4aix5s+lyAMEK/wSFD75HLA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/figures": "^1.0.7", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.0", + "@inquirer/figures": "^1.0.8", + "@inquirer/type": "^3.0.1", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1825,15 +1825,15 @@ } }, "node_modules/@inquirer/select": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.1.tgz", - "integrity": "sha512-tVRatFRGU49bxFCKi/3P+C0E13KZduNFbWuHWRx0L2+jbiyKRpXgHp9qiRHWRk/KarhYBXzH/di6w3VQ5aJd5w==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.2.tgz", + "integrity": "sha512-uSWUzaSYAEj0hlzxa1mUB6VqrKaYx0QxGBLZzU4xWFxaSyGaXxsSE4OSOwdU24j0xl8OajgayqFXW0l2bkl2kg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/figures": "^1.0.7", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.0", + "@inquirer/figures": "^1.0.8", + "@inquirer/type": "^3.0.1", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -1845,9 +1845,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.1.tgz", + "integrity": "sha512-+ksJMIy92sOAiAccGpcKZUc3bYO07cADnscIxHBknEm3uNts3movSmBofc1908BNy5edKscxYeAdaX1NXkHS6A==", "dev": true, "license": "MIT", "engines": { @@ -2453,9 +2453,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.4.tgz", - "integrity": "sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.26.0.tgz", + "integrity": "sha512-gJNwtPDGEaOEgejbaseY6xMFu+CPltsc8/T+diUTTbOQLqD+bnrJq9ulH6WD69TqwqWmrfRAtUv30cCFZlbGTQ==", "cpu": [ "arm" ], @@ -2467,9 +2467,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.4.tgz", - "integrity": "sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.26.0.tgz", + "integrity": "sha512-YJa5Gy8mEZgz5JquFruhJODMq3lTHWLm1fOy+HIANquLzfIOzE9RA5ie3JjCdVb9r46qfAQY/l947V0zfGJ0OQ==", "cpu": [ "arm64" ], @@ -2481,9 +2481,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.4.tgz", - "integrity": "sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.26.0.tgz", + "integrity": "sha512-ErTASs8YKbqTBoPLp/kA1B1Um5YSom8QAc4rKhg7b9tyyVqDBlQxy7Bf2wW7yIlPGPg2UODDQcbkTlruPzDosw==", "cpu": [ "arm64" ], @@ -2495,9 +2495,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.4.tgz", - "integrity": "sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.26.0.tgz", + "integrity": "sha512-wbgkYDHcdWW+NqP2mnf2NOuEbOLzDblalrOWcPyY6+BRbVhliavon15UploG7PpBRQ2bZJnbmh8o3yLoBvDIHA==", "cpu": [ "x64" ], @@ -2509,9 +2509,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.4.tgz", - "integrity": "sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.26.0.tgz", + "integrity": "sha512-Y9vpjfp9CDkAG4q/uwuhZk96LP11fBz/bYdyg9oaHYhtGZp7NrbkQrj/66DYMMP2Yo/QPAsVHkV891KyO52fhg==", "cpu": [ "arm64" ], @@ -2523,9 +2523,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.4.tgz", - "integrity": "sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.26.0.tgz", + "integrity": "sha512-A/jvfCZ55EYPsqeaAt/yDAG4q5tt1ZboWMHEvKAH9Zl92DWvMIbnZe/f/eOXze65aJaaKbL+YeM0Hz4kLQvdwg==", "cpu": [ "x64" ], @@ -2537,9 +2537,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.4.tgz", - "integrity": "sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.26.0.tgz", + "integrity": "sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==", "cpu": [ "arm" ], @@ -2551,9 +2551,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.4.tgz", - "integrity": "sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.26.0.tgz", + "integrity": "sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==", "cpu": [ "arm" ], @@ -2565,9 +2565,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.4.tgz", - "integrity": "sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.26.0.tgz", + "integrity": "sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==", "cpu": [ "arm64" ], @@ -2579,9 +2579,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.4.tgz", - "integrity": "sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.26.0.tgz", + "integrity": "sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==", "cpu": [ "arm64" ], @@ -2593,9 +2593,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.4.tgz", - "integrity": "sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.26.0.tgz", + "integrity": "sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==", "cpu": [ "ppc64" ], @@ -2607,9 +2607,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.4.tgz", - "integrity": "sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.26.0.tgz", + "integrity": "sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==", "cpu": [ "riscv64" ], @@ -2621,9 +2621,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.4.tgz", - "integrity": "sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.26.0.tgz", + "integrity": "sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==", "cpu": [ "s390x" ], @@ -2635,9 +2635,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.4.tgz", - "integrity": "sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.26.0.tgz", + "integrity": "sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==", "cpu": [ "x64" ], @@ -2649,9 +2649,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.4.tgz", - "integrity": "sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.26.0.tgz", + "integrity": "sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==", "cpu": [ "x64" ], @@ -2663,9 +2663,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.4.tgz", - "integrity": "sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.26.0.tgz", + "integrity": "sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==", "cpu": [ "arm64" ], @@ -2677,9 +2677,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.4.tgz", - "integrity": "sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.26.0.tgz", + "integrity": "sha512-D4CxkazFKBfN1akAIY6ieyOqzoOoBV1OICxgUblWxff/pSjCA2khXlASUx7mK6W1oP4McqhgcCsu6QaLj3WMWg==", "cpu": [ "ia32" ], @@ -2691,9 +2691,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz", - "integrity": "sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.26.0.tgz", + "integrity": "sha512-2x8MO1rm4PGEP0xWbubJW5RtbNLk3puzAMaLQd3B3JHVw4KcHlmXcO+Wewx9zCoo7EUFiMlu/aZbCJ7VjMzAag==", "cpu": [ "x64" ], @@ -3329,17 +3329,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", - "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz", + "integrity": "sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/type-utils": "8.13.0", - "@typescript-eslint/utils": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/type-utils": "8.14.0", + "@typescript-eslint/utils": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3363,16 +3363,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz", - "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.14.0.tgz", + "integrity": "sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/typescript-estree": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", "debug": "^4.3.4" }, "engines": { @@ -3392,14 +3392,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", - "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz", + "integrity": "sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0" + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3410,14 +3410,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz", - "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz", + "integrity": "sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/utils": "8.13.0", + "@typescript-eslint/typescript-estree": "8.14.0", + "@typescript-eslint/utils": "8.14.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -3435,9 +3435,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", - "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.14.0.tgz", + "integrity": "sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==", "dev": true, "license": "MIT", "engines": { @@ -3449,14 +3449,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", - "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz", + "integrity": "sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3478,16 +3478,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", - "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.14.0.tgz", + "integrity": "sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0" + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/typescript-estree": "8.14.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3501,13 +3501,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", - "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz", + "integrity": "sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/types": "8.14.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -3552,9 +3552,9 @@ "license": "ISC" }, "node_modules/@vitejs/plugin-vue": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", - "integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.0.tgz", + "integrity": "sha512-7n7KdUEtx/7Yl7I/WVAMZ1bEb0eVvXF3ummWTeLcs/9gvo9pJhuLdouSXGjdZ/MKD1acf1I272+X0RMua4/R3g==", "dev": true, "license": "MIT", "engines": { @@ -3639,9 +3639,9 @@ } }, "node_modules/@vscode/emmet-helper": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.9.3.tgz", - "integrity": "sha512-rB39LHWWPQYYlYfpv9qCoZOVioPCftKXXqrsyqN1mTWZM6dTnONT63Db+03vgrBbHzJN45IrgS/AGxw9iiqfEw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.10.0.tgz", + "integrity": "sha512-UHw1EQRgLbSYkyB73/7wR/IzV6zTBnbzEHuuU4Z6b95HKf2lmeTdGwBIwspWBSRrnIA1TI2x2tetBym6ErA7Gw==", "dev": true, "license": "MIT", "dependencies": { @@ -3649,16 +3649,9 @@ "jsonc-parser": "^2.3.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.15.1", - "vscode-uri": "^2.1.2" + "vscode-uri": "^3.0.8" } }, - "node_modules/@vscode/emmet-helper/node_modules/vscode-uri": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", - "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==", - "dev": true, - "license": "MIT" - }, "node_modules/@vscode/l10n": { "version": "0.0.18", "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", @@ -3739,13 +3732,13 @@ "license": "MIT" }, "node_modules/@vue/devtools-kit": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.6.3.tgz", - "integrity": "sha512-ETsFc8GlOp04rSFN79tB2TpVloWfsSx9BoCSElV3w3CaJTSBfz42KsIi5Ka+dNTJs1jY7QVLTDeoBmUGgA9h2A==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.6.4.tgz", + "integrity": "sha512-Zs86qIXXM9icU0PiGY09PQCle4TI750IPLmAJzW5Kf9n9t5HzSYf6Rz6fyzSwmfMPiR51SUKJh9sXVZu78h2QA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^7.6.3", + "@vue/devtools-shared": "^7.6.4", "birpc": "^0.2.19", "hookable": "^5.5.3", "mitt": "^3.0.1", @@ -3755,9 +3748,9 @@ } }, "node_modules/@vue/devtools-shared": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.6.3.tgz", - "integrity": "sha512-wJW5QF27i16+sNQIaes8QoEZg1eqEgF83GkiPUlEQe9k7ZoHXHV7PRrnrxOKem42sIHPU813J2V/ZK1uqTJe6g==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.6.4.tgz", + "integrity": "sha512-nD6CUvBEel+y7zpyorjiUocy0nh77DThZJ0k1GRnJeOmY3ATq2fWijEp7wk37gb023Cb0R396uYh5qMSBQ5WFg==", "dev": true, "license": "MIT", "dependencies": { @@ -4172,34 +4165,34 @@ } }, "node_modules/algoliasearch": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.13.0.tgz", - "integrity": "sha512-04lyQX3Ev/oLYQx+aagamQDXvkUUfX1mwrLrus15+9fNaYj28GDxxEzbwaRfvmHFcZyoxvup7mMtDTTw8SrTEQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.14.0.tgz", + "integrity": "sha512-qr21NtvIkpptwj9z6W5guICK8yijXIGzw7Ka26zAPofnefofVXoXtuAopjtmk1ZKDu4YpACj38n9mgKKc5Zuhw==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-abtesting": "5.13.0", - "@algolia/client-analytics": "5.13.0", - "@algolia/client-common": "5.13.0", - "@algolia/client-insights": "5.13.0", - "@algolia/client-personalization": "5.13.0", - "@algolia/client-query-suggestions": "5.13.0", - "@algolia/client-search": "5.13.0", - "@algolia/ingestion": "1.13.0", - "@algolia/monitoring": "1.13.0", - "@algolia/recommend": "5.13.0", - "@algolia/requester-browser-xhr": "5.13.0", - "@algolia/requester-fetch": "5.13.0", - "@algolia/requester-node-http": "5.13.0" + "@algolia/client-abtesting": "5.14.0", + "@algolia/client-analytics": "5.14.0", + "@algolia/client-common": "5.14.0", + "@algolia/client-insights": "5.14.0", + "@algolia/client-personalization": "5.14.0", + "@algolia/client-query-suggestions": "5.14.0", + "@algolia/client-search": "5.14.0", + "@algolia/ingestion": "1.14.0", + "@algolia/monitoring": "1.14.0", + "@algolia/recommend": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/alien-signals": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.0.tgz", - "integrity": "sha512-StlonZhBBrsPPwrDjiPAiVTf/rolxffLxVPT60Qv/t88BZ81BvUVzHgGqEFvJ1ii8HXtm1+zU2Icr59tfWEcag==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.2.tgz", + "integrity": "sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==", "dev": true, "license": "MIT" }, @@ -4801,9 +4794,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001679", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz", - "integrity": "sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==", + "version": "1.0.30001680", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", + "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", "dev": true, "funding": [ { @@ -4975,9 +4968,9 @@ } }, "node_modules/ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", "dev": true, "funding": [ { @@ -6278,9 +6271,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1354347", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1354347.tgz", - "integrity": "sha512-BlmkSqV0V84E2WnEnoPnwyix57rQxAM5SKJjf4TbYOCGLAWtz8CDH8RIaGOjPgPCXo2Mce3kxSY497OySidY3Q==", + "version": "0.0.1367902", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", + "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", "dev": true, "license": "BSD-3-Clause", "peer": true @@ -6313,9 +6306,9 @@ "license": "(MPL-2.0 OR Apache-2.0)" }, "node_modules/electron-to-chromium": { - "version": "1.5.55", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", - "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==", + "version": "1.5.59", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.59.tgz", + "integrity": "sha512-faAXB6+gEbC8FsiRdpOXgOe4snP49YwjiXynEB8Mp7sUx80W5eN+BnnBHJ/F7eIeLzs+QBfDD40bJMm97oEFcw==", "dev": true, "license": "ISC" }, @@ -6692,9 +6685,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.30.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.30.0.tgz", - "integrity": "sha512-CyqlRgShvljFkOeYK8wN5frh/OGTvkj1S7wlr2Q2pUvwq+X5VYiLd6ZjujpgSgLnys2W8qrBLkXQ41SUYaoPIQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.31.0.tgz", + "integrity": "sha512-aYMUCgivhz1o4tLkRHj5oq9YgYPM4/EJc0M7TAKRLCUA5OYxRLAhYEVD2nLtTwLyixEFI+/QXSvKU9ESZFgqjQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7291,9 +7284,9 @@ } }, "node_modules/focus-trap": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.0.tgz", - "integrity": "sha512-1td0l3pMkWJLFipobUcGaf+5DTY4PLDDrcqoSaKP8ediO/CoWCCYk/fT/Y2A4e6TNB+Sh6clRJCjOPPnKoNHnQ==", + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.1.tgz", + "integrity": "sha512-nB8y4nQl8PshahLpGKZOq1sb0xrMVFSn6at7u/qOsBZTlZRzaapISGENcB6mOkoezbClZyiMwEF/dGY8AZ00rA==", "dev": true, "license": "MIT", "dependencies": { @@ -8220,13 +8213,13 @@ } }, "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "*" + "@types/estree": "^1.0.6" } }, "node_modules/is-regex": { @@ -9456,9 +9449,9 @@ } }, "node_modules/micromark": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", - "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", "dev": true, "funding": [ { @@ -9492,9 +9485,9 @@ } }, "node_modules/micromark-core-commonmark": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", - "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", "dev": true, "funding": [ { @@ -9527,9 +9520,9 @@ } }, "node_modules/micromark-factory-destination": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", - "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", "dev": true, "funding": [ { @@ -9549,9 +9542,9 @@ } }, "node_modules/micromark-factory-label": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", - "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", "dev": true, "funding": [ { @@ -9572,9 +9565,9 @@ } }, "node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", "dev": true, "funding": [ { @@ -9593,9 +9586,9 @@ } }, "node_modules/micromark-factory-title": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", - "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", "dev": true, "funding": [ { @@ -9616,9 +9609,9 @@ } }, "node_modules/micromark-factory-whitespace": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", - "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", "dev": true, "funding": [ { @@ -9639,9 +9632,9 @@ } }, "node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "dev": true, "funding": [ { @@ -9660,9 +9653,9 @@ } }, "node_modules/micromark-util-chunked": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", - "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", "dev": true, "funding": [ { @@ -9680,9 +9673,9 @@ } }, "node_modules/micromark-util-classify-character": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", - "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", "dev": true, "funding": [ { @@ -9702,9 +9695,9 @@ } }, "node_modules/micromark-util-combine-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", - "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", "dev": true, "funding": [ { @@ -9723,9 +9716,9 @@ } }, "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", - "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", "dev": true, "funding": [ { @@ -9743,9 +9736,9 @@ } }, "node_modules/micromark-util-decode-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", - "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", "dev": true, "funding": [ { @@ -9766,9 +9759,9 @@ } }, "node_modules/micromark-util-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "dev": true, "funding": [ { @@ -9783,9 +9776,9 @@ "license": "MIT" }, "node_modules/micromark-util-html-tag-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", - "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", "dev": true, "funding": [ { @@ -9800,9 +9793,9 @@ "license": "MIT" }, "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", - "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", "dev": true, "funding": [ { @@ -9820,9 +9813,9 @@ } }, "node_modules/micromark-util-resolve-all": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", - "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", "dev": true, "funding": [ { @@ -9840,9 +9833,9 @@ } }, "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", - "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", "dev": true, "funding": [ { @@ -9862,9 +9855,9 @@ } }, "node_modules/micromark-util-subtokenize": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", - "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.2.tgz", + "integrity": "sha512-xKxhkB62vwHUuuxHe9Xqty3UaAsizV2YKq5OV344u3hFBbf8zIYrhYOWhAQb94MtMPkjTOzzjJ/hid9/dR5vFA==", "dev": true, "funding": [ { @@ -9885,9 +9878,9 @@ } }, "node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "dev": true, "funding": [ { @@ -9902,9 +9895,9 @@ "license": "MIT" }, "node_modules/micromark-util-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", "dev": true, "funding": [ { @@ -10102,15 +10095,15 @@ } }, "node_modules/mlly": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz", - "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz", + "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.12.1", + "acorn": "^8.14.0", "pathe": "^1.1.2", - "pkg-types": "^1.2.0", + "pkg-types": "^1.2.1", "ufo": "^1.5.4" } }, @@ -11379,9 +11372,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -11400,7 +11393,7 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -11518,9 +11511,9 @@ } }, "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -11651,9 +11644,9 @@ } }, "node_modules/puppeteer": { - "version": "23.7.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.7.1.tgz", - "integrity": "sha512-jS6XehagMvxQ12etwY/4EOYZ0Sm8GAsrtGhdQn4AqpJAyHc3RYl7tGd4QYh/MmShDw8sF9FWYQqGidhoXaqokQ==", + "version": "23.8.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.8.0.tgz", + "integrity": "sha512-MFWDMWoCcOpwNwQIjA9gPKWrEUbj8bLCzkK56w5lZPMUT6wK4FfpgOEPxKffVmXEMYMZzgcjxzqy15b/Q1ibaw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -11662,8 +11655,8 @@ "@puppeteer/browsers": "2.4.1", "chromium-bidi": "0.8.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1354347", - "puppeteer-core": "23.7.1", + "devtools-protocol": "0.0.1367902", + "puppeteer-core": "23.8.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -11674,9 +11667,9 @@ } }, "node_modules/puppeteer-core": { - "version": "23.7.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.7.1.tgz", - "integrity": "sha512-Om/qCZhd+HLoAr7GltrRAZpS3uOXwHu7tXAoDbNcJADHjG2zeAlDArgyIPXYGG4QB/EQUHk13Q6RklNxGM73Pg==", + "version": "23.8.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.8.0.tgz", + "integrity": "sha512-c2ymGN2M//We7pC+JhP2dE/g4+qnT89BO+EMSZyJmecN3DN6RNqErA7eH7DrWoNIcU75r2nP4VHa4pswAL6NVg==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -11684,7 +11677,7 @@ "@puppeteer/browsers": "2.4.1", "chromium-bidi": "0.8.0", "debug": "^4.3.7", - "devtools-protocol": "0.0.1354347", + "devtools-protocol": "0.0.1367902", "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, @@ -12224,9 +12217,9 @@ "license": "Unlicense" }, "node_modules/rollup": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.4.tgz", - "integrity": "sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.26.0.tgz", + "integrity": "sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==", "dev": true, "license": "MIT", "dependencies": { @@ -12240,24 +12233,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.4", - "@rollup/rollup-android-arm64": "4.24.4", - "@rollup/rollup-darwin-arm64": "4.24.4", - "@rollup/rollup-darwin-x64": "4.24.4", - "@rollup/rollup-freebsd-arm64": "4.24.4", - "@rollup/rollup-freebsd-x64": "4.24.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.4", - "@rollup/rollup-linux-arm-musleabihf": "4.24.4", - "@rollup/rollup-linux-arm64-gnu": "4.24.4", - "@rollup/rollup-linux-arm64-musl": "4.24.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.4", - "@rollup/rollup-linux-riscv64-gnu": "4.24.4", - "@rollup/rollup-linux-s390x-gnu": "4.24.4", - "@rollup/rollup-linux-x64-gnu": "4.24.4", - "@rollup/rollup-linux-x64-musl": "4.24.4", - "@rollup/rollup-win32-arm64-msvc": "4.24.4", - "@rollup/rollup-win32-ia32-msvc": "4.24.4", - "@rollup/rollup-win32-x64-msvc": "4.24.4", + "@rollup/rollup-android-arm-eabi": "4.26.0", + "@rollup/rollup-android-arm64": "4.26.0", + "@rollup/rollup-darwin-arm64": "4.26.0", + "@rollup/rollup-darwin-x64": "4.26.0", + "@rollup/rollup-freebsd-arm64": "4.26.0", + "@rollup/rollup-freebsd-x64": "4.26.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.26.0", + "@rollup/rollup-linux-arm-musleabihf": "4.26.0", + "@rollup/rollup-linux-arm64-gnu": "4.26.0", + "@rollup/rollup-linux-arm64-musl": "4.26.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.26.0", + "@rollup/rollup-linux-riscv64-gnu": "4.26.0", + "@rollup/rollup-linux-s390x-gnu": "4.26.0", + "@rollup/rollup-linux-x64-gnu": "4.26.0", + "@rollup/rollup-linux-x64-musl": "4.26.0", + "@rollup/rollup-win32-arm64-msvc": "4.26.0", + "@rollup/rollup-win32-ia32-msvc": "4.26.0", + "@rollup/rollup-win32-x64-msvc": "4.26.0", "fsevents": "~2.3.2" } }, @@ -12920,9 +12913,9 @@ "peer": true }, "node_modules/streamx": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", - "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "version": "2.20.2", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.2.tgz", + "integrity": "sha512-aDGDLU+j9tJcUdPGOaHmVF1u/hhI+CsGkT02V3OKlHDV7IukOI+nTWAGkiZEKCO35rWN1wIr4tS7YFr1f4qSvA==", "dev": true, "license": "MIT", "peer": true, @@ -13559,15 +13552,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.13.0.tgz", - "integrity": "sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.14.0.tgz", + "integrity": "sha512-K8fBJHxVL3kxMmwByvz8hNdBJ8a0YqKzKDX6jRlrjMuNXyd5T2V02HIq37+OiWXvUUOXgOOGiSSOh26Mh8pC3w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.13.0", - "@typescript-eslint/parser": "8.13.0", - "@typescript-eslint/utils": "8.13.0" + "@typescript-eslint/eslint-plugin": "8.14.0", + "@typescript-eslint/parser": "8.14.0", + "@typescript-eslint/utils": "8.14.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -13861,9 +13854,9 @@ } }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -13963,13 +13956,13 @@ } }, "node_modules/vitepress/node_modules/@vue/devtools-api": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.6.3.tgz", - "integrity": "sha512-H2TRzFA9hNezdtM6I0y3RCMhIg5T3gib/p9qI2IAS8gB9tvkAv4JZHAZZl5BZHhO7btuHkvHzU5qpO/vdsjYMg==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.6.4.tgz", + "integrity": "sha512-5AaJ5ELBIuevmFMZYYLuOO9HUuY/6OlkOELHE7oeDhy4XD/hSODIzktlsvBOsn+bto3aD0psj36LGzwVu5Ip8w==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-kit": "^7.6.3" + "@vue/devtools-kit": "^7.6.4" } }, "node_modules/volar-service-css": { diff --git a/package.json b/package.json index 452408292..833f2cde6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "4.26.0", + "version": "4.27.2", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", @@ -124,7 +124,7 @@ "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.34.2", "@eslint/js": "^9.14.0", - "@inquirer/prompts": "^7.0.1", + "@inquirer/prompts": "^7.1.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@mermaid-js/mermaid-cli": "^11.4.0", "@napi-rs/cli": "^2.18.4", @@ -160,7 +160,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-unicorn": "^56.0.0", - "eslint-plugin-vue": "^9.30.0", + "eslint-plugin-vue": "^9.31.0", "fixturify": "^3.0.0", "flru": "^1.0.2", "fs-extra": "^11.2.0", @@ -181,7 +181,7 @@ "pretty-bytes": "^6.1.1", "pretty-ms": "^9.1.0", "requirejs": "^2.3.7", - "rollup": "^4.24.4", + "rollup": "^4.25.0", "rollup-plugin-license": "^3.5.3", "rollup-plugin-string": "^3.0.0", "semver": "^7.6.3", @@ -193,8 +193,8 @@ "terser": "^5.36.0", "tslib": "^2.8.1", "typescript": "^5.6.3", - "typescript-eslint": "^8.13.0", - "vite": "^5.4.10", + "typescript-eslint": "^8.14.0", + "vite": "^5.4.11", "vitepress": "^1.5.0", "vue": "^3.5.12", "vue-tsc": "^2.1.10", diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 941b87b67..f70e211dd 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -26,9 +26,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "anyhow" @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.36" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "shlex", ] @@ -796,9 +796,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] @@ -882,9 +882,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -959,18 +959,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -1029,9 +1029,9 @@ dependencies = [ [[package]] name = "sourcemap" -version = "9.0.1" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86721155737d89818020dc35713595c6e7b21016073c101e6590c47ca58c2d16" +checksum = "4d146f02f4bbbabbbe3da0f9cd3ea2ab779defc4ed1f070b5bd83ea48ed78811" dependencies = [ "base64-simd", "bitvec", @@ -1124,9 +1124,9 @@ dependencies = [ [[package]] name = "swc_common" -version = "3.0.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "992b89cdcff8e61c1308a984af5450a60a382b106f3e79fd6aabf9e2e193d076" +checksum = "053e784870430ba47043278626e75686e745ac16876a8f5f4d6c9f39354ee7e7" dependencies = [ "ahash", "ast_node", @@ -1153,9 +1153,9 @@ dependencies = [ [[package]] name = "swc_compiler_base" -version = "4.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990ad90a98e1e72af29ce25ed72e12e61d4a06c64725e6fa63c9d7291a9f1e27" +checksum = "93642202236e85434c36ec37daee144d4faf05d5495a4187228f9b03e6b4db88" dependencies = [ "anyhow", "base64", @@ -1206,9 +1206,9 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "3.0.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e7c0cd9dfe2a49c8f0b4ce699c13c9e270b8487a0176e1d89e5a9a586d0b3b" +checksum = "1bdab7759509c1b37ec77bd9fc231f525b888d9609c2963ce71995da1b27357c" dependencies = [ "bitflags", "is-macro", @@ -1225,9 +1225,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "3.0.0" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09236707a86e5d9f24c58e46c7f0efcc728daf1dd48167b2071f7afc11b7ea67" +checksum = "e474f6c2671524dbb179b44a36425cb1a58928f0f7211c45043f0951a1842c5d" dependencies = [ "memchr", "num-bigint", @@ -1257,9 +1257,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "4.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef4de55c8a57d10a2683157b4d17301482518a07fd15c3d71000fe0d1b99328" +checksum = "ba0fa4819b1353cbe5e15fabbc1618e0da6c51404214042457d3dd7a60e14960" dependencies = [ "arrayvec", "indexmap", @@ -1292,9 +1292,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "4.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49f6ab5fa19498d0feb45a4943e1ad962736ee251e8f0f885330f7aeca39c39" +checksum = "54c5ab8bd4cc4a4956514699c84d1a25cdb5a33f5ec760ec64ce712e973019c9" dependencies = [ "either", "new_debug_unreachable", @@ -1314,9 +1314,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "4.0.1" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f43d1983d48dca819a7d0c79c5eb98011a2f8759acbddd972858ec228c66d4" +checksum = "0eb4000822f02b54af0be4f668649fa1e5555f1e3392479d17a277eb81a841f0" dependencies = [ "better_scoped_tls", "bitflags", @@ -1349,9 +1349,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "4.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30019eed0d2bf516f08216c87c89e372c91750c1bae8a3254335c5f6ad044852" +checksum = "f63d691ccea03a8eb25f37c7498e7609ad76ca3dc2070b630596e49f0b8fd1f4" dependencies = [ "dashmap", "indexmap", @@ -1373,9 +1373,9 @@ dependencies = [ [[package]] name = "swc_ecma_usage_analyzer" -version = "4.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c31dad1d402a52394eb184742a7095ba02277c55df327b89fb8bd65e59a7c" +checksum = "89892c33cf84806957c34539cb84a26c69f6d2c7c8d9ae3131113105852f1d60" dependencies = [ "indexmap", "rustc-hash", @@ -1390,9 +1390,9 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "4.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9371e7e39fca55508ae91abf28fd3d8dae8eff3782e918081f6932523c68789c" +checksum = "024a9ee9a19f448b31af002b90c43b9dfdb4e1fad23c76c21fe26a7c6e0f78a7" dependencies = [ "indexmap", "num_cpus", @@ -1409,9 +1409,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "3.0.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a380252c317c67f321b8e0d66dbc2427842bd184505e12016f0d3f811776af86" +checksum = "642c58202491c273ea984e0d7e923319afe0f94195d2985b3e7f71f7d8232e06" dependencies = [ "new_debug_unreachable", "num-bigint", @@ -1435,9 +1435,9 @@ dependencies = [ [[package]] name = "swc_fast_graph" -version = "4.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1b3add3571bc073f49b4f4fb4326f54c24fe7799296e0ad5341af895150e79c" +checksum = "3f65856acf41991a43d47d19ca947ee34f1152fccc42f048063c64eaf45a8e26" dependencies = [ "indexmap", "petgraph", diff --git a/rust/parse_ast/Cargo.toml b/rust/parse_ast/Cargo.toml index 74d60e1a6..bf9c95170 100644 --- a/rust/parse_ast/Cargo.toml +++ b/rust/parse_ast/Cargo.toml @@ -6,10 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0.92" +anyhow = "1.0.93" swc_atoms = "2.0.0" -swc_compiler_base = "4.0.0" -swc_common = { version = "3.0.0", features = ["ahash", "parking_lot"] } -swc_ecma_ast = "3.0.0" -swc_ecma_parser = "4.0.0" +swc_compiler_base = "5.0.0" +swc_common = { version = "4.0.0", features = ["ahash", "parking_lot"] } +swc_ecma_ast = "4.0.1" +swc_ecma_parser = "5.0.0" parking_lot = "0.12.3" diff --git a/scripts/ast-types.js b/scripts/ast-types.js index d69c45d9d..831cfc556 100644 --- a/scripts/ast-types.js +++ b/scripts/ast-types.js @@ -88,7 +88,7 @@ export const AST_NODES = { 'parameters', `scope.addParameterVariables( parameters.map( - parameter => parameter.declare('parameter', UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => parameter.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] ), parameters[parameters.length - 1] instanceof RestElement );` @@ -153,7 +153,7 @@ export const AST_NODES = { ['body', 'Node'] ], postProcessFields: { - param: ['parameter', "parameter?.declare('parameter', UNKNOWN_EXPRESSION)"] + param: ['parameter', "parameter?.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION)"] }, scopes: { body: 'scope.bodyScope' @@ -310,7 +310,7 @@ export const AST_NODES = { 'parameters', `scope.addParameterVariables( parameters.map( - parameter => parameter.declare('parameter', UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => parameter.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] ), parameters[parameters.length - 1] instanceof RestElement );` diff --git a/scripts/generate-buffer-parsers.js b/scripts/generate-buffer-parsers.js index 801079f5b..051151e05 100644 --- a/scripts/generate-buffer-parsers.js +++ b/scripts/generate-buffer-parsers.js @@ -178,6 +178,7 @@ import type { Node, NodeBase } from './nodes/shared/Node'; import type ChildScope from './scopes/ChildScope'; import type ModuleScope from './scopes/ModuleScope'; import TrackingScope from './scopes/TrackingScope'; +import { EMPTY_PATH } from './utils/PathTracker'; import type ParameterVariable from './variables/ParameterVariable'; export function convertProgram( diff --git a/scripts/prepare-release.js b/scripts/prepare-release.js index 87eb2834c..720bcd68e 100755 --- a/scripts/prepare-release.js +++ b/scripts/prepare-release.js @@ -192,10 +192,8 @@ function getDummyLogSection(headline, pr) { * @return {Promise} */ async function installDependenciesAndLint() { - await Promise.all([ - runWithEcho('npm', ['ci', '--ignore-scripts']), - runWithEcho('npm', ['run', 'check-audit']) - ]); + await runWithEcho('npm', ['ci', '--ignore-scripts']); + await runWithEcho('npm', ['run', 'check-audit']); await runWithEcho('npm', ['run', 'ci:lint']); } diff --git a/src/Graph.ts b/src/Graph.ts index 9e9f22559..e940c3639 100644 --- a/src/Graph.ts +++ b/src/Graph.ts @@ -1,6 +1,6 @@ import flru from 'flru'; import GlobalScope from './ast/scopes/GlobalScope'; -import { PathTracker } from './ast/utils/PathTracker'; +import { EntityPathTracker } from './ast/utils/PathTracker'; import type ExternalModule from './ExternalModule'; import Module from './Module'; import { ModuleLoader, type UnresolvedModule } from './ModuleLoader'; @@ -54,7 +54,7 @@ function normalizeEntryModules( export default class Graph { readonly astLru = flru(5); readonly cachedModules = new Map(); - readonly deoptimizationTracker = new PathTracker(); + readonly deoptimizationTracker = new EntityPathTracker(); entryModules: Module[] = []; readonly fileOperationQueue: Queue; readonly moduleLoader: ModuleLoader; diff --git a/src/Module.ts b/src/Module.ts index d8c7cddee..fbc96ae57 100644 --- a/src/Module.ts +++ b/src/Module.ts @@ -20,7 +20,12 @@ import type Program from './ast/nodes/Program'; import type { NodeBase } from './ast/nodes/shared/Node'; import VariableDeclaration from './ast/nodes/VariableDeclaration'; import ModuleScope from './ast/scopes/ModuleScope'; -import { type PathTracker, UNKNOWN_PATH } from './ast/utils/PathTracker'; +import { + EMPTY_PATH, + type EntityPathTracker, + type ObjectPath, + UNKNOWN_PATH +} from './ast/utils/PathTracker'; import ExportDefaultVariable from './ast/variables/ExportDefaultVariable'; import ExportShimVariable from './ast/variables/ExportShimVariable'; import ExternalVariable from './ast/variables/ExternalVariable'; @@ -116,7 +121,7 @@ export interface AstContext { addImportMeta: (node: MetaProperty) => void; addImportSource: (importSource: string) => void; code: string; - deoptimizationTracker: PathTracker; + deoptimizationTracker: EntityPathTracker; error: (properties: RollupLog, pos: number) => never; fileName: string; getExports: () => string[]; @@ -128,7 +133,7 @@ export interface AstContext { importDescriptions: Map; includeAllExports: () => void; includeDynamicImport: (node: ImportExpression) => void; - includeVariableInModule: (variable: Variable) => void; + includeVariableInModule: (variable: Variable, path: ObjectPath) => void; log: (level: LogLevel, properties: RollupLog, pos: number) => void; magicString: MagicString; manualPureFunctions: PureFunctions; @@ -699,7 +704,7 @@ export default class Module { include(): void { const context = createInclusionContext(); - if (this.ast!.shouldBeIncluded(context)) this.ast!.include(context, false); + if (this.ast!.shouldBeIncluded(context)) this.ast!.includePath(EMPTY_PATH, context, false); } includeAllExports(includeNamespaceMembers: boolean): void { @@ -715,9 +720,7 @@ export default class Module { return error(logMissingEntryExport(exportName, this.id)); } variable.deoptimizePath(UNKNOWN_PATH); - if (!variable.included) { - this.includeVariable(variable); - } + this.includeVariable(variable, UNKNOWN_PATH); } } @@ -726,7 +729,7 @@ export default class Module { if (variable) { variable.deoptimizePath(UNKNOWN_PATH); if (!variable.included) { - this.includeVariable(variable); + this.includeVariable(variable, UNKNOWN_PATH); } if (variable instanceof ExternalVariable) { variable.module.reexported = true; @@ -740,7 +743,7 @@ export default class Module { } includeAllInBundle(): void { - this.ast!.include(createInclusionContext(), true); + this.ast!.includePath(UNKNOWN_PATH, createInclusionContext(), true); this.includeAllExports(false); } @@ -757,7 +760,7 @@ export default class Module { if (variable) { variable.deoptimizePath(UNKNOWN_PATH); if (!variable.included) { - this.includeVariable(variable); + this.includeVariable(variable, UNKNOWN_PATH); } } @@ -1336,12 +1339,12 @@ export default class Module { for (const module of [this, ...this.exportAllModules]) { if (module instanceof ExternalModule) { const [externalVariable] = module.getVariableForExportName('*'); - externalVariable.include(); + externalVariable.includePath(UNKNOWN_PATH, createInclusionContext()); this.includedImports.add(externalVariable); externalNamespaces.add(externalVariable); } else if (module.info.syntheticNamedExports) { const syntheticNamespace = module.getSyntheticNamespace(); - syntheticNamespace.include(); + syntheticNamespace.includePath(UNKNOWN_PATH, createInclusionContext()); this.includedImports.add(syntheticNamespace); syntheticNamespaces.add(syntheticNamespace); } @@ -1350,14 +1353,14 @@ export default class Module { } private includeDynamicImport(node: ImportExpression): void { - const resolution = ( - this.dynamicImports.find(dynamicImport => dynamicImport.node === node) as { - resolution: string | Module | ExternalModule | undefined; - } - ).resolution; + const resolution = this.dynamicImports.find( + dynamicImport => dynamicImport.node === node + )!.resolution; if (resolution instanceof Module) { - resolution.includedDynamicImporters.push(this); + if (!resolution.includedDynamicImporters.includes(this)) { + resolution.includedDynamicImporters.push(this); + } const importedNames = this.options.treeshake ? node.getDeterministicImportedNames() @@ -1371,14 +1374,13 @@ export default class Module { } } - private includeVariable(variable: Variable): void { + private includeVariable(variable: Variable, path: ObjectPath): void { const variableModule = variable.module; if (variable.included) { if (variableModule instanceof Module && variableModule !== this) { getAndExtendSideEffectModules(variable, this); } } else { - variable.include(); this.graph.needsTreeshakingPass = true; if (variableModule instanceof Module) { if (!variableModule.isExecuted) { @@ -1394,10 +1396,11 @@ export default class Module { } } } + variable.includePath(path, createInclusionContext()); } - private includeVariableInModule(variable: Variable): void { - this.includeVariable(variable); + private includeVariableInModule(variable: Variable, path: ObjectPath): void { + this.includeVariable(variable, path); const variableModule = variable.module; if (variableModule && variableModule !== this) { this.includedImports.add(variable); diff --git a/src/ast/ExecutionContext.ts b/src/ast/ExecutionContext.ts index 3906fb7b9..df7bb3637 100644 --- a/src/ast/ExecutionContext.ts +++ b/src/ast/ExecutionContext.ts @@ -1,6 +1,6 @@ import type { Entity } from './Entity'; import type { ExpressionEntity } from './nodes/shared/Expression'; -import { DiscriminatedPathTracker, PathTracker } from './utils/PathTracker'; +import { DiscriminatedPathTracker, EntityPathTracker } from './utils/PathTracker'; import type ThisVariable from './variables/ThisVariable'; interface ExecutionContextIgnore { @@ -23,8 +23,8 @@ export interface InclusionContext extends ControlFlowContext { } export interface HasEffectsContext extends ControlFlowContext { - accessed: PathTracker; - assigned: PathTracker; + accessed: EntityPathTracker; + assigned: EntityPathTracker; brokenFlow: boolean; called: DiscriminatedPathTracker; ignore: ExecutionContextIgnore; @@ -44,8 +44,8 @@ export function createInclusionContext(): InclusionContext { export function createHasEffectsContext(): HasEffectsContext { return { - accessed: new PathTracker(), - assigned: new PathTracker(), + accessed: new EntityPathTracker(), + assigned: new EntityPathTracker(), brokenFlow: false, called: new DiscriminatedPathTracker(), hasBreak: false, diff --git a/src/ast/bufferParsers.ts b/src/ast/bufferParsers.ts index 8a7b8128f..385610ed3 100644 --- a/src/ast/bufferParsers.ts +++ b/src/ast/bufferParsers.ts @@ -103,6 +103,7 @@ import type { Node, NodeBase } from './nodes/shared/Node'; import type ChildScope from './scopes/ChildScope'; import type ModuleScope from './scopes/ModuleScope'; import TrackingScope from './scopes/TrackingScope'; +import { EMPTY_PATH } from './utils/PathTracker'; import type ParameterVariable from './variables/ParameterVariable'; export function convertProgram( @@ -335,7 +336,8 @@ const bufferParsers: ((node: any, position: number, buffer: AstBuffer) => void)[ const parameters = (node.params = convertNodeList(node, scope, buffer[position + 2], buffer)); scope.addParameterVariables( parameters.map( - parameter => parameter.declare('parameter', UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => + parameter.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] ), parameters[parameters.length - 1] instanceof RestElement ); @@ -384,7 +386,7 @@ const bufferParsers: ((node: any, position: number, buffer: AstBuffer) => void)[ const parameterPosition = buffer[position]; const parameter = (node.param = parameterPosition === 0 ? null : convertNode(node, scope, parameterPosition, buffer)); - parameter?.declare('parameter', UNKNOWN_EXPRESSION); + parameter?.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION); node.body = convertNode(node, scope.bodyScope, buffer[position + 1], buffer); }, function chainExpression(node: ChainExpression, position, buffer) { @@ -528,7 +530,8 @@ const bufferParsers: ((node: any, position: number, buffer: AstBuffer) => void)[ const parameters = (node.params = convertNodeList(node, scope, buffer[position + 3], buffer)); scope.addParameterVariables( parameters.map( - parameter => parameter.declare('parameter', UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => + parameter.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] ), parameters[parameters.length - 1] instanceof RestElement ); @@ -546,7 +549,8 @@ const bufferParsers: ((node: any, position: number, buffer: AstBuffer) => void)[ const parameters = (node.params = convertNodeList(node, scope, buffer[position + 3], buffer)); scope.addParameterVariables( parameters.map( - parameter => parameter.declare('parameter', UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => + parameter.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] ), parameters[parameters.length - 1] instanceof RestElement ); diff --git a/src/ast/nodes/ArrayExpression.ts b/src/ast/nodes/ArrayExpression.ts index 1d731e364..739cde53a 100644 --- a/src/ast/nodes/ArrayExpression.ts +++ b/src/ast/nodes/ArrayExpression.ts @@ -2,8 +2,8 @@ import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext } from '../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions'; import { + type EntityPathTracker, type ObjectPath, - type PathTracker, UNKNOWN_PATH, UnknownInteger } from '../utils/PathTracker'; @@ -23,7 +23,7 @@ export default class ArrayExpression extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -38,7 +38,7 @@ export default class ArrayExpression extends NodeBase { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getObjectEntity().getLiteralValueAtPath(path, recursionTracker, origin); @@ -47,7 +47,7 @@ export default class ArrayExpression extends NodeBase { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.getObjectEntity().getReturnExpressionWhenCalledAtPath( diff --git a/src/ast/nodes/ArrayPattern.ts b/src/ast/nodes/ArrayPattern.ts index 1cbb302b8..daf57eb35 100644 --- a/src/ast/nodes/ArrayPattern.ts +++ b/src/ast/nodes/ArrayPattern.ts @@ -1,15 +1,15 @@ -import type { HasEffectsContext } from '../ExecutionContext'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteractionAssigned } from '../NodeInteractions'; -import { EMPTY_PATH, type ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, type ObjectPath, UnknownInteger, UnknownKey } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; import type Variable from '../variables/Variable'; import type * as NodeType from './NodeType'; -import { UNKNOWN_EXPRESSION } from './shared/Expression'; +import type { ExpressionEntity } from './shared/Expression'; import { NodeBase } from './shared/Node'; -import type { PatternNode } from './shared/Pattern'; +import type { DeclarationPatternNode, PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class ArrayPattern extends NodeBase implements PatternNode { +export default class ArrayPattern extends NodeBase implements DeclarationPatternNode { declare elements: (PatternNode | null)[]; declare type: NodeType.tArrayPattern; @@ -22,16 +22,30 @@ export default class ArrayPattern extends NodeBase implements PatternNode { } } - declare(kind: VariableKind): LocalVariable[] { + declare( + kind: VariableKind, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): LocalVariable[] { const variables: LocalVariable[] = []; + const includedPatternPath = getIncludedPatternPath(destructuredInitPath); for (const element of this.elements) { if (element !== null) { - variables.push(...element.declare(kind, UNKNOWN_EXPRESSION)); + variables.push( + ...(element as DeclarationPatternNode).declare(kind, includedPatternPath, init) + ); } } return variables; } + deoptimizeAssignment(destructuredInitPath: ObjectPath, init: ExpressionEntity): void { + const includedPatternPath = getIncludedPatternPath(destructuredInitPath); + for (const element of this.elements) { + element?.deoptimizeAssignment(includedPatternPath, init); + } + } + // Patterns can only be deoptimized at the empty path at the moment deoptimizePath(): void { for (const element of this.elements) { @@ -39,6 +53,20 @@ export default class ArrayPattern extends NodeBase implements PatternNode { } } + hasEffectsWhenDestructuring( + context: HasEffectsContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + const includedPatternPath = getIncludedPatternPath(destructuredInitPath); + for (const element of this.elements) { + if (element?.hasEffectsWhenDestructuring(context, includedPatternPath, init)) { + return true; + } + } + return false; + } + // Patterns are only checked at the empty path at the moment hasEffectsOnInteractionAtPath( _path: ObjectPath, @@ -51,9 +79,41 @@ export default class ArrayPattern extends NodeBase implements PatternNode { return false; } + includeDestructuredIfNecessary( + context: InclusionContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + let included = false; + const includedPatternPath = getIncludedPatternPath(destructuredInitPath); + for (const element of this.elements) { + if (element) { + element.included ||= included; + included = + element.includeDestructuredIfNecessary(context, includedPatternPath, init) || included; + } + } + if (included) { + // This is necessary so that if any pattern element is included, all are + // included for proper deconflicting + for (const element of this.elements) { + if (element && !element.included) { + element.included = true; + element.includeDestructuredIfNecessary(context, includedPatternPath, init); + } + } + } + return (this.included ||= included); + } + markDeclarationReached(): void { for (const element of this.elements) { - element?.markDeclarationReached(); + (element as DeclarationPatternNode)?.markDeclarationReached(); } } } + +const getIncludedPatternPath = (destructuredInitPath: ObjectPath): ObjectPath => + destructuredInitPath.at(-1) === UnknownKey + ? destructuredInitPath + : [...destructuredInitPath, UnknownInteger]; diff --git a/src/ast/nodes/ArrowFunctionExpression.ts b/src/ast/nodes/ArrowFunctionExpression.ts index c46db27f5..2c0125ec3 100644 --- a/src/ast/nodes/ArrowFunctionExpression.ts +++ b/src/ast/nodes/ArrowFunctionExpression.ts @@ -3,7 +3,7 @@ import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_CALLED } from '../NodeInteractions'; import type ChildScope from '../scopes/ChildScope'; import ReturnValueScope from '../scopes/ReturnValueScope'; -import { type ObjectPath } from '../utils/PathTracker'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type BlockStatement from './BlockStatement'; import type CallExpression from './CallExpression'; import Identifier from './Identifier'; @@ -13,11 +13,11 @@ import FunctionBase from './shared/FunctionBase'; import type { ExpressionNode, IncludeChildren } from './shared/Node'; import { ObjectEntity } from './shared/ObjectEntity'; import { OBJECT_PROTOTYPE } from './shared/ObjectPrototype'; -import type { PatternNode } from './shared/Pattern'; +import type { DeclarationPatternNode } from './shared/Pattern'; export default class ArrowFunctionExpression extends FunctionBase { declare body: BlockStatement | ExpressionNode; - declare params: PatternNode[]; + declare params: DeclarationPatternNode[]; declare preventChildBlockScope: true; declare scope: ReturnValueScope; declare type: NodeType.tArrowFunctionExpression; @@ -79,11 +79,15 @@ export default class ArrowFunctionExpression extends FunctionBase { return isIIFE || super.onlyFunctionCallUsed(); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { - super.include(context, includeChildrenRecursively); + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { + super.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); for (const parameter of this.params) { if (!(parameter instanceof Identifier)) { - parameter.include(context, includeChildrenRecursively); + parameter.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } } } diff --git a/src/ast/nodes/AssignmentExpression.ts b/src/ast/nodes/AssignmentExpression.ts index aecdf927e..bce594178 100644 --- a/src/ast/nodes/AssignmentExpression.ts +++ b/src/ast/nodes/AssignmentExpression.ts @@ -28,7 +28,7 @@ import { type ExpressionNode, type IncludeChildren, NodeBase } from './shared/No import type { PatternNode } from './shared/Pattern'; export default class AssignmentExpression extends NodeBase { - declare left: ExpressionNode | PatternNode; + declare left: PatternNode; declare operator: | '=' | '+=' @@ -55,7 +55,9 @@ export default class AssignmentExpression extends NodeBase { // MemberExpressions do not access the property before assignments if the // operator is '='. return ( - right.hasEffects(context) || left.hasEffectsAsAssignmentTarget(context, operator !== '=') + right.hasEffects(context) || + left.hasEffectsAsAssignmentTarget(context, operator !== '=') || + this.left.hasEffectsWhenDestructuring?.(context, EMPTY_PATH, right) ); } @@ -67,19 +69,25 @@ export default class AssignmentExpression extends NodeBase { return this.right.hasEffectsOnInteractionAtPath(path, interaction, context); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { const { deoptimized, left, right, operator } = this; if (!deoptimized) this.applyDeoptimizations(); this.included = true; + const hasEffectsContext = createHasEffectsContext(); if ( includeChildrenRecursively || operator !== '=' || left.included || - left.hasEffectsAsAssignmentTarget(createHasEffectsContext(), false) + left.hasEffectsAsAssignmentTarget(hasEffectsContext, false) || + left.hasEffectsWhenDestructuring?.(hasEffectsContext, EMPTY_PATH, right) ) { left.includeAsAssignmentTarget(context, includeChildrenRecursively, operator !== '='); } - right.include(context, includeChildrenRecursively); + right.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } initialise(): void { @@ -164,8 +172,7 @@ export default class AssignmentExpression extends NodeBase { protected applyDeoptimizations(): void { this.deoptimized = true; - this.left.deoptimizePath(EMPTY_PATH); - this.right.deoptimizePath(UNKNOWN_PATH); + this.left.deoptimizeAssignment(EMPTY_PATH, this.right); this.scope.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/AssignmentPattern.ts b/src/ast/nodes/AssignmentPattern.ts index a67646b90..e071fe980 100644 --- a/src/ast/nodes/AssignmentPattern.ts +++ b/src/ast/nodes/AssignmentPattern.ts @@ -1,7 +1,7 @@ import type MagicString from 'magic-string'; import { BLANK } from '../../utils/blank'; import type { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers'; -import type { HasEffectsContext } from '../ExecutionContext'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteractionAssigned } from '../NodeInteractions'; import { EMPTY_PATH, type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; @@ -9,10 +9,10 @@ import type Variable from '../variables/Variable'; import type * as NodeType from './NodeType'; import type { ExpressionEntity } from './shared/Expression'; import { type ExpressionNode, NodeBase } from './shared/Node'; -import type { PatternNode } from './shared/Pattern'; +import type { DeclarationPatternNode, PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class AssignmentPattern extends NodeBase implements PatternNode { +export default class AssignmentPattern extends NodeBase implements DeclarationPatternNode { declare left: PatternNode; declare right: ExpressionNode; declare type: NodeType.tAssignmentPattern; @@ -24,8 +24,16 @@ export default class AssignmentPattern extends NodeBase implements PatternNode { this.left.addExportedVariables(variables, exportNamesByVariable); } - declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { - return this.left.declare(kind, init); + declare( + kind: VariableKind, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): LocalVariable[] { + return (this.left as DeclarationPatternNode).declare(kind, destructuredInitPath, init); + } + + deoptimizeAssignment(destructuredInitPath: ObjectPath, init: ExpressionEntity): void { + this.left.deoptimizeAssignment(destructuredInitPath, init); } deoptimizePath(path: ObjectPath): void { @@ -44,8 +52,36 @@ export default class AssignmentPattern extends NodeBase implements PatternNode { ); } + hasEffectsWhenDestructuring( + context: HasEffectsContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + return this.left.hasEffectsWhenDestructuring(context, destructuredInitPath, init); + } + + includeDestructuredIfNecessary( + context: InclusionContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + let included = + this.left.includeDestructuredIfNecessary(context, destructuredInitPath, init) || + this.included; + if ((included ||= this.right.shouldBeIncluded(context))) { + this.right.includePath(UNKNOWN_PATH, context, false); + if (!this.left.included) { + this.left.included = true; + // Unfortunately, we need to include the left side again now, so that + // any declared variables are properly included. + this.left.includeDestructuredIfNecessary(context, destructuredInitPath, init); + } + } + return (this.included = included); + } + markDeclarationReached(): void { - this.left.markDeclarationReached(); + (this.left as DeclarationPatternNode).markDeclarationReached(); } render( diff --git a/src/ast/nodes/AwaitExpression.ts b/src/ast/nodes/AwaitExpression.ts index 25f262c0e..55585f265 100644 --- a/src/ast/nodes/AwaitExpression.ts +++ b/src/ast/nodes/AwaitExpression.ts @@ -1,4 +1,5 @@ import type { InclusionContext } from '../ExecutionContext'; +import { type ObjectPath } from '../utils/PathTracker'; import ArrowFunctionExpression from './ArrowFunctionExpression'; import type * as NodeType from './NodeType'; import FunctionNode from './shared/FunctionNode'; @@ -13,7 +14,11 @@ export default class AwaitExpression extends NodeBase { return true; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { if (!this.deoptimized) this.applyDeoptimizations(); if (!this.included) { this.included = true; @@ -26,6 +31,6 @@ export default class AwaitExpression extends NodeBase { this.scope.context.usesTopLevelAwait = true; } } - this.argument.include(context, includeChildrenRecursively); + this.argument.includePath(path, context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/BinaryExpression.ts b/src/ast/nodes/BinaryExpression.ts index 7c969f3b6..8021d923b 100644 --- a/src/ast/nodes/BinaryExpression.ts +++ b/src/ast/nodes/BinaryExpression.ts @@ -7,8 +7,8 @@ import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ACCESSED } from '../NodeInteractions'; import { EMPTY_PATH, + type EntityPathTracker, type ObjectPath, - type PathTracker, SHARED_RECURSION_TRACKER } from '../utils/PathTracker'; import ExpressionStatement from './ExpressionStatement'; @@ -80,7 +80,7 @@ export default class BinaryExpression extends NodeBase implements DeoptimizableE getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { if (path.length > 0) return UnknownValue; diff --git a/src/ast/nodes/BlockStatement.ts b/src/ast/nodes/BlockStatement.ts index 22c332f9b..ff6baa331 100644 --- a/src/ast/nodes/BlockStatement.ts +++ b/src/ast/nodes/BlockStatement.ts @@ -3,6 +3,7 @@ import { type RenderOptions, renderStatementList } from '../../utils/renderHelpe import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import BlockScope from '../scopes/BlockScope'; import type ChildScope from '../scopes/ChildScope'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import ExpressionStatement from './ExpressionStatement'; import * as NodeType from './NodeType'; import { Flag, isFlagSet, setFlag } from './shared/BitFlags'; @@ -49,14 +50,18 @@ export default class BlockStatement extends StatementBase { return false; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { if (!(this.deoptimizeBody && this.directlyIncluded)) { this.included = true; this.directlyIncluded = true; if (this.deoptimizeBody) includeChildrenRecursively = true; for (const node of this.body) { if (includeChildrenRecursively || node.shouldBeIncluded(context)) - node.include(context, includeChildrenRecursively); + node.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } } } diff --git a/src/ast/nodes/BreakStatement.ts b/src/ast/nodes/BreakStatement.ts index 836fb356c..b3e783e84 100644 --- a/src/ast/nodes/BreakStatement.ts +++ b/src/ast/nodes/BreakStatement.ts @@ -1,4 +1,9 @@ -import { type HasEffectsContext, type InclusionContext } from '../ExecutionContext'; +import { + createInclusionContext, + type HasEffectsContext, + type InclusionContext +} from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type Identifier from './Identifier'; import type * as NodeType from './NodeType'; import { StatementBase } from './shared/Node'; @@ -19,10 +24,10 @@ export default class BreakStatement extends StatementBase { return false; } - include(context: InclusionContext): void { + includePath(_: ObjectPath, context: InclusionContext): void { this.included = true; if (this.label) { - this.label.include(); + this.label.includePath(UNKNOWN_PATH, createInclusionContext()); context.includedLabels.add(this.label.name); } else { context.hasBreak = true; diff --git a/src/ast/nodes/CallExpression.ts b/src/ast/nodes/CallExpression.ts index ae18f8758..531a9c5d2 100644 --- a/src/ast/nodes/CallExpression.ts +++ b/src/ast/nodes/CallExpression.ts @@ -8,20 +8,20 @@ import { type NodeRenderOptions, type RenderOptions } from '../../utils/renderHe import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import { INTERACTION_CALLED } from '../NodeInteractions'; -import type { ObjectPath, PathTracker } from '../utils/PathTracker'; -import { EMPTY_PATH, SHARED_RECURSION_TRACKER } from '../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker'; import Identifier from './Identifier'; import MemberExpression from './MemberExpression'; import type * as NodeType from './NodeType'; -import type SpreadElement from './SpreadElement'; -import type Super from './Super'; import { Flag, isFlagSet, setFlag } from './shared/BitFlags'; import CallExpressionBase from './shared/CallExpressionBase'; +import { getChainElementLiteralValueAtPath } from './shared/chainElements'; import type { ExpressionEntity, LiteralValueOrUnknown } from './shared/Expression'; import { UNKNOWN_RETURN_EXPRESSION } from './shared/Expression'; import type { ChainElement, ExpressionNode, IncludeChildren, SkippedChain } from './shared/Node'; import { INCLUDE_PARAMETERS, IS_SKIPPED_CHAIN } from './shared/Node'; -import { getChainElementLiteralValueAtPath } from './shared/chainElements'; +import type SpreadElement from './SpreadElement'; +import type Super from './Super'; export default class CallExpression extends CallExpressionBase @@ -67,7 +67,7 @@ export default class CallExpression getLiteralValueAtPathAsChainElement( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown | SkippedChain { return getChainElementLiteralValueAtPath(this, this.callee, path, recursionTracker, origin); @@ -111,10 +111,14 @@ export default class CallExpression ); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { if (!this.deoptimized) this.applyDeoptimizations(); if (includeChildrenRecursively) { - super.include(context, includeChildrenRecursively); + super.includePath(path, context, includeChildrenRecursively); if ( includeChildrenRecursively === INCLUDE_PARAMETERS && this.callee instanceof Identifier && @@ -124,9 +128,17 @@ export default class CallExpression } } else { this.included = true; - this.callee.include(context, false); + // If the callee is a member expression and does not have a variable, its + // object will already be included via the first argument of the + // interaction in includeCallArguments. Including it again can lead to + // severe performance problems. + if (this.callee instanceof MemberExpression && !this.callee.variable) { + this.callee.property.includePath(UNKNOWN_PATH, context, false); + } else { + this.callee.includePath(UNKNOWN_PATH, context, false); + } + this.callee.includeCallArguments(context, this.interaction); } - this.callee.includeCallArguments(context, this.arguments); } initialise() { @@ -162,7 +174,7 @@ export default class CallExpression } protected getReturnExpression( - recursionTracker: PathTracker = SHARED_RECURSION_TRACKER + recursionTracker: EntityPathTracker = SHARED_RECURSION_TRACKER ): [expression: ExpressionEntity, isPure: boolean] { if (this.returnExpression === null) { this.returnExpression = UNKNOWN_RETURN_EXPRESSION; diff --git a/src/ast/nodes/CatchClause.ts b/src/ast/nodes/CatchClause.ts index ae9743c2f..be2a9d6c2 100644 --- a/src/ast/nodes/CatchClause.ts +++ b/src/ast/nodes/CatchClause.ts @@ -1,14 +1,15 @@ import type ChildScope from '../scopes/ChildScope'; import ParameterScope from '../scopes/ParameterScope'; +import { EMPTY_PATH } from '../utils/PathTracker'; import BlockStatement from './BlockStatement'; import type * as NodeType from './NodeType'; import { UNKNOWN_EXPRESSION } from './shared/Expression'; import { type GenericEsTreeNode, NodeBase } from './shared/Node'; -import type { PatternNode } from './shared/Pattern'; +import type { DeclarationPatternNode } from './shared/Pattern'; export default class CatchClause extends NodeBase { declare body: BlockStatement; - declare param: PatternNode | null; + declare param: DeclarationPatternNode | null; declare preventChildBlockScope: true; declare scope: ParameterScope; declare type: NodeType.tCatchClause; @@ -24,8 +25,8 @@ export default class CatchClause extends NodeBase { this.param = new (this.scope.context.getNodeConstructor(param.type))( this, this.scope - ).parseNode(param) as unknown as PatternNode; - this.param!.declare('parameter', UNKNOWN_EXPRESSION); + ).parseNode(param) as unknown as DeclarationPatternNode; + this.param!.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION); } this.body = new BlockStatement(this, this.scope.bodyScope).parseNode(body); return super.parseNode(esTreeNode); diff --git a/src/ast/nodes/ChainExpression.ts b/src/ast/nodes/ChainExpression.ts index 28b583332..5bd7868bc 100644 --- a/src/ast/nodes/ChainExpression.ts +++ b/src/ast/nodes/ChainExpression.ts @@ -1,7 +1,7 @@ import type MagicString from 'magic-string'; import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext } from '../ExecutionContext'; -import type { ObjectPath, PathTracker } from '../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; import type CallExpression from './CallExpression'; import type MemberExpression from './MemberExpression'; import type * as NodeType from './NodeType'; @@ -17,7 +17,7 @@ export default class ChainExpression extends NodeBase implements DeoptimizableEn getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { const literalValue = this.expression.getLiteralValueAtPathAsChainElement( diff --git a/src/ast/nodes/ClassBody.ts b/src/ast/nodes/ClassBody.ts index 845f21b15..53590a4d4 100644 --- a/src/ast/nodes/ClassBody.ts +++ b/src/ast/nodes/ClassBody.ts @@ -1,13 +1,14 @@ import type { InclusionContext } from '../ExecutionContext'; import type ChildScope from '../scopes/ChildScope'; import ClassBodyScope from '../scopes/ClassBodyScope'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type MethodDefinition from './MethodDefinition'; import type * as NodeType from './NodeType'; import type PropertyDefinition from './PropertyDefinition'; -import type StaticBlock from './StaticBlock'; import type ClassNode from './shared/ClassNode'; import { type GenericEsTreeNode, type IncludeChildren, NodeBase } from './shared/Node'; +import type StaticBlock from './StaticBlock'; export default class ClassBody extends NodeBase { declare body: (MethodDefinition | PropertyDefinition | StaticBlock)[]; @@ -18,11 +19,15 @@ export default class ClassBody extends NodeBase { this.scope = new ClassBodyScope(parentScope, this.parent as ClassNode); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; - this.scope.context.includeVariableInModule(this.scope.thisVariable); + this.scope.context.includeVariableInModule(this.scope.thisVariable, UNKNOWN_PATH); for (const definition of this.body) { - definition.include(context, includeChildrenRecursively); + definition.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/ConditionalExpression.ts b/src/ast/nodes/ConditionalExpression.ts index 5645a23f9..51669e601 100644 --- a/src/ast/nodes/ConditionalExpression.ts +++ b/src/ast/nodes/ConditionalExpression.ts @@ -9,10 +9,9 @@ import { import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions'; -import type { ObjectPath, PathTracker } from '../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; -import type SpreadElement from './SpreadElement'; import { Flag, isFlagSet, setFlag } from './shared/BitFlags'; import type { ExpressionEntity, LiteralValueOrUnknown } from './shared/Expression'; import { UnknownValue } from './shared/Expression'; @@ -39,7 +38,7 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.consequent.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); this.alternate.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); @@ -70,7 +69,7 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { const usedBranch = this.getUsedBranch(); @@ -82,7 +81,7 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { const usedBranch = this.getUsedBranch(); @@ -137,28 +136,29 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz return usedBranch.hasEffectsOnInteractionAtPath(path, interaction, context); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; const usedBranch = this.getUsedBranch(); if (includeChildrenRecursively || this.test.shouldBeIncluded(context) || usedBranch === null) { - this.test.include(context, includeChildrenRecursively); - this.consequent.include(context, includeChildrenRecursively); - this.alternate.include(context, includeChildrenRecursively); + this.test.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.consequent.includePath(path, context, includeChildrenRecursively); + this.alternate.includePath(path, context, includeChildrenRecursively); } else { - usedBranch.include(context, includeChildrenRecursively); + usedBranch.includePath(path, context, includeChildrenRecursively); } } - includeCallArguments( - context: InclusionContext, - parameters: readonly (ExpressionEntity | SpreadElement)[] - ): void { + includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { const usedBranch = this.getUsedBranch(); if (usedBranch) { - usedBranch.includeCallArguments(context, parameters); + usedBranch.includeCallArguments(context, interaction); } else { - this.consequent.includeCallArguments(context, parameters); - this.alternate.includeCallArguments(context, parameters); + this.consequent.includeCallArguments(context, interaction); + this.alternate.includeCallArguments(context, interaction); } } diff --git a/src/ast/nodes/ContinueStatement.ts b/src/ast/nodes/ContinueStatement.ts index 9e137e1af..16c4f8c7a 100644 --- a/src/ast/nodes/ContinueStatement.ts +++ b/src/ast/nodes/ContinueStatement.ts @@ -1,4 +1,9 @@ -import { type HasEffectsContext, type InclusionContext } from '../ExecutionContext'; +import { + createInclusionContext, + type HasEffectsContext, + type InclusionContext +} from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type Identifier from './Identifier'; import type * as NodeType from './NodeType'; import { StatementBase } from './shared/Node'; @@ -19,10 +24,10 @@ export default class ContinueStatement extends StatementBase { return false; } - include(context: InclusionContext): void { + includePath(_: ObjectPath, context: InclusionContext): void { this.included = true; if (this.label) { - this.label.include(); + this.label.includePath(UNKNOWN_PATH, createInclusionContext()); context.includedLabels.add(this.label.name); } else { context.hasContinue = true; diff --git a/src/ast/nodes/DoWhileStatement.ts b/src/ast/nodes/DoWhileStatement.ts index 7b74b9ad7..829aaae34 100644 --- a/src/ast/nodes/DoWhileStatement.ts +++ b/src/ast/nodes/DoWhileStatement.ts @@ -1,4 +1,5 @@ import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { type ExpressionNode, @@ -18,9 +19,13 @@ export default class DoWhileStatement extends StatementBase { return hasLoopBodyEffects(context, this.body); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; - this.test.include(context, includeChildrenRecursively); + this.test.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); includeLoopBody(context, this.body, includeChildrenRecursively); } } diff --git a/src/ast/nodes/ExportDefaultDeclaration.ts b/src/ast/nodes/ExportDefaultDeclaration.ts index 5cde06600..98898083c 100644 --- a/src/ast/nodes/ExportDefaultDeclaration.ts +++ b/src/ast/nodes/ExportDefaultDeclaration.ts @@ -9,6 +9,7 @@ import { getSystemExportStatement } from '../../utils/systemJsRendering'; import { treeshakeNode } from '../../utils/treeshakeNode'; import type { InclusionContext } from '../ExecutionContext'; import type ModuleScope from '../scopes/ModuleScope'; +import type { ObjectPath } from '../utils/PathTracker'; import type ExportDefaultVariable from '../variables/ExportDefaultVariable'; import ClassDeclaration from './ClassDeclaration'; import FunctionDeclaration from './FunctionDeclaration'; @@ -41,10 +42,15 @@ export default class ExportDefaultDeclaration extends NodeBase { private declare declarationName: string | undefined; - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { - super.include(context, includeChildrenRecursively); + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { + this.included = true; + this.declaration.includePath(path, context, includeChildrenRecursively); if (includeChildrenRecursively) { - this.scope.context.includeVariableInModule(this.variable); + this.scope.context.includeVariableInModule(this.variable, path); } } diff --git a/src/ast/nodes/ForInStatement.ts b/src/ast/nodes/ForInStatement.ts index c078d09e5..0cdb33b66 100644 --- a/src/ast/nodes/ForInStatement.ts +++ b/src/ast/nodes/ForInStatement.ts @@ -3,11 +3,11 @@ import { NO_SEMICOLON, type RenderOptions } from '../../utils/renderHelpers'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import BlockScope from '../scopes/BlockScope'; import type ChildScope from '../scopes/ChildScope'; -import { EMPTY_PATH } from '../utils/PathTracker'; -import type MemberExpression from './MemberExpression'; +import type { ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; -import type VariableDeclaration from './VariableDeclaration'; import { UNKNOWN_EXPRESSION } from './shared/Expression'; +import { hasLoopBodyEffects, includeLoopBody } from './shared/loops'; import { type ExpressionNode, type IncludeChildren, @@ -15,11 +15,11 @@ import { type StatementNode } from './shared/Node'; import type { PatternNode } from './shared/Pattern'; -import { hasLoopBodyEffects, includeLoopBody } from './shared/loops'; +import type VariableDeclaration from './VariableDeclaration'; export default class ForInStatement extends StatementBase { declare body: StatementNode; - declare left: VariableDeclaration | PatternNode | MemberExpression; + declare left: VariableDeclaration | PatternNode; declare right: ExpressionNode; declare type: NodeType.tForInStatement; @@ -34,12 +34,16 @@ export default class ForInStatement extends StatementBase { return hasLoopBodyEffects(context, body); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { const { body, deoptimized, left, right } = this; if (!deoptimized) this.applyDeoptimizations(); this.included = true; left.includeAsAssignmentTarget(context, includeChildrenRecursively || true, false); - right.include(context, includeChildrenRecursively); + right.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); includeLoopBody(context, body, includeChildrenRecursively); } diff --git a/src/ast/nodes/ForOfStatement.ts b/src/ast/nodes/ForOfStatement.ts index 227075a69..17fc07b02 100644 --- a/src/ast/nodes/ForOfStatement.ts +++ b/src/ast/nodes/ForOfStatement.ts @@ -3,12 +3,12 @@ import { NO_SEMICOLON, type RenderOptions } from '../../utils/renderHelpers'; import type { InclusionContext } from '../ExecutionContext'; import BlockScope from '../scopes/BlockScope'; import type ChildScope from '../scopes/ChildScope'; +import type { ObjectPath } from '../utils/PathTracker'; import { EMPTY_PATH, UNKNOWN_PATH } from '../utils/PathTracker'; -import type MemberExpression from './MemberExpression'; import type * as NodeType from './NodeType'; -import type VariableDeclaration from './VariableDeclaration'; import { Flag, isFlagSet, setFlag } from './shared/BitFlags'; import { UNKNOWN_EXPRESSION } from './shared/Expression'; +import { includeLoopBody } from './shared/loops'; import { type ExpressionNode, type IncludeChildren, @@ -16,11 +16,11 @@ import { type StatementNode } from './shared/Node'; import type { PatternNode } from './shared/Pattern'; -import { includeLoopBody } from './shared/loops'; +import type VariableDeclaration from './VariableDeclaration'; export default class ForOfStatement extends StatementBase { declare body: StatementNode; - declare left: VariableDeclaration | PatternNode | MemberExpression; + declare left: VariableDeclaration | PatternNode; declare right: ExpressionNode; declare type: NodeType.tForOfStatement; @@ -41,12 +41,16 @@ export default class ForOfStatement extends StatementBase { return true; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { const { body, deoptimized, left, right } = this; if (!deoptimized) this.applyDeoptimizations(); this.included = true; left.includeAsAssignmentTarget(context, includeChildrenRecursively || true, false); - right.include(context, includeChildrenRecursively); + right.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); includeLoopBody(context, body, includeChildrenRecursively); } diff --git a/src/ast/nodes/ForStatement.ts b/src/ast/nodes/ForStatement.ts index 1fe86e73e..6f94edcc9 100644 --- a/src/ast/nodes/ForStatement.ts +++ b/src/ast/nodes/ForStatement.ts @@ -3,6 +3,7 @@ import { NO_SEMICOLON, type RenderOptions } from '../../utils/renderHelpers'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import BlockScope from '../scopes/BlockScope'; import type ChildScope from '../scopes/ChildScope'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import type VariableDeclaration from './VariableDeclaration'; import { @@ -35,11 +36,17 @@ export default class ForStatement extends StatementBase { return hasLoopBodyEffects(context, this.body); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; - this.init?.include(context, includeChildrenRecursively, { asSingleStatement: true }); - this.test?.include(context, includeChildrenRecursively); - this.update?.include(context, includeChildrenRecursively); + this.init?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively, { + asSingleStatement: true + }); + this.test?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.update?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); includeLoopBody(context, this.body, includeChildrenRecursively); } diff --git a/src/ast/nodes/Identifier.ts b/src/ast/nodes/Identifier.ts index c513e800b..75a27641a 100644 --- a/src/ast/nodes/Identifier.ts +++ b/src/ast/nodes/Identifier.ts @@ -1,23 +1,39 @@ import isReference, { type NodeWithFieldDefinition } from 'is-reference'; import type MagicString from 'magic-string'; +import type { NormalizedTreeshakingOptions } from '../../rollup/types'; import { BLANK } from '../../utils/blank'; import type { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import { createHasEffectsContext } from '../ExecutionContext'; +import { INTERACTION_ACCESSED, NODE_INTERACTION_UNKNOWN_ACCESS } from '../NodeInteractions'; import type FunctionScope from '../scopes/FunctionScope'; +import type { ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UnknownKey } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; import type Variable from '../variables/Variable'; import * as NodeType from './NodeType'; +import { Flag, isFlagSet, setFlag } from './shared/BitFlags'; import { type ExpressionEntity } from './shared/Expression'; import IdentifierBase from './shared/IdentifierBase'; -import type { PatternNode } from './shared/Pattern'; +import { ObjectMember } from './shared/ObjectMember'; +import type { DeclarationPatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; export type IdentifierWithVariable = Identifier & { variable: Variable }; -export default class Identifier extends IdentifierBase implements PatternNode { +export default class Identifier extends IdentifierBase implements DeclarationPatternNode { name!: string; type!: NodeType.tIdentifier; variable: Variable | null = null; + private get isDestructuringDeoptimized(): boolean { + return isFlagSet(this.flags, Flag.destructuringDeoptimized); + } + + private set isDestructuringDeoptimized(value: boolean) { + this.flags = setFlag(this.flags, Flag.destructuringDeoptimized, value); + } + addExportedVariables( variables: Variable[], exportNamesByVariable: ReadonlyMap @@ -35,44 +51,90 @@ export default class Identifier extends IdentifierBase implements PatternNode { } } - declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { + declare( + kind: VariableKind, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): LocalVariable[] { let variable: LocalVariable; const { treeshake } = this.scope.context.options; - switch (kind) { - case 'var': { - variable = this.scope.addDeclaration(this, this.scope.context, init, kind); - if (treeshake && treeshake.correctVarValueBeforeDeclaration) { - // Necessary to make sure the init is deoptimized. We cannot call deoptimizePath here. - variable.markInitializersForDeoptimization(); - } - break; - } - case 'function': { - // in strict mode, functions are only hoisted within a scope but not across block scopes - variable = this.scope.addDeclaration(this, this.scope.context, init, kind); - break; - } - case 'let': - case 'const': - case 'using': - case 'await using': - case 'class': { - variable = this.scope.addDeclaration(this, this.scope.context, init, kind); - break; - } - case 'parameter': { - variable = (this.scope as FunctionScope).addParameterDeclaration(this); - break; - } - /* istanbul ignore next */ - default: { - /* istanbul ignore next */ - throw new Error(`Internal Error: Unexpected identifier kind ${kind}.`); + if (kind === 'parameter') { + variable = (this.scope as FunctionScope).addParameterDeclaration(this, destructuredInitPath); + } else { + variable = this.scope.addDeclaration( + this, + this.scope.context, + init, + destructuredInitPath, + kind + ); + if (kind === 'var' && treeshake && treeshake.correctVarValueBeforeDeclaration) { + // Necessary to make sure the init is deoptimized. We cannot call deoptimizePath here. + variable.markInitializersForDeoptimization(); } } return [(this.variable = variable)]; } + deoptimizeAssignment(destructuredInitPath: ObjectPath, init: ExpressionEntity) { + this.deoptimizePath(EMPTY_PATH); + init.deoptimizePath([...destructuredInitPath, UnknownKey]); + } + + hasEffectsWhenDestructuring( + context: HasEffectsContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + return ( + destructuredInitPath.length > 0 && + init.hasEffectsOnInteractionAtPath( + destructuredInitPath, + NODE_INTERACTION_UNKNOWN_ACCESS, + context + ) + ); + } + + includeDestructuredIfNecessary( + context: InclusionContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + if (destructuredInitPath.length > 0 && !this.isDestructuringDeoptimized) { + this.isDestructuringDeoptimized = true; + init.deoptimizeArgumentsOnInteractionAtPath( + { + args: [new ObjectMember(init, destructuredInitPath.slice(0, -1))], + type: INTERACTION_ACCESSED + }, + destructuredInitPath, + SHARED_RECURSION_TRACKER + ); + } + const { propertyReadSideEffects } = this.scope.context.options + .treeshake as NormalizedTreeshakingOptions; + if ( + (this.included ||= + destructuredInitPath.length > 0 && + !context.brokenFlow && + propertyReadSideEffects && + (propertyReadSideEffects === 'always' || + init.hasEffectsOnInteractionAtPath( + destructuredInitPath, + NODE_INTERACTION_UNKNOWN_ACCESS, + createHasEffectsContext() + ))) + ) { + if (this.variable && !this.variable.included) { + this.scope.context.includeVariableInModule(this.variable, EMPTY_PATH); + } + init.includePath(destructuredInitPath, context, false); + return true; + } + return false; + } + markDeclarationReached(): void { this.variable!.initReached = true; } diff --git a/src/ast/nodes/IfStatement.ts b/src/ast/nodes/IfStatement.ts index 07651843b..d3bd5d83c 100644 --- a/src/ast/nodes/IfStatement.ts +++ b/src/ast/nodes/IfStatement.ts @@ -3,7 +3,8 @@ import type { RenderOptions } from '../../utils/renderHelpers'; import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import { type HasEffectsContext, type InclusionContext } from '../ExecutionContext'; import TrackingScope from '../scopes/TrackingScope'; -import { EMPTY_PATH, SHARED_RECURSION_TRACKER } from '../utils/PathTracker'; +import type { ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker'; import BlockStatement from './BlockStatement'; import type Identifier from './Identifier'; import * as NodeType from './NodeType'; @@ -50,7 +51,11 @@ export default class IfStatement extends StatementBase implements DeoptimizableE return testValue ? this.consequent.hasEffects(context) : !!this.alternate?.hasEffects(context); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; if (includeChildrenRecursively) { this.includeRecursively(includeChildrenRecursively, context); @@ -135,13 +140,13 @@ export default class IfStatement extends StatementBase implements DeoptimizableE private includeKnownTest(context: InclusionContext, testValue: LiteralValueOrUnknown) { if (this.test.shouldBeIncluded(context)) { - this.test.include(context, false); + this.test.includePath(UNKNOWN_PATH, context, false); } if (testValue && this.consequent.shouldBeIncluded(context)) { - this.consequent.include(context, false, { asSingleStatement: true }); + this.consequent.includePath(UNKNOWN_PATH, context, false, { asSingleStatement: true }); } if (!testValue && this.alternate?.shouldBeIncluded(context)) { - this.alternate.include(context, false, { asSingleStatement: true }); + this.alternate.includePath(UNKNOWN_PATH, context, false, { asSingleStatement: true }); } } @@ -149,22 +154,22 @@ export default class IfStatement extends StatementBase implements DeoptimizableE includeChildrenRecursively: true | 'variables', context: InclusionContext ) { - this.test.include(context, includeChildrenRecursively); - this.consequent.include(context, includeChildrenRecursively); - this.alternate?.include(context, includeChildrenRecursively); + this.test.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.consequent.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.alternate?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } private includeUnknownTest(context: InclusionContext) { - this.test.include(context, false); + this.test.includePath(UNKNOWN_PATH, context, false); const { brokenFlow } = context; let consequentBrokenFlow = false; if (this.consequent.shouldBeIncluded(context)) { - this.consequent.include(context, false, { asSingleStatement: true }); + this.consequent.includePath(UNKNOWN_PATH, context, false, { asSingleStatement: true }); consequentBrokenFlow = context.brokenFlow; context.brokenFlow = brokenFlow; } if (this.alternate?.shouldBeIncluded(context)) { - this.alternate.include(context, false, { asSingleStatement: true }); + this.alternate.includePath(UNKNOWN_PATH, context, false, { asSingleStatement: true }); context.brokenFlow = context.brokenFlow && consequentBrokenFlow; } } diff --git a/src/ast/nodes/ImportExpression.ts b/src/ast/nodes/ImportExpression.ts index 785d6b679..480dbcac5 100644 --- a/src/ast/nodes/ImportExpression.ts +++ b/src/ast/nodes/ImportExpression.ts @@ -2,16 +2,17 @@ import type MagicString from 'magic-string'; import ExternalModule from '../../ExternalModule'; import type Module from '../../Module'; import type { AstNode, GetInterop, NormalizedOutputOptions } from '../../rollup/types'; -import type { PluginDriver } from '../../utils/PluginDriver'; import { EMPTY_ARRAY } from '../../utils/blank'; import type { GenerateCodeSnippets } from '../../utils/generateCodeSnippets'; import { INTEROP_NAMESPACE_DEFAULT_ONLY_VARIABLE, namespaceInteropHelpersByInteropType } from '../../utils/interopHelpers'; +import type { PluginDriver } from '../../utils/PluginDriver'; import { findFirstOccurrenceOutsideComment, type RenderOptions } from '../../utils/renderHelpers'; import type { InclusionContext } from '../ExecutionContext'; import type ChildScope from '../scopes/ChildScope'; +import { type ObjectPath, UnknownKey } from '../utils/PathTracker'; import type NamespaceVariable from '../variables/NamespaceVariable'; import ArrowFunctionExpression from './ArrowFunctionExpression'; import AwaitExpression from './AwaitExpression'; @@ -22,13 +23,13 @@ import Identifier from './Identifier'; import MemberExpression from './MemberExpression'; import type * as NodeType from './NodeType'; import ObjectPattern from './ObjectPattern'; -import VariableDeclarator from './VariableDeclarator'; import { type ExpressionNode, type GenericEsTreeNode, type IncludeChildren, NodeBase } from './shared/Node'; +import VariableDeclarator from './VariableDeclarator'; interface DynamicImportMechanism { left: string; @@ -42,6 +43,8 @@ export default class ImportExpression extends NodeBase { declare type: NodeType.tImportExpression; declare sourceAstNode: AstNode; + private hasUnknownAccessedKey = false; + private accessedPropKey = new Set(); private attributes: string | null | true = null; private mechanism: DynamicImportMechanism | null = null; private namespaceExportName: string | false | undefined = undefined; @@ -79,12 +82,15 @@ export default class ImportExpression extends NodeBase { return EMPTY_ARRAY; } - // Case 1: const { foo } = await import('bar') + // Case 1: const { foo } / module = await import('bar') if (parent2 instanceof VariableDeclarator) { const declaration = parent2.id; - return declaration instanceof ObjectPattern - ? getDeterministicObjectDestructure(declaration) - : undefined; + if (declaration instanceof Identifier) { + return this.hasUnknownAccessedKey ? undefined : [...this.accessedPropKey]; + } + if (declaration instanceof ObjectPattern) { + return getDeterministicObjectDestructure(declaration); + } } // Case 2: (await import('bar')).foo @@ -151,13 +157,25 @@ export default class ImportExpression extends NodeBase { return true; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { if (!this.included) { this.included = true; this.scope.context.includeDynamicImport(this); this.scope.addAccessedDynamicImport(this); + this.source.includePath(path, context, includeChildrenRecursively); + } + if (this.hasUnknownAccessedKey) return; + if (path[0] === UnknownKey) { + this.hasUnknownAccessedKey = true; + this.scope.context.includeDynamicImport(this); + } else if (typeof path[0] === 'string') { + this.accessedPropKey.add(path[0]); + this.scope.context.includeDynamicImport(this); } - this.source.include(context, includeChildrenRecursively); } initialise(): void { diff --git a/src/ast/nodes/JSXOpeningFragment.ts b/src/ast/nodes/JSXOpeningFragment.ts index 6d50740c1..ee127b388 100644 --- a/src/ast/nodes/JSXOpeningFragment.ts +++ b/src/ast/nodes/JSXOpeningFragment.ts @@ -2,6 +2,7 @@ import type MagicString from 'magic-string'; import type { NormalizedJsxOptions } from '../../rollup/types'; import type { RenderOptions } from '../../utils/renderHelpers'; import type { InclusionContext } from '../ExecutionContext'; +import type { ObjectPath } from '../utils/PathTracker'; import type Variable from '../variables/Variable'; import type * as NodeType from './NodeType'; import { getAndIncludeFactoryVariable } from './shared/jsxHelpers'; @@ -15,7 +16,11 @@ export default class JSXOpeningFragment extends NodeBase { private fragment: string | null = null; private fragmentVariable: Variable | null = null; - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren) { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ) { if (!this.included) { const jsx = this.scope.context.options.jsx as NormalizedJsxOptions; if (jsx.mode === 'automatic') { @@ -39,7 +44,7 @@ export default class JSXOpeningFragment extends NodeBase { } } } - super.include(context, includeChildrenRecursively); + super.includePath(path, context, includeChildrenRecursively); } render(code: MagicString, options: RenderOptions): void { diff --git a/src/ast/nodes/LabeledStatement.ts b/src/ast/nodes/LabeledStatement.ts index 57162a566..bbdb401d1 100644 --- a/src/ast/nodes/LabeledStatement.ts +++ b/src/ast/nodes/LabeledStatement.ts @@ -4,7 +4,12 @@ import { findNonWhiteSpace, type RenderOptions } from '../../utils/renderHelpers'; -import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import { + createInclusionContext, + type HasEffectsContext, + type InclusionContext +} from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type Identifier from './Identifier'; import type * as NodeType from './NodeType'; import { type IncludeChildren, StatementBase, type StatementNode } from './shared/Node'; @@ -32,13 +37,17 @@ export default class LabeledStatement extends StatementBase { return bodyHasEffects; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; const { brokenFlow, includedLabels } = context; context.includedLabels = new Set(); - this.body.include(context, includeChildrenRecursively); + this.body.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); if (includeChildrenRecursively || context.includedLabels.has(this.label.name)) { - this.label.include(); + this.label.includePath(UNKNOWN_PATH, createInclusionContext()); context.includedLabels.delete(this.label.name); context.brokenFlow = brokenFlow; } diff --git a/src/ast/nodes/LogicalExpression.ts b/src/ast/nodes/LogicalExpression.ts index e5fa9d6f5..f958edb79 100644 --- a/src/ast/nodes/LogicalExpression.ts +++ b/src/ast/nodes/LogicalExpression.ts @@ -13,8 +13,8 @@ import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions'; import { EMPTY_PATH, + type EntityPathTracker, type ObjectPath, - type PathTracker, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker'; @@ -51,7 +51,7 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.left.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); this.right.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); @@ -88,7 +88,7 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { const usedBranch = this.getUsedBranch(); @@ -100,7 +100,7 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { const usedBranch = this.getUsedBranch(); @@ -156,7 +156,11 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable return usedBranch.hasEffectsOnInteractionAtPath(path, interaction, context); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; const usedBranch = this.getUsedBranch(); if ( @@ -164,10 +168,10 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable (usedBranch === this.right && this.left.shouldBeIncluded(context)) || !usedBranch ) { - this.left.include(context, includeChildrenRecursively); - this.right.include(context, includeChildrenRecursively); + this.left.includePath(path, context, includeChildrenRecursively); + this.right.includePath(path, context, includeChildrenRecursively); } else { - usedBranch.include(context, includeChildrenRecursively); + usedBranch.includePath(path, context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/MemberExpression.ts b/src/ast/nodes/MemberExpression.ts index 0fe75f3d1..740c7ceae 100644 --- a/src/ast/nodes/MemberExpression.ts +++ b/src/ast/nodes/MemberExpression.ts @@ -7,18 +7,24 @@ import { logIllegalImportReassignment, logMissingExport } from '../../utils/logs import type { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers'; import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import { createHasEffectsContext } from '../ExecutionContext'; import type { NodeInteraction, NodeInteractionAccessed, NodeInteractionAssigned, NodeInteractionCalled } from '../NodeInteractions'; -import { INTERACTION_ACCESSED, INTERACTION_ASSIGNED } from '../NodeInteractions'; +import { + INTERACTION_ACCESSED, + INTERACTION_ASSIGNED, + NODE_INTERACTION_UNKNOWN_ACCESS +} from '../NodeInteractions'; +import { MAX_PATH_DEPTH } from '../utils/limitPathLength'; import { EMPTY_PATH, + type EntityPathTracker, type ObjectPath, type ObjectPathKey, - type PathTracker, SHARED_RECURSION_TRACKER, SymbolToStringTag, UNKNOWN_PATH, @@ -33,9 +39,8 @@ import Identifier from './Identifier'; import Literal from './Literal'; import type * as NodeType from './NodeType'; import type PrivateIdentifier from './PrivateIdentifier'; -import type SpreadElement from './SpreadElement'; -import type Super from './Super'; import { Flag, isFlagSet, setFlag } from './shared/BitFlags'; +import { getChainElementLiteralValueAtPath } from './shared/chainElements'; import { deoptimizeInteraction, type ExpressionEntity, @@ -45,10 +50,8 @@ import { } from './shared/Expression'; import type { ChainElement, ExpressionNode, IncludeChildren, SkippedChain } from './shared/Node'; import { IS_SKIPPED_CHAIN, NodeBase } from './shared/Node'; -import { getChainElementLiteralValueAtPath } from './shared/chainElements'; - -// To avoid infinite recursions -const MAX_PATH_DEPTH = 7; +import type { PatternNode } from './shared/Pattern'; +import type Super from './Super'; function getResolvablePropertyKey(memberExpression: MemberExpression): string | null { return memberExpression.computed @@ -95,7 +98,7 @@ function getStringFromPath(path: PathWithPositions): string { export default class MemberExpression extends NodeBase - implements DeoptimizableEntity, ChainElement + implements DeoptimizableEntity, ChainElement, PatternNode { declare object: ExpressionNode | Super; declare property: ExpressionNode | PrivateIdentifier; @@ -167,7 +170,7 @@ export default class MemberExpression deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { if (this.variable) { this.variable.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); @@ -184,6 +187,11 @@ export default class MemberExpression } } + deoptimizeAssignment(destructuredInitPath: ObjectPath, init: ExpressionEntity) { + this.deoptimizePath(EMPTY_PATH); + init.deoptimizePath([...destructuredInitPath, UnknownKey]); + } + deoptimizeCache(): void { const { expressionsToBeDeoptimized, object } = this; this.expressionsToBeDeoptimized = EMPTY_ARRAY as unknown as DeoptimizableEntity[]; @@ -198,18 +206,20 @@ export default class MemberExpression if (path.length === 0) this.disallowNamespaceReassignment(); if (this.variable) { this.variable.deoptimizePath(path); - } else if (!this.isUndefined && path.length < MAX_PATH_DEPTH) { + } else if (!this.isUndefined) { const propertyKey = this.getPropertyKey(); this.object.deoptimizePath([ propertyKey === UnknownKey ? UnknownNonAccessorKey : propertyKey, - ...path + ...(path.length < MAX_PATH_DEPTH + ? path + : [...path.slice(0, MAX_PATH_DEPTH), UnknownKey as ObjectPathKey]) ]); } } getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { if (this.variable) { @@ -231,7 +241,7 @@ export default class MemberExpression getLiteralValueAtPathAsChainElement( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown | SkippedChain { if (this.variable) { @@ -246,7 +256,7 @@ export default class MemberExpression getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { if (this.variable) { @@ -331,9 +341,38 @@ export default class MemberExpression return true; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + hasEffectsWhenDestructuring( + context: HasEffectsContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + return ( + destructuredInitPath.length > 0 && + init.hasEffectsOnInteractionAtPath( + destructuredInitPath, + NODE_INTERACTION_UNKNOWN_ACCESS, + context + ) + ); + } + + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { if (!this.deoptimized) this.applyDeoptimizations(); - this.includeProperties(context, includeChildrenRecursively); + this.includeProperties( + path, + [ + this.getPropertyKey(), + ...(path.length < MAX_PATH_DEPTH + ? path + : [...path.slice(0, MAX_PATH_DEPTH), UnknownKey as ObjectPathKey]) + ], + context, + includeChildrenRecursively + ); } includeAsAssignmentTarget( @@ -343,21 +382,44 @@ export default class MemberExpression ): void { if (!this.assignmentDeoptimized) this.applyAssignmentDeoptimization(); if (deoptimizeAccess) { - this.include(context, includeChildrenRecursively); + this.includePath([this.getPropertyKey()], context, includeChildrenRecursively); } else { - this.includeProperties(context, includeChildrenRecursively); + this.includeProperties( + EMPTY_PATH, + [this.getPropertyKey()], + context, + includeChildrenRecursively + ); } } - includeCallArguments( - context: InclusionContext, - parameters: readonly (ExpressionEntity | SpreadElement)[] - ): void { + includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { if (this.variable) { - this.variable.includeCallArguments(context, parameters); + this.variable.includeCallArguments(context, interaction); } else { - super.includeCallArguments(context, parameters); + super.includeCallArguments(context, interaction); + } + } + + includeDestructuredIfNecessary( + context: InclusionContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + if ( + (this.included ||= + destructuredInitPath.length > 0 && + !context.brokenFlow && + init.hasEffectsOnInteractionAtPath( + destructuredInitPath, + NODE_INTERACTION_UNKNOWN_ACCESS, + createHasEffectsContext() + )) + ) { + init.includePath(destructuredInitPath, context, false); + return true; } + return false; } initialise(): void { @@ -449,7 +511,7 @@ export default class MemberExpression const variable = this.scope.findVariable(this.object.name); if (variable.isNamespace) { if (this.variable) { - this.scope.context.includeVariableInModule(this.variable); + this.scope.context.includeVariableInModule(this.variable, UNKNOWN_PATH); } this.scope.context.log( LOGLEVEL_WARN, @@ -490,17 +552,21 @@ export default class MemberExpression } private includeProperties( + includedPath: ObjectPath, + objectPath: ObjectPath, context: InclusionContext, includeChildrenRecursively: IncludeChildren ) { if (!this.included) { this.included = true; if (this.variable) { - this.scope.context.includeVariableInModule(this.variable); + this.scope.context.includeVariableInModule(this.variable, includedPath); } + } else if (includedPath.length > 0) { + this.variable?.includePath(includedPath, context); } - this.object.include(context, includeChildrenRecursively); - this.property.include(context, includeChildrenRecursively); + this.object.includePath(objectPath, context, includeChildrenRecursively); + this.property.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/MetaProperty.ts b/src/ast/nodes/MetaProperty.ts index a16a85c07..fa6bf8cef 100644 --- a/src/ast/nodes/MetaProperty.ts +++ b/src/ast/nodes/MetaProperty.ts @@ -46,7 +46,7 @@ export default class MetaProperty extends NodeBase { return path.length > 1 || type !== INTERACTION_ACCESSED; } - include(): void { + includePath(): void { if (!this.included) { this.included = true; if (this.meta.name === IMPORT) { diff --git a/src/ast/nodes/NewExpression.ts b/src/ast/nodes/NewExpression.ts index cc36256d1..69ae15445 100644 --- a/src/ast/nodes/NewExpression.ts +++ b/src/ast/nodes/NewExpression.ts @@ -5,7 +5,12 @@ import type { RenderOptions } from '../../utils/renderHelpers'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions'; import { INTERACTION_ACCESSED, INTERACTION_CALLED } from '../NodeInteractions'; -import { EMPTY_PATH, type ObjectPath, SHARED_RECURSION_TRACKER } from '../utils/PathTracker'; +import { + EMPTY_PATH, + type ObjectPath, + SHARED_RECURSION_TRACKER, + UNKNOWN_PATH +} from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import type { ExpressionNode, IncludeChildren } from './shared/Node'; import { NodeBase } from './shared/Node'; @@ -36,15 +41,19 @@ export default class NewExpression extends NodeBase { return path.length > 0 || type !== INTERACTION_ACCESSED; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { if (!this.deoptimized) this.applyDeoptimizations(); if (includeChildrenRecursively) { - super.include(context, includeChildrenRecursively); + super.includePath(path, context, includeChildrenRecursively); } else { this.included = true; - this.callee.include(context, false); + this.callee.includePath(UNKNOWN_PATH, context, false); } - this.callee.includeCallArguments(context, this.arguments); + this.callee.includeCallArguments(context, this.interaction); } initialise(): void { diff --git a/src/ast/nodes/ObjectExpression.ts b/src/ast/nodes/ObjectExpression.ts index f9c0bb4d4..4c9950b1a 100644 --- a/src/ast/nodes/ObjectExpression.ts +++ b/src/ast/nodes/ObjectExpression.ts @@ -1,35 +1,40 @@ import type MagicString from 'magic-string'; import { BLANK } from '../../utils/blank'; import type { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers'; +import { getCommaSeparatedNodesWithBoundaries } from '../../utils/renderHelpers'; +import { treeshakeNode } from '../../utils/treeshakeNode'; import type { DeoptimizableEntity } from '../DeoptimizableEntity'; -import type { HasEffectsContext } from '../ExecutionContext'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions'; import { EMPTY_PATH, + type EntityPathTracker, type ObjectPath, - type PathTracker, SHARED_RECURSION_TRACKER, + UNKNOWN_PATH, UnknownKey } from '../utils/PathTracker'; import Identifier from './Identifier'; import Literal from './Literal'; import * as NodeType from './NodeType'; import type Property from './Property'; -import SpreadElement from './SpreadElement'; -import { type ExpressionEntity, type LiteralValueOrUnknown } from './shared/Expression'; +import type { ExpressionEntity, LiteralValueOrUnknown } from './shared/Expression'; +import type { IncludeChildren } from './shared/Node'; import { NodeBase } from './shared/Node'; import { ObjectEntity, type ObjectProperty } from './shared/ObjectEntity'; import { OBJECT_PROTOTYPE } from './shared/ObjectPrototype'; +import SpreadElement from './SpreadElement'; export default class ObjectExpression extends NodeBase implements DeoptimizableEntity { declare properties: readonly (Property | SpreadElement)[]; declare type: NodeType.tObjectExpression; private objectEntity: ObjectEntity | null = null; + private protoProp: Property | null = null; deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -48,7 +53,7 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getObjectEntity().getLiteralValueAtPath(path, recursionTracker, origin); @@ -57,7 +62,7 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.getObjectEntity().getReturnExpressionWhenCalledAtPath( @@ -76,12 +81,21 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE return this.getObjectEntity().hasEffectsOnInteractionAtPath(path, interaction, context); } + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ) { + this.included = true; + this.getObjectEntity().includePath(path, context, includeChildrenRecursively); + this.protoProp?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + } + render( code: MagicString, options: RenderOptions, { renderedSurroundingElement }: NodeRenderOptions = BLANK ): void { - super.render(code, options); if ( renderedSurroundingElement === NodeType.ExpressionStatement || renderedSurroundingElement === NodeType.ArrowFunctionExpression @@ -89,6 +103,26 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE code.appendRight(this.start, '('); code.prependLeft(this.end, ')'); } + if (this.properties.length > 0) { + const separatedNodes = getCommaSeparatedNodesWithBoundaries( + this.properties, + code, + this.start + 1, + this.end - 1 + ); + let lastSeparatorPos: number | null = null; + for (const { node, separator, start, end } of separatedNodes) { + if (!node.included) { + treeshakeNode(node, code, start, end); + continue; + } + lastSeparatorPos = separator; + node.render(code, options); + } + if (lastSeparatorPos) { + code.remove(lastSeparatorPos, this.end - 1); + } + } } protected applyDeoptimizations() {} @@ -123,6 +157,7 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE ? property.key.name : String((property.key as Literal).value); if (key === '__proto__' && property.kind === 'init') { + this.protoProp = property; prototype = property.value instanceof Literal && property.value.value === null ? null diff --git a/src/ast/nodes/ObjectPattern.ts b/src/ast/nodes/ObjectPattern.ts index df8b9ef37..dba5598e0 100644 --- a/src/ast/nodes/ObjectPattern.ts +++ b/src/ast/nodes/ObjectPattern.ts @@ -1,4 +1,8 @@ -import type { HasEffectsContext } from '../ExecutionContext'; +import type MagicString from 'magic-string'; +import type { RenderOptions } from '../../utils/renderHelpers'; +import { getCommaSeparatedNodesWithBoundaries } from '../../utils/renderHelpers'; +import { treeshakeNode } from '../../utils/treeshakeNode'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteractionAssigned } from '../NodeInteractions'; import { EMPTY_PATH, type ObjectPath } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; @@ -8,10 +12,10 @@ import type Property from './Property'; import type RestElement from './RestElement'; import type { ExpressionEntity } from './shared/Expression'; import { NodeBase } from './shared/Node'; -import type { PatternNode } from './shared/Pattern'; +import type { DeclarationPatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class ObjectPattern extends NodeBase implements PatternNode { +export default class ObjectPattern extends NodeBase implements DeclarationPatternNode { declare properties: readonly (Property | RestElement)[]; declare type: NodeType.tObjectPattern; @@ -21,24 +25,31 @@ export default class ObjectPattern extends NodeBase implements PatternNode { ): void { for (const property of this.properties) { if (property.type === NodeType.Property) { - (property.value as unknown as PatternNode).addExportedVariables( - variables, - exportNamesByVariable - ); + property.value.addExportedVariables(variables, exportNamesByVariable); } else { property.argument.addExportedVariables(variables, exportNamesByVariable); } } } - declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { + declare( + kind: VariableKind, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): LocalVariable[] { const variables: LocalVariable[] = []; for (const property of this.properties) { - variables.push(...property.declare(kind, init)); + variables.push(...property.declare(kind, destructuredInitPath, init)); } return variables; } + deoptimizeAssignment(destructuredInitPath: ObjectPath, init: ExpressionEntity): void { + for (const property of this.properties) { + property.deoptimizeAssignment(destructuredInitPath, init); + } + } + deoptimizePath(path: ObjectPath): void { if (path.length === 0) { for (const property of this.properties) { @@ -60,9 +71,58 @@ export default class ObjectPattern extends NodeBase implements PatternNode { return false; } + hasEffectsWhenDestructuring( + context: HasEffectsContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + for (const property of this.properties) { + if (property.hasEffectsWhenDestructuring(context, destructuredInitPath, init)) return true; + } + return false; + } + + includeDestructuredIfNecessary( + context: InclusionContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + let included = false; + for (const property of this.properties) { + included = + property.includeDestructuredIfNecessary(context, destructuredInitPath, init) || included; + } + return (this.included ||= included); + } + markDeclarationReached(): void { for (const property of this.properties) { property.markDeclarationReached(); } } + + render(code: MagicString, options: RenderOptions): void { + if (this.properties.length > 0) { + const separatedNodes = getCommaSeparatedNodesWithBoundaries( + this.properties, + code, + this.start + 1, + this.end - 1 + ); + let lastSeparatorPos: number | null = null; + for (const { node, separator, start, end } of separatedNodes) { + if (!node.included) { + treeshakeNode(node, code, start, end); + continue; + } + lastSeparatorPos = separator; + node.render(code, options); + } + if (lastSeparatorPos) { + code.remove(lastSeparatorPos, this.end - 1); + } + } + } + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/Program.ts b/src/ast/nodes/Program.ts index 0441eb8b9..0079db695 100644 --- a/src/ast/nodes/Program.ts +++ b/src/ast/nodes/Program.ts @@ -10,6 +10,7 @@ import { } from '../../utils/renderHelpers'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import { createHasEffectsContext } from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { type IncludeChildren, NodeBase, type StatementNode } from './shared/Node'; @@ -49,11 +50,15 @@ export default class Program extends NodeBase { return false; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; for (const node of this.body) { if (includeChildrenRecursively || node.shouldBeIncluded(context)) { - node.include(context, includeChildrenRecursively); + node.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } } } diff --git a/src/ast/nodes/Property.ts b/src/ast/nodes/Property.ts index 10b5a32c0..b71f96a9b 100644 --- a/src/ast/nodes/Property.ts +++ b/src/ast/nodes/Property.ts @@ -1,22 +1,24 @@ import type MagicString from 'magic-string'; -import type { NormalizedTreeshakingOptions } from '../../rollup/types'; import type { RenderOptions } from '../../utils/renderHelpers'; -import type { HasEffectsContext } from '../ExecutionContext'; -import { UnknownKey } from '../utils/PathTracker'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import { createHasEffectsContext } from '../ExecutionContext'; +import type { ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, UnknownKey } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; +import Identifier from './Identifier'; +import type Literal from './Literal'; import type * as NodeType from './NodeType'; import { Flag, isFlagSet, setFlag } from './shared/BitFlags'; -import { type ExpressionEntity, UNKNOWN_EXPRESSION } from './shared/Expression'; +import { type ExpressionEntity } from './shared/Expression'; import MethodBase from './shared/MethodBase'; -import type { ExpressionNode } from './shared/Node'; -import type { PatternNode } from './shared/Pattern'; +import type { ExpressionNode, IncludeChildren } from './shared/Node'; +import type { DeclarationPatternNode, PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class Property extends MethodBase implements PatternNode { +export default class Property extends MethodBase implements DeclarationPatternNode { declare key: ExpressionNode; declare kind: 'init' | 'get' | 'set'; declare type: NodeType.tProperty; - private declarationInit: ExpressionEntity | null = null; //declare method: boolean; get method(): boolean { @@ -34,25 +36,75 @@ export default class Property extends MethodBase implements PatternNode { this.flags = setFlag(this.flags, Flag.shorthand, value); } - declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { - this.declarationInit = init; - return (this.value as PatternNode).declare(kind, UNKNOWN_EXPRESSION); + declare( + kind: VariableKind, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): LocalVariable[] { + return (this.value as DeclarationPatternNode).declare( + kind, + this.getPathInProperty(destructuredInitPath), + init + ); + } + + deoptimizeAssignment(destructuredInitPath: ObjectPath, init: ExpressionEntity): void { + (this.value as PatternNode).deoptimizeAssignment?.( + this.getPathInProperty(destructuredInitPath), + init + ); } hasEffects(context: HasEffectsContext): boolean { if (!this.deoptimized) this.applyDeoptimizations(); - const propertyReadSideEffects = ( - this.scope.context.options.treeshake as NormalizedTreeshakingOptions - ).propertyReadSideEffects; - return ( - (this.parent.type === 'ObjectPattern' && propertyReadSideEffects === 'always') || - this.key.hasEffects(context) || - this.value.hasEffects(context) + return this.key.hasEffects(context) || this.value.hasEffects(context); + } + + hasEffectsWhenDestructuring( + context: HasEffectsContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + return (this.value as PatternNode).hasEffectsWhenDestructuring?.( + context, + this.getPathInProperty(destructuredInitPath), + init ); } + includeDestructuredIfNecessary( + context: InclusionContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + const path = this.getPathInProperty(destructuredInitPath); + let included = + (this.value as PatternNode).includeDestructuredIfNecessary(context, path, init) || + this.included; + if ((included ||= this.key.hasEffects(createHasEffectsContext()))) { + this.key.includePath(EMPTY_PATH, context, false); + if (!this.value.included) { + this.value.included = true; + // Unfortunately, we need to include the value again now, so that any + // declared variables are properly included. + (this.value as PatternNode).includeDestructuredIfNecessary(context, path, init); + } + } + return (this.included = included); + } + + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ) { + this.included = true; + this.key.includePath(EMPTY_PATH, context, includeChildrenRecursively); + this.value.includePath(path, context, includeChildrenRecursively); + } + markDeclarationReached(): void { - (this.value as PatternNode).markDeclarationReached(); + (this.value as DeclarationPatternNode).markDeclarationReached(); } render(code: MagicString, options: RenderOptions): void { @@ -62,11 +114,17 @@ export default class Property extends MethodBase implements PatternNode { this.value.render(code, options, { isShorthandProperty: this.shorthand }); } - protected applyDeoptimizations(): void { - this.deoptimized = true; - if (this.declarationInit !== null) { - this.declarationInit.deoptimizePath([UnknownKey, UnknownKey]); - this.scope.context.requestTreeshakingPass(); - } + protected applyDeoptimizations(): void {} + + private getPathInProperty(destructuredInitPath: ObjectPath): ObjectPath { + return destructuredInitPath.at(-1) === UnknownKey + ? destructuredInitPath + : // For now, we only consider static paths as we do not know how to + // deoptimize the path in the dynamic case. + this.computed + ? [...destructuredInitPath, UnknownKey] + : this.key instanceof Identifier + ? [...destructuredInitPath, this.key.name] + : [...destructuredInitPath, String((this.key as Literal).value)]; } } diff --git a/src/ast/nodes/PropertyDefinition.ts b/src/ast/nodes/PropertyDefinition.ts index bb1e9903d..a5a2d5e53 100644 --- a/src/ast/nodes/PropertyDefinition.ts +++ b/src/ast/nodes/PropertyDefinition.ts @@ -1,7 +1,7 @@ import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext } from '../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions'; -import type { ObjectPath, PathTracker } from '../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; import { checkEffectForNodes } from '../utils/checkEffectForNodes'; import type Decorator from './Decorator'; import type * as NodeType from './NodeType'; @@ -32,7 +32,7 @@ export default class PropertyDefinition extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.value?.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); } @@ -43,7 +43,7 @@ export default class PropertyDefinition extends NodeBase { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.value @@ -54,7 +54,7 @@ export default class PropertyDefinition extends NodeBase { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.value diff --git a/src/ast/nodes/RestElement.ts b/src/ast/nodes/RestElement.ts index 85cbb2e9f..02148edbe 100644 --- a/src/ast/nodes/RestElement.ts +++ b/src/ast/nodes/RestElement.ts @@ -1,15 +1,16 @@ -import type { HasEffectsContext } from '../ExecutionContext'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteractionAssigned } from '../NodeInteractions'; import { EMPTY_PATH, type ObjectPath, UnknownKey } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; import type Variable from '../variables/Variable'; import type * as NodeType from './NodeType'; -import { type ExpressionEntity, UNKNOWN_EXPRESSION } from './shared/Expression'; +import { type ExpressionEntity } from './shared/Expression'; +import type { IncludeChildren } from './shared/Node'; import { NodeBase } from './shared/Node'; -import type { PatternNode } from './shared/Pattern'; +import type { DeclarationPatternNode, PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class RestElement extends NodeBase implements PatternNode { +export default class RestElement extends NodeBase implements DeclarationPatternNode { declare argument: PatternNode; declare type: NodeType.tRestElement; private declarationInit: ExpressionEntity | null = null; @@ -21,9 +22,21 @@ export default class RestElement extends NodeBase implements PatternNode { this.argument.addExportedVariables(variables, exportNamesByVariable); } - declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { + declare( + kind: VariableKind, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): LocalVariable[] { this.declarationInit = init; - return this.argument.declare(kind, UNKNOWN_EXPRESSION); + return (this.argument as DeclarationPatternNode).declare( + kind, + getIncludedPatternPath(destructuredInitPath), + init + ); + } + + deoptimizeAssignment(destructuredInitPath: ObjectPath, init: ExpressionEntity): void { + this.argument.deoptimizeAssignment(getIncludedPatternPath(destructuredInitPath), init); } deoptimizePath(path: ObjectPath): void { @@ -43,8 +56,44 @@ export default class RestElement extends NodeBase implements PatternNode { ); } + hasEffectsWhenDestructuring( + context: HasEffectsContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + return this.argument.hasEffectsWhenDestructuring( + context, + getIncludedPatternPath(destructuredInitPath), + init + ); + } + + includeDestructuredIfNecessary( + context: InclusionContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean { + return (this.included = + this.argument.includeDestructuredIfNecessary( + context, + getIncludedPatternPath(destructuredInitPath), + init + ) || this.included); + } + + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ) { + this.included = true; + // This should just include the identifier, its properties should be + // included where the variable is used. + this.argument.includePath(EMPTY_PATH, context, includeChildrenRecursively); + } + markDeclarationReached(): void { - this.argument.markDeclarationReached(); + (this.argument as DeclarationPatternNode).markDeclarationReached(); } protected applyDeoptimizations(): void { @@ -55,3 +104,8 @@ export default class RestElement extends NodeBase implements PatternNode { } } } + +const getIncludedPatternPath = (destructuredInitPath: ObjectPath): ObjectPath => + destructuredInitPath.at(-1) === UnknownKey + ? destructuredInitPath + : [...destructuredInitPath, UnknownKey]; diff --git a/src/ast/nodes/ReturnStatement.ts b/src/ast/nodes/ReturnStatement.ts index 624a52165..6f3d08a41 100644 --- a/src/ast/nodes/ReturnStatement.ts +++ b/src/ast/nodes/ReturnStatement.ts @@ -1,6 +1,7 @@ import type MagicString from 'magic-string'; import type { RenderOptions } from '../../utils/renderHelpers'; import { type HasEffectsContext, type InclusionContext } from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { UNKNOWN_EXPRESSION } from './shared/Expression'; import { type ExpressionNode, type IncludeChildren, StatementBase } from './shared/Node'; @@ -15,9 +16,13 @@ export default class ReturnStatement extends StatementBase { return false; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; - this.argument?.include(context, includeChildrenRecursively); + this.argument?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); context.brokenFlow = true; } diff --git a/src/ast/nodes/SequenceExpression.ts b/src/ast/nodes/SequenceExpression.ts index 8ca3f731d..a753b227c 100644 --- a/src/ast/nodes/SequenceExpression.ts +++ b/src/ast/nodes/SequenceExpression.ts @@ -10,7 +10,7 @@ import { treeshakeNode } from '../../utils/treeshakeNode'; import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; -import type { ObjectPath, PathTracker } from '../utils/PathTracker'; +import { type EntityPathTracker, type ObjectPath } from '../utils/PathTracker'; import ExpressionStatement from './ExpressionStatement'; import type * as NodeType from './NodeType'; import type { LiteralValueOrUnknown } from './shared/Expression'; @@ -23,7 +23,7 @@ export default class SequenceExpression extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.expressions[this.expressions.length - 1].deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -38,7 +38,7 @@ export default class SequenceExpression extends NodeBase { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.expressions[this.expressions.length - 1].getLiteralValueAtPath( @@ -67,7 +67,11 @@ export default class SequenceExpression extends NodeBase { ); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; const lastExpression = this.expressions[this.expressions.length - 1]; for (const expression of this.expressions) { @@ -76,7 +80,7 @@ export default class SequenceExpression extends NodeBase { (expression === lastExpression && !(this.parent instanceof ExpressionStatement)) || expression.shouldBeIncluded(context) ) - expression.include(context, includeChildrenRecursively); + expression.includePath(path, context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/SpreadElement.ts b/src/ast/nodes/SpreadElement.ts index 483781572..be6e8c72f 100644 --- a/src/ast/nodes/SpreadElement.ts +++ b/src/ast/nodes/SpreadElement.ts @@ -2,7 +2,12 @@ import type { NormalizedTreeshakingOptions } from '../../rollup/types'; import type { HasEffectsContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { NODE_INTERACTION_UNKNOWN_ACCESS } from '../NodeInteractions'; -import { type ObjectPath, type PathTracker, UNKNOWN_PATH, UnknownKey } from '../utils/PathTracker'; +import { + type EntityPathTracker, + type ObjectPath, + UNKNOWN_PATH, + UnknownKey +} from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { type ExpressionNode, NodeBase } from './shared/Node'; @@ -13,12 +18,12 @@ export default class SpreadElement extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { if (path.length > 0) { this.argument.deoptimizeArgumentsOnInteractionAtPath( interaction, - [UnknownKey, ...path], + UNKNOWN_PATH, recursionTracker ); } diff --git a/src/ast/nodes/StaticBlock.ts b/src/ast/nodes/StaticBlock.ts index d97dad164..ff0878977 100644 --- a/src/ast/nodes/StaticBlock.ts +++ b/src/ast/nodes/StaticBlock.ts @@ -7,6 +7,7 @@ import { import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import BlockScope from '../scopes/BlockScope'; import type ChildScope from '../scopes/ChildScope'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import * as NodeType from './NodeType'; import { type IncludeChildren, StatementBase, type StatementNode } from './shared/Node'; @@ -25,11 +26,15 @@ export default class StaticBlock extends StatementBase { return false; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; for (const node of this.body) { if (includeChildrenRecursively || node.shouldBeIncluded(context)) - node.include(context, includeChildrenRecursively); + node.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/Super.ts b/src/ast/nodes/Super.ts index 5db71aed4..470d4cac3 100644 --- a/src/ast/nodes/Super.ts +++ b/src/ast/nodes/Super.ts @@ -1,5 +1,6 @@ +import type { InclusionContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; -import type { ObjectPath, PathTracker } from '../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; import type Variable from '../variables/Variable'; import type * as NodeType from './NodeType'; import { NodeBase } from './shared/Node'; @@ -15,7 +16,7 @@ export default class Super extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ) { this.variable.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); } @@ -24,10 +25,12 @@ export default class Super extends NodeBase { this.variable.deoptimizePath(path); } - include(): void { + includePath(path: ObjectPath, context: InclusionContext): void { if (!this.included) { this.included = true; - this.scope.context.includeVariableInModule(this.variable); + this.scope.context.includeVariableInModule(this.variable, path); + } else if (path.length > 0) { + this.variable.includePath(path, context); } } } diff --git a/src/ast/nodes/SwitchCase.ts b/src/ast/nodes/SwitchCase.ts index fd78688e0..3437460b7 100644 --- a/src/ast/nodes/SwitchCase.ts +++ b/src/ast/nodes/SwitchCase.ts @@ -6,6 +6,7 @@ import { renderStatementList } from '../../utils/renderHelpers'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { type ExpressionNode, @@ -29,12 +30,16 @@ export default class SwitchCase extends NodeBase { return false; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; - this.test?.include(context, includeChildrenRecursively); + this.test?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); for (const node of this.consequent) { if (includeChildrenRecursively || node.shouldBeIncluded(context)) - node.include(context, includeChildrenRecursively); + node.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/SwitchStatement.ts b/src/ast/nodes/SwitchStatement.ts index f9ae63007..748d35115 100644 --- a/src/ast/nodes/SwitchStatement.ts +++ b/src/ast/nodes/SwitchStatement.ts @@ -7,6 +7,7 @@ import { } from '../ExecutionContext'; import BlockScope from '../scopes/BlockScope'; import type ChildScope from '../scopes/ChildScope'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import type SwitchCase from './SwitchCase'; import type { ExpressionNode, GenericEsTreeNode, IncludeChildren } from './shared/Node'; @@ -46,9 +47,13 @@ export default class SwitchStatement extends StatementBase { return false; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; - this.discriminant.include(context, includeChildrenRecursively); + this.discriminant.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); const { brokenFlow, hasBreak } = context; context.hasBreak = false; let onlyHasBrokenFlow = true; @@ -66,7 +71,7 @@ export default class SwitchStatement extends StatementBase { isCaseIncluded = switchCase.hasEffects(hasEffectsContext); } if (isCaseIncluded) { - switchCase.include(context, includeChildrenRecursively); + switchCase.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); onlyHasBrokenFlow &&= context.brokenFlow && !context.hasBreak; context.hasBreak = false; context.brokenFlow = brokenFlow; diff --git a/src/ast/nodes/TaggedTemplateExpression.ts b/src/ast/nodes/TaggedTemplateExpression.ts index e8f01e6a6..639284732 100644 --- a/src/ast/nodes/TaggedTemplateExpression.ts +++ b/src/ast/nodes/TaggedTemplateExpression.ts @@ -4,16 +4,16 @@ import { logCannotCallNamespace } from '../../utils/logs'; import { type RenderOptions } from '../../utils/renderHelpers'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import { INTERACTION_CALLED } from '../NodeInteractions'; -import type { PathTracker } from '../utils/PathTracker'; -import { EMPTY_PATH, SHARED_RECURSION_TRACKER } from '../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker'; import type Identifier from './Identifier'; import MemberExpression from './MemberExpression'; import * as NodeType from './NodeType'; -import type TemplateLiteral from './TemplateLiteral'; import CallExpressionBase from './shared/CallExpressionBase'; import type { ExpressionEntity } from './shared/Expression'; import { UNKNOWN_EXPRESSION, UNKNOWN_RETURN_EXPRESSION } from './shared/Expression'; import type { ExpressionNode, IncludeChildren } from './shared/Node'; +import type TemplateLiteral from './TemplateLiteral'; export default class TaggedTemplateExpression extends CallExpressionBase { declare quasi: TemplateLiteral; @@ -44,19 +44,23 @@ export default class TaggedTemplateExpression extends CallExpressionBase { ); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { if (!this.deoptimized) this.applyDeoptimizations(); if (includeChildrenRecursively) { - super.include(context, includeChildrenRecursively); + super.includePath(path, context, includeChildrenRecursively); } else { this.included = true; - this.tag.include(context, includeChildrenRecursively); - this.quasi.include(context, includeChildrenRecursively); + this.tag.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.quasi.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } - this.tag.includeCallArguments(context, this.args); + this.tag.includeCallArguments(context, this.interaction); const [returnExpression] = this.getReturnExpression(); if (!returnExpression.included) { - returnExpression.include(context, false); + returnExpression.includePath(UNKNOWN_PATH, context, false); } } @@ -89,7 +93,7 @@ export default class TaggedTemplateExpression extends CallExpressionBase { } protected getReturnExpression( - recursionTracker: PathTracker = SHARED_RECURSION_TRACKER + recursionTracker: EntityPathTracker = SHARED_RECURSION_TRACKER ): [expression: ExpressionEntity, isPure: boolean] { if (this.returnExpression === null) { this.returnExpression = UNKNOWN_RETURN_EXPRESSION; diff --git a/src/ast/nodes/TemplateElement.ts b/src/ast/nodes/TemplateElement.ts index 69f54ff33..1489655d4 100644 --- a/src/ast/nodes/TemplateElement.ts +++ b/src/ast/nodes/TemplateElement.ts @@ -23,7 +23,7 @@ export default class TemplateElement extends NodeBase { return false; } - include(): void { + includePath(): void { this.included = true; } diff --git a/src/ast/nodes/ThisExpression.ts b/src/ast/nodes/ThisExpression.ts index eae0151c4..9f242fc08 100644 --- a/src/ast/nodes/ThisExpression.ts +++ b/src/ast/nodes/ThisExpression.ts @@ -1,11 +1,11 @@ import type MagicString from 'magic-string'; import { LOGLEVEL_WARN } from '../../utils/logging'; import { logThisIsUndefined } from '../../utils/logs'; -import type { HasEffectsContext } from '../ExecutionContext'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ACCESSED } from '../NodeInteractions'; import ModuleScope from '../scopes/ModuleScope'; -import type { ObjectPath, PathTracker } from '../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; import type Variable from '../variables/Variable'; import type * as NodeType from './NodeType'; import { NodeBase } from './shared/Node'; @@ -22,7 +22,7 @@ export default class ThisExpression extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.variable.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); } @@ -42,10 +42,12 @@ export default class ThisExpression extends NodeBase { return this.variable.hasEffectsOnInteractionAtPath(path, interaction, context); } - include(): void { + includePath(path: ObjectPath, context: InclusionContext): void { if (!this.included) { this.included = true; - this.scope.context.includeVariableInModule(this.variable); + this.scope.context.includeVariableInModule(this.variable, path); + } else if (path.length > 0) { + this.variable.includePath(path, context); } } diff --git a/src/ast/nodes/ThrowStatement.ts b/src/ast/nodes/ThrowStatement.ts index 2d9956e82..7b64de077 100644 --- a/src/ast/nodes/ThrowStatement.ts +++ b/src/ast/nodes/ThrowStatement.ts @@ -1,6 +1,7 @@ import type MagicString from 'magic-string'; import type { RenderOptions } from '../../utils/renderHelpers'; import { type InclusionContext } from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { type ExpressionNode, type IncludeChildren, StatementBase } from './shared/Node'; @@ -12,9 +13,13 @@ export default class ThrowStatement extends StatementBase { return true; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; - this.argument.include(context, includeChildrenRecursively); + this.argument.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); context.brokenFlow = true; } diff --git a/src/ast/nodes/TryStatement.ts b/src/ast/nodes/TryStatement.ts index 851d2cdd0..76fdfa4b6 100644 --- a/src/ast/nodes/TryStatement.ts +++ b/src/ast/nodes/TryStatement.ts @@ -1,5 +1,6 @@ import type { NormalizedTreeshakingOptions } from '../../rollup/types'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type BlockStatement from './BlockStatement'; import type CatchClause from './CatchClause'; import type * as NodeType from './NodeType'; @@ -22,7 +23,11 @@ export default class TryStatement extends StatementBase { ); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { const tryCatchDeoptimization = ( this.scope.context.options.treeshake as NormalizedTreeshakingOptions )?.tryCatchDeoptimization; @@ -30,7 +35,8 @@ export default class TryStatement extends StatementBase { if (!this.directlyIncluded || !tryCatchDeoptimization) { this.included = true; this.directlyIncluded = true; - this.block.include( + this.block.includePath( + UNKNOWN_PATH, context, tryCatchDeoptimization ? INCLUDE_PARAMETERS : includeChildrenRecursively ); @@ -44,9 +50,9 @@ export default class TryStatement extends StatementBase { } } if (this.handler !== null) { - this.handler.include(context, includeChildrenRecursively); + this.handler.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); context.brokenFlow = brokenFlow; } - this.finalizer?.include(context, includeChildrenRecursively); + this.finalizer?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/UnaryExpression.ts b/src/ast/nodes/UnaryExpression.ts index aa8f325c3..b8f72c42d 100644 --- a/src/ast/nodes/UnaryExpression.ts +++ b/src/ast/nodes/UnaryExpression.ts @@ -2,7 +2,7 @@ import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ACCESSED, NODE_INTERACTION_UNKNOWN_ASSIGNMENT } from '../NodeInteractions'; -import { EMPTY_PATH, type ObjectPath, type PathTracker } from '../utils/PathTracker'; +import { EMPTY_PATH, type EntityPathTracker, type ObjectPath } from '../utils/PathTracker'; import Identifier from './Identifier'; import type { LiteralValue } from './Literal'; import type * as NodeType from './NodeType'; @@ -34,7 +34,7 @@ export default class UnaryExpression extends NodeBase { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { if (path.length > 0) return UnknownValue; diff --git a/src/ast/nodes/UnknownNode.ts b/src/ast/nodes/UnknownNode.ts index 60ebf1360..112eb1269 100644 --- a/src/ast/nodes/UnknownNode.ts +++ b/src/ast/nodes/UnknownNode.ts @@ -1,4 +1,5 @@ import type { InclusionContext } from '../ExecutionContext'; +import type { ObjectPath } from '../utils/PathTracker'; import { NodeBase } from './shared/Node'; export default class UnknownNode extends NodeBase { @@ -6,7 +7,7 @@ export default class UnknownNode extends NodeBase { return true; } - include(context: InclusionContext): void { - super.include(context, true); + includePath(path: ObjectPath, context: InclusionContext): void { + super.includePath(path, context, true); } } diff --git a/src/ast/nodes/UpdateExpression.ts b/src/ast/nodes/UpdateExpression.ts index 860bdd927..311efff60 100644 --- a/src/ast/nodes/UpdateExpression.ts +++ b/src/ast/nodes/UpdateExpression.ts @@ -31,7 +31,11 @@ export default class UpdateExpression extends NodeBase { return path.length > 1 || type !== INTERACTION_ACCESSED; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren) { + includePath( + _: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ) { if (!this.deoptimized) this.applyDeoptimizations(); this.included = true; this.argument.includeAsAssignmentTarget(context, includeChildrenRecursively, true); diff --git a/src/ast/nodes/VariableDeclaration.ts b/src/ast/nodes/VariableDeclaration.ts index dd787ab79..89be6e470 100644 --- a/src/ast/nodes/VariableDeclaration.ts +++ b/src/ast/nodes/VariableDeclaration.ts @@ -12,8 +12,10 @@ import { getSystemExportStatement, renderSystemExportExpression } from '../../utils/systemJsRendering'; +import { treeshakeNode } from '../../utils/treeshakeNode'; import type { InclusionContext } from '../ExecutionContext'; -import { EMPTY_PATH } from '../utils/PathTracker'; +import type { ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, UNKNOWN_PATH } from '../utils/PathTracker'; import type Variable from '../variables/Variable'; import ArrayPattern from './ArrayPattern'; import Identifier, { type IdentifierWithVariable } from './Identifier'; @@ -57,7 +59,8 @@ export default class VariableDeclaration extends NodeBase { return false; } - include( + includePath( + _path: ObjectPath, context: InclusionContext, includeChildrenRecursively: IncludeChildren, { asSingleStatement }: InclusionOptions = BLANK @@ -65,10 +68,10 @@ export default class VariableDeclaration extends NodeBase { this.included = true; for (const declarator of this.declarations) { if (includeChildrenRecursively || declarator.shouldBeIncluded(context)) - declarator.include(context, includeChildrenRecursively); + declarator.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); const { id, init } = declarator; if (asSingleStatement) { - id.include(context, includeChildrenRecursively); + id.includePath(EMPTY_PATH, context, includeChildrenRecursively); } if ( init && @@ -76,7 +79,7 @@ export default class VariableDeclaration extends NodeBase { !init.included && (id instanceof ObjectPattern || id instanceof ArrayPattern) ) { - init.include(context, includeChildrenRecursively); + init.includePath(EMPTY_PATH, context, includeChildrenRecursively); } } } @@ -183,8 +186,7 @@ export default class VariableDeclaration extends NodeBase { ); for (const { node, start, separator, contentEnd, end } of separatedNodes) { if (!node.included) { - code.remove(start, end); - node.removeAnnotations(code); + treeshakeNode(node, code, start, end); continue; } node.render(code, options); diff --git a/src/ast/nodes/VariableDeclarator.ts b/src/ast/nodes/VariableDeclarator.ts index 2a96aba44..f48a97737 100644 --- a/src/ast/nodes/VariableDeclarator.ts +++ b/src/ast/nodes/VariableDeclarator.ts @@ -1,4 +1,5 @@ import type MagicString from 'magic-string'; +import type { NormalizedTreeshakingOptions } from '../../rollup/types'; import { BLANK } from '../../utils/blank'; import { isReassignedExportsMember } from '../../utils/reassignedExportsMember'; import { @@ -7,24 +8,24 @@ import { type RenderOptions } from '../../utils/renderHelpers'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; -import type { ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, type ObjectPath } from '../utils/PathTracker'; import { UNDEFINED_EXPRESSION } from '../values'; import ClassExpression from './ClassExpression'; import Identifier from './Identifier'; import * as NodeType from './NodeType'; import { type ExpressionNode, type IncludeChildren, NodeBase } from './shared/Node'; -import type { PatternNode } from './shared/Pattern'; +import type { DeclarationPatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; export default class VariableDeclarator extends NodeBase { - declare id: PatternNode; + declare id: DeclarationPatternNode; declare init: ExpressionNode | null; declare type: NodeType.tVariableDeclarator; declare isUsingDeclaration: boolean; declareDeclarator(kind: VariableKind, isUsingDeclaration: boolean): void { this.isUsingDeclaration = isUsingDeclaration; - this.id.declare(kind, this.init || UNDEFINED_EXPRESSION); + this.id.declare(kind, EMPTY_PATH, this.init || UNDEFINED_EXPRESSION); } deoptimizePath(path: ObjectPath): void { @@ -35,17 +36,30 @@ export default class VariableDeclarator extends NodeBase { if (!this.deoptimized) this.applyDeoptimizations(); const initEffect = this.init?.hasEffects(context); this.id.markDeclarationReached(); - return initEffect || this.id.hasEffects(context) || this.isUsingDeclaration; + return ( + initEffect || + this.isUsingDeclaration || + this.id.hasEffects(context) || + ((this.scope.context.options.treeshake as NormalizedTreeshakingOptions) + .propertyReadSideEffects && + this.id.hasEffectsWhenDestructuring(context, EMPTY_PATH, this.init || UNDEFINED_EXPRESSION)) + ); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { const { deoptimized, id, init } = this; if (!deoptimized) this.applyDeoptimizations(); this.included = true; - init?.include(context, includeChildrenRecursively); + init?.includePath(EMPTY_PATH, context, includeChildrenRecursively); id.markDeclarationReached(); - if (includeChildrenRecursively || id.shouldBeIncluded(context)) { - id.include(context, includeChildrenRecursively); + if (includeChildrenRecursively) { + id.includePath(EMPTY_PATH, context, includeChildrenRecursively); + } else { + id.includeDestructuredIfNecessary(context, EMPTY_PATH, init || UNDEFINED_EXPRESSION); } } diff --git a/src/ast/nodes/WhileStatement.ts b/src/ast/nodes/WhileStatement.ts index a710564cd..65f1a8506 100644 --- a/src/ast/nodes/WhileStatement.ts +++ b/src/ast/nodes/WhileStatement.ts @@ -1,4 +1,5 @@ import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { type ExpressionNode, @@ -18,9 +19,13 @@ export default class WhileStatement extends StatementBase { return hasLoopBodyEffects(context, this.body); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { this.included = true; - this.test.include(context, includeChildrenRecursively); + this.test.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); includeLoopBody(context, this.body, includeChildrenRecursively); } } diff --git a/src/ast/nodes/shared/BitFlags.ts b/src/ast/nodes/shared/BitFlags.ts index 0d668f841..04b51af39 100644 --- a/src/ast/nodes/shared/BitFlags.ts +++ b/src/ast/nodes/shared/BitFlags.ts @@ -22,7 +22,8 @@ export const enum Flag { tail = 1 << 20, prefix = 1 << 21, generator = 1 << 22, - expression = 1 << 23 + expression = 1 << 23, + destructuringDeoptimized = 1 << 23 } export function isFlagSet(flags: number, flag: Flag): boolean { diff --git a/src/ast/nodes/shared/CallExpressionBase.ts b/src/ast/nodes/shared/CallExpressionBase.ts index 04c045b5d..8efca6957 100644 --- a/src/ast/nodes/shared/CallExpressionBase.ts +++ b/src/ast/nodes/shared/CallExpressionBase.ts @@ -3,7 +3,7 @@ import type { DeoptimizableEntity } from '../../DeoptimizableEntity'; import type { HasEffectsContext } from '../../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../../NodeInteractions'; import { INTERACTION_ASSIGNED, INTERACTION_CALLED } from '../../NodeInteractions'; -import { type ObjectPath, type PathTracker, UNKNOWN_PATH } from '../../utils/PathTracker'; +import { type EntityPathTracker, type ObjectPath, UNKNOWN_PATH } from '../../utils/PathTracker'; import { type ExpressionEntity, type LiteralValueOrUnknown, @@ -22,7 +22,7 @@ export default abstract class CallExpressionBase extends NodeBase implements Deo deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { const { args } = interaction; const [returnExpression, isPure] = this.getReturnExpression(recursionTracker); @@ -84,7 +84,7 @@ export default abstract class CallExpressionBase extends NodeBase implements Deo getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { const [returnExpression] = this.getReturnExpression(recursionTracker); @@ -105,7 +105,7 @@ export default abstract class CallExpressionBase extends NodeBase implements Deo getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { const returnExpression = this.getReturnExpression(recursionTracker); @@ -162,6 +162,6 @@ export default abstract class CallExpressionBase extends NodeBase implements Deo } protected abstract getReturnExpression( - recursionTracker?: PathTracker + recursionTracker?: EntityPathTracker ): [expression: ExpressionEntity, isPure: boolean]; } diff --git a/src/ast/nodes/shared/ClassNode.ts b/src/ast/nodes/shared/ClassNode.ts index 43ca97814..8439cc659 100644 --- a/src/ast/nodes/shared/ClassNode.ts +++ b/src/ast/nodes/shared/ClassNode.ts @@ -1,17 +1,21 @@ import type { DeoptimizableEntity } from '../../DeoptimizableEntity'; -import type { HasEffectsContext, InclusionContext } from '../../ExecutionContext'; +import { + createInclusionContext, + type HasEffectsContext, + type InclusionContext +} from '../../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../../NodeInteractions'; import { INTERACTION_CALLED } from '../../NodeInteractions'; import ChildScope from '../../scopes/ChildScope'; +import { checkEffectForNodes } from '../../utils/checkEffectForNodes'; import { EMPTY_PATH, + type EntityPathTracker, type ObjectPath, - type PathTracker, SHARED_RECURSION_TRACKER, UNKNOWN_PATH, UnknownKey } from '../../utils/PathTracker'; -import { checkEffectForNodes } from '../../utils/checkEffectForNodes'; import type ClassBody from '../ClassBody'; import type Decorator from '../Decorator'; import Identifier from '../Identifier'; @@ -39,7 +43,7 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -58,7 +62,7 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getObjectEntity().getLiteralValueAtPath(path, recursionTracker, origin); @@ -67,7 +71,7 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.getObjectEntity().getReturnExpressionWhenCalledAtPath( @@ -99,21 +103,26 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity { : this.getObjectEntity().hasEffectsOnInteractionAtPath(path, interaction, context); } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { if (!this.deoptimized) this.applyDeoptimizations(); this.included = true; - this.superClass?.include(context, includeChildrenRecursively); - this.body.include(context, includeChildrenRecursively); - for (const decorator of this.decorators) decorator.include(context, includeChildrenRecursively); + this.superClass?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.body.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + for (const decorator of this.decorators) + decorator.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); if (this.id) { this.id.markDeclarationReached(); - this.id.include(); + this.id.includePath(UNKNOWN_PATH, createInclusionContext()); } } initialise(): void { super.initialise(); - this.id?.declare('class', this); + this.id?.declare('class', EMPTY_PATH, this); for (const method of this.body.body) { if (method instanceof MethodDefinition && method.kind === 'constructor') { this.classConstructor = method; @@ -179,7 +188,7 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity { kind: 'init', property: new ObjectEntity( dynamicMethods, - this.superClass ? new ObjectMember(this.superClass, 'prototype') : OBJECT_PROTOTYPE + this.superClass ? new ObjectMember(this.superClass, ['prototype']) : OBJECT_PROTOTYPE ) }); return (this.objectEntity = new ObjectEntity( diff --git a/src/ast/nodes/shared/Expression.ts b/src/ast/nodes/shared/Expression.ts index 146ae33a8..af77ce213 100644 --- a/src/ast/nodes/shared/Expression.ts +++ b/src/ast/nodes/shared/Expression.ts @@ -2,10 +2,9 @@ import type { DeoptimizableEntity } from '../../DeoptimizableEntity'; import type { WritableEntity } from '../../Entity'; import type { HasEffectsContext, InclusionContext } from '../../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../../NodeInteractions'; -import type { ObjectPath, PathTracker, SymbolToStringTag } from '../../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath, SymbolToStringTag } from '../../utils/PathTracker'; import { UNKNOWN_PATH } from '../../utils/PathTracker'; import type { LiteralValue } from '../Literal'; -import type SpreadElement from '../SpreadElement'; import { Flag, isFlagSet, setFlag } from './BitFlags'; import type { IncludeChildren } from './Node'; @@ -39,7 +38,7 @@ export class ExpressionEntity implements WritableEntity { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, _path: ObjectPath, - _recursionTracker: PathTracker + _recursionTracker: EntityPathTracker ): void { deoptimizeInteraction(interaction); } @@ -53,7 +52,7 @@ export class ExpressionEntity implements WritableEntity { */ getLiteralValueAtPath( _path: ObjectPath, - _recursionTracker: PathTracker, + _recursionTracker: EntityPathTracker, _origin: DeoptimizableEntity ): LiteralValueOrUnknown { return UnknownValue; @@ -62,7 +61,7 @@ export class ExpressionEntity implements WritableEntity { getReturnExpressionWhenCalledAtPath( _path: ObjectPath, _interaction: NodeInteractionCalled, - _recursionTracker: PathTracker, + _recursionTracker: EntityPathTracker, _origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return UNKNOWN_RETURN_EXPRESSION; @@ -76,7 +75,8 @@ export class ExpressionEntity implements WritableEntity { return true; } - include( + includePath( + _path: ObjectPath, _context: InclusionContext, _includeChildrenRecursively: IncludeChildren, _options?: InclusionOptions @@ -84,12 +84,9 @@ export class ExpressionEntity implements WritableEntity { this.included = true; } - includeCallArguments( - context: InclusionContext, - parameters: readonly (ExpressionEntity | SpreadElement)[] - ): void { - for (const argument of parameters) { - argument.include(context, false); + includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { + for (const argument of interaction.args) { + argument?.includePath(UNKNOWN_PATH, context, false); } } diff --git a/src/ast/nodes/shared/FunctionBase.ts b/src/ast/nodes/shared/FunctionBase.ts index e57af88d0..5f363b6e6 100644 --- a/src/ast/nodes/shared/FunctionBase.ts +++ b/src/ast/nodes/shared/FunctionBase.ts @@ -8,17 +8,15 @@ import { NODE_INTERACTION_UNKNOWN_CALL } from '../../NodeInteractions'; import type ReturnValueScope from '../../scopes/ReturnValueScope'; -import type { ObjectPath, PathTracker } from '../../utils/PathTracker'; -import { UNKNOWN_PATH, UnknownKey } from '../../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; +import { EMPTY_PATH, UNKNOWN_PATH, UnknownKey } from '../../utils/PathTracker'; import { UNDEFINED_EXPRESSION } from '../../values'; import type ParameterVariable from '../../variables/ParameterVariable'; import type Variable from '../../variables/Variable'; import BlockStatement from '../BlockStatement'; import type ExportDefaultDeclaration from '../ExportDefaultDeclaration'; -import Identifier from '../Identifier'; import * as NodeType from '../NodeType'; import RestElement from '../RestElement'; -import SpreadElement from '../SpreadElement'; import type VariableDeclarator from '../VariableDeclarator'; import { Flag, isFlagSet, setFlag } from './BitFlags'; import type { ExpressionEntity, LiteralValueOrUnknown } from './Expression'; @@ -30,13 +28,11 @@ import { NodeBase } from './Node'; import type { ObjectEntity } from './ObjectEntity'; -import type { PatternNode } from './Pattern'; - -type InteractionCalledArguments = NodeInteractionCalled['args']; +import type { DeclarationPatternNode } from './Pattern'; export default abstract class FunctionBase extends NodeBase { declare body: BlockStatement | ExpressionNode; - declare params: PatternNode[]; + declare params: DeclarationPatternNode[]; declare preventChildBlockScope: true; declare scope: ReturnValueScope; @@ -64,58 +60,13 @@ export default abstract class FunctionBase extends NodeBase { this.flags = setFlag(this.flags, Flag.generator, value); } - private updateParameterVariableValues(_arguments: InteractionCalledArguments): void { - for (let position = 0; position < this.params.length; position++) { - const parameter = this.params[position]; - if (!(parameter instanceof Identifier)) { - continue; - } - const parameterVariable = parameter.variable as ParameterVariable; - const argument = _arguments[position + 1] ?? UNDEFINED_EXPRESSION; - parameterVariable.updateKnownValue(argument); - } - } - - private deoptimizeParameterVariableValues() { - for (const parameter of this.params) { - if (parameter instanceof Identifier) { - const parameterVariable = parameter.variable as ParameterVariable; - parameterVariable.markReassigned(); - } - } - } - - protected objectEntity: ObjectEntity | null = null; - deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { - if (interaction.type === INTERACTION_CALLED) { - const { parameters } = this.scope; - const { args } = interaction; - let hasRest = false; - for (let position = 0; position < args.length - 1; position++) { - const parameter = this.params[position]; - // Only the "this" argument arg[0] can be null - const argument = args[position + 1]!; - if (argument instanceof SpreadElement) { - this.deoptimizeParameterVariableValues(); - } - if (hasRest || parameter instanceof RestElement) { - hasRest = true; - argument.deoptimizePath(UNKNOWN_PATH); - } else if (parameter instanceof Identifier) { - parameters[position][0].addEntityToBeDeoptimized(argument); - this.addArgumentToBeDeoptimized(argument); - } else if (parameter) { - argument.deoptimizePath(UNKNOWN_PATH); - } else { - this.addArgumentToBeDeoptimized(argument); - } - } - this.updateParameterVariableValues(args); + if (interaction.type === INTERACTION_CALLED && path.length === 0) { + this.scope.deoptimizeArgumentsOnCall(interaction); } else { this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -131,18 +82,13 @@ export default abstract class FunctionBase extends NodeBase { // A reassignment of UNKNOWN_PATH is considered equivalent to having lost track // which means the return expression and parameters need to be reassigned this.scope.getReturnExpression().deoptimizePath(UNKNOWN_PATH); - for (const parameterList of this.scope.parameters) { - for (const parameter of parameterList) { - parameter.deoptimizePath(UNKNOWN_PATH); - parameter.markReassigned(); - } - } + this.scope.deoptimizeAllParameters(); } } getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getObjectEntity().getLiteralValueAtPath(path, recursionTracker, origin); @@ -151,7 +97,7 @@ export default abstract class FunctionBase extends NodeBase { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { if (path.length > 0) { @@ -207,8 +153,20 @@ export default abstract class FunctionBase extends NodeBase { return true; } } - for (const parameter of this.params) { - if (parameter.hasEffects(context)) return true; + const { propertyReadSideEffects } = this.scope.context.options + .treeshake as NormalizedTreeshakingOptions; + for (let index = 0; index < this.params.length; index++) { + const parameter = this.params[index]; + if ( + parameter.hasEffects(context) || + (propertyReadSideEffects && + parameter.hasEffectsWhenDestructuring( + context, + EMPTY_PATH, + interaction.args[index + 1] || UNDEFINED_EXPRESSION + )) + ) + return true; } return false; } @@ -228,25 +186,25 @@ export default abstract class FunctionBase extends NodeBase { } private parameterVariableValuesDeoptimized = false; - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { - if (!this.parameterVariableValuesDeoptimized && !this.onlyFunctionCallUsed()) { + + includePath( + _path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { + if (!(this.parameterVariableValuesDeoptimized || this.onlyFunctionCallUsed())) { this.parameterVariableValuesDeoptimized = true; - this.deoptimizeParameterVariableValues(); + this.scope.reassignAllParameters(); } if (!this.deoptimized) this.applyDeoptimizations(); this.included = true; const { brokenFlow } = context; context.brokenFlow = false; - this.body.include(context, includeChildrenRecursively); + this.body.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); context.brokenFlow = brokenFlow; } - includeCallArguments( - context: InclusionContext, - parameters: readonly (ExpressionEntity | SpreadElement)[] - ): void { - this.scope.includeCallArguments(context, parameters); - } + includeCallArguments = this.scope.includeCallArguments.bind(this.scope); initialise(): void { super.initialise(); @@ -276,11 +234,12 @@ export default abstract class FunctionBase extends NodeBase { (parameter: GenericEsTreeNode) => new (context.getNodeConstructor(parameter.type))(this, scope).parseNode( parameter - ) as unknown as PatternNode + ) as unknown as DeclarationPatternNode )); scope.addParameterVariables( parameters.map( - parameter => parameter.declare('parameter', UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => + parameter.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] ), parameters[parameters.length - 1] instanceof RestElement ); @@ -288,8 +247,6 @@ export default abstract class FunctionBase extends NodeBase { return super.parseNode(esTreeNode); } - protected addArgumentToBeDeoptimized(_argument: ExpressionEntity) {} - protected applyDeoptimizations() {} protected abstract getObjectEntity(): ObjectEntity; diff --git a/src/ast/nodes/shared/FunctionNode.ts b/src/ast/nodes/shared/FunctionNode.ts index 7b3399615..74e77441b 100644 --- a/src/ast/nodes/shared/FunctionNode.ts +++ b/src/ast/nodes/shared/FunctionNode.ts @@ -1,23 +1,31 @@ -import { type HasEffectsContext, type InclusionContext } from '../../ExecutionContext'; +import { + createInclusionContext, + type HasEffectsContext, + type InclusionContext +} from '../../ExecutionContext'; import type { NodeInteraction } from '../../NodeInteractions'; import { INTERACTION_CALLED } from '../../NodeInteractions'; import type ChildScope from '../../scopes/ChildScope'; import FunctionScope from '../../scopes/FunctionScope'; -import type { ObjectPath, PathTracker } from '../../utils/PathTracker'; +import { + EMPTY_PATH, + type EntityPathTracker, + type ObjectPath, + UNKNOWN_PATH +} from '../../utils/PathTracker'; import type BlockStatement from '../BlockStatement'; import Identifier, { type IdentifierWithVariable } from '../Identifier'; -import type { ExpressionEntity } from './Expression'; import { UNKNOWN_EXPRESSION } from './Expression'; import FunctionBase from './FunctionBase'; import { type IncludeChildren } from './Node'; import { ObjectEntity } from './ObjectEntity'; import { OBJECT_PROTOTYPE } from './ObjectPrototype'; -import type { PatternNode } from './Pattern'; +import type { DeclarationPatternNode } from './Pattern'; export default class FunctionNode extends FunctionBase { declare body: BlockStatement; declare id: IdentifierWithVariable | null; - declare params: PatternNode[]; + declare params: DeclarationPatternNode[]; declare preventChildBlockScope: true; declare scope: FunctionScope; protected objectEntity: ObjectEntity | null = null; @@ -28,18 +36,18 @@ export default class FunctionNode extends FunctionBase { this.constructedEntity = new ObjectEntity(Object.create(null), OBJECT_PROTOTYPE); // This makes sure that all deoptimizations of "this" are applied to the // constructed entity. - this.scope.thisVariable.addEntityToBeDeoptimized(this.constructedEntity); + this.scope.thisVariable.addArgumentValue(this.constructedEntity); } deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { super.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); if (interaction.type === INTERACTION_CALLED && path.length === 0 && interaction.args[0]) { // args[0] is the "this" argument - this.scope.thisVariable.addEntityToBeDeoptimized(interaction.args[0]); + this.scope.thisVariable.addArgumentValue(interaction.args[0]); } } @@ -90,24 +98,24 @@ export default class FunctionNode extends FunctionBase { return false; } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { - super.include(context, includeChildrenRecursively); - this.id?.include(); + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ): void { + super.includePath(path, context, includeChildrenRecursively); + this.id?.includePath(UNKNOWN_PATH, createInclusionContext()); const hasArguments = this.scope.argumentsVariable.included; for (const parameter of this.params) { if (!(parameter instanceof Identifier) || hasArguments) { - parameter.include(context, includeChildrenRecursively); + parameter.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); } } } initialise(): void { super.initialise(); - this.id?.declare('function', this); - } - - protected addArgumentToBeDeoptimized(argument: ExpressionEntity) { - this.scope.argumentsVariable.addArgumentToBeDeoptimized(argument); + this.id?.declare('function', EMPTY_PATH, this); } protected getObjectEntity(): ObjectEntity { diff --git a/src/ast/nodes/shared/IdentifierBase.ts b/src/ast/nodes/shared/IdentifierBase.ts index 684d7f2de..72241cc67 100644 --- a/src/ast/nodes/shared/IdentifierBase.ts +++ b/src/ast/nodes/shared/IdentifierBase.ts @@ -11,12 +11,11 @@ import { INTERACTION_CALLED, NODE_INTERACTION_UNKNOWN_ACCESS } from '../../NodeInteractions'; -import type { ObjectPath, PathTracker } from '../../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; import { EMPTY_PATH } from '../../utils/PathTracker'; import GlobalVariable from '../../variables/GlobalVariable'; import LocalVariable from '../../variables/LocalVariable'; import type Variable from '../../variables/Variable'; -import type SpreadElement from '../SpreadElement'; import { Flag, isFlagSet, setFlag } from './BitFlags'; import type { ExpressionEntity, LiteralValueOrUnknown } from './Expression'; import { UNKNOWN_EXPRESSION } from './Expression'; @@ -45,7 +44,7 @@ export default class IdentifierBase extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.variable!.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); } @@ -61,7 +60,7 @@ export default class IdentifierBase extends NodeBase { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getVariableRespectingTDZ()!.getLiteralValueAtPath(path, recursionTracker, origin); @@ -70,7 +69,7 @@ export default class IdentifierBase extends NodeBase { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { const [expression, isPure] = @@ -128,21 +127,20 @@ export default class IdentifierBase extends NodeBase { } } - include(): void { + includePath(path: ObjectPath, context: InclusionContext): void { if (!this.deoptimized) this.applyDeoptimizations(); if (!this.included) { this.included = true; if (this.variable !== null) { - this.scope.context.includeVariableInModule(this.variable); + this.scope.context.includeVariableInModule(this.variable, path); } + } else if (path.length > 0) { + this.variable?.includePath(path, context); } } - includeCallArguments( - context: InclusionContext, - parameters: readonly (ExpressionEntity | SpreadElement)[] - ): void { - this.variable!.includeCallArguments(context, parameters); + includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { + this.variable!.includeCallArguments(context, interaction); } isPossibleTDZ(): boolean { diff --git a/src/ast/nodes/shared/JSXElementBase.ts b/src/ast/nodes/shared/JSXElementBase.ts index 6f8b08e79..62474cc65 100644 --- a/src/ast/nodes/shared/JSXElementBase.ts +++ b/src/ast/nodes/shared/JSXElementBase.ts @@ -3,6 +3,7 @@ import type { NormalizedJsxOptions } from '../../../rollup/types'; import { getRenderedJsxChildren } from '../../../utils/jsx'; import type { RenderOptions } from '../../../utils/renderHelpers'; import type { InclusionContext } from '../../ExecutionContext'; +import type { ObjectPath } from '../../utils/PathTracker'; import type Variable from '../../variables/Variable'; import JSXEmptyExpression from '../JSXEmptyExpression'; import JSXExpressionContainer from '../JSXExpressionContainer'; @@ -26,7 +27,11 @@ export default class JSXElementBase extends NodeBase { } } - include(context: InclusionContext, includeChildrenRecursively: IncludeChildren) { + includePath( + path: ObjectPath, + context: InclusionContext, + includeChildrenRecursively: IncludeChildren + ) { if (!this.included) { const { factory, importSource, mode } = this.jsxMode; if (factory) { @@ -39,7 +44,7 @@ export default class JSXElementBase extends NodeBase { ); } } - super.include(context, includeChildrenRecursively); + super.includePath(path, context, includeChildrenRecursively); } protected applyDeoptimizations() {} diff --git a/src/ast/nodes/shared/MethodBase.ts b/src/ast/nodes/shared/MethodBase.ts index b893dbc61..5ca69cfa0 100644 --- a/src/ast/nodes/shared/MethodBase.ts +++ b/src/ast/nodes/shared/MethodBase.ts @@ -9,8 +9,8 @@ import { } from '../../NodeInteractions'; import { EMPTY_PATH, + type EntityPathTracker, type ObjectPath, - type PathTracker, SHARED_RECURSION_TRACKER } from '../../utils/PathTracker'; import type PrivateIdentifier from '../PrivateIdentifier'; @@ -21,12 +21,12 @@ import { UNKNOWN_RETURN_EXPRESSION } from './Expression'; import { type ExpressionNode, NodeBase } from './Node'; -import type { PatternNode } from './Pattern'; +import type { DeclarationPatternNode } from './Pattern'; export default class MethodBase extends NodeBase implements DeoptimizableEntity { declare key: ExpressionNode | PrivateIdentifier; declare kind: 'constructor' | 'method' | 'init' | 'get' | 'set'; - declare value: ExpressionNode | (ExpressionNode & PatternNode); + declare value: ExpressionNode | (ExpressionNode & DeclarationPatternNode); get computed(): boolean { return isFlagSet(this.flags, Flag.computed); @@ -40,7 +40,7 @@ export default class MethodBase extends NodeBase implements DeoptimizableEntity deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { if (interaction.type === INTERACTION_ACCESSED && this.kind === 'get' && path.length === 0) { return this.value.deoptimizeArgumentsOnInteractionAtPath( @@ -81,7 +81,7 @@ export default class MethodBase extends NodeBase implements DeoptimizableEntity getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getAccessedValue()[0].getLiteralValueAtPath(path, recursionTracker, origin); @@ -90,7 +90,7 @@ export default class MethodBase extends NodeBase implements DeoptimizableEntity getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.getAccessedValue()[0].getReturnExpressionWhenCalledAtPath( diff --git a/src/ast/nodes/shared/MultiExpression.ts b/src/ast/nodes/shared/MultiExpression.ts index 5981af20b..5b1279355 100644 --- a/src/ast/nodes/shared/MultiExpression.ts +++ b/src/ast/nodes/shared/MultiExpression.ts @@ -1,7 +1,7 @@ import type { DeoptimizableEntity } from '../../DeoptimizableEntity'; import type { HasEffectsContext } from '../../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../../NodeInteractions'; -import type { ObjectPath, PathTracker } from '../../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; import { ExpressionEntity } from './Expression'; export class MultiExpression extends ExpressionEntity { @@ -18,7 +18,7 @@ export class MultiExpression extends ExpressionEntity { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return [ diff --git a/src/ast/nodes/shared/Node.ts b/src/ast/nodes/shared/Node.ts index 7e6aee31b..6be0f33bf 100644 --- a/src/ast/nodes/shared/Node.ts +++ b/src/ast/nodes/shared/Node.ts @@ -18,8 +18,8 @@ import { INTERACTION_ASSIGNED } from '../../NodeInteractions'; import type ChildScope from '../../scopes/ChildScope'; import { EMPTY_PATH, + type EntityPathTracker, type ObjectPath, - type PathTracker, UNKNOWN_PATH } from '../../utils/PathTracker'; import type Variable from '../../variables/Variable'; @@ -78,11 +78,19 @@ export interface Node extends Entity { hasEffectsAsAssignmentTarget(context: HasEffectsContext, checkAccess: boolean): boolean; /** - * Includes the node in the bundle. If the flag is not set, children are - * usually included if they are necessary for this node (e.g. a function body) - * or if they have effects. Necessary variables need to be included as well. + * Includes the given path of the Node in the bundle. If + * "includeChildrenRecursively" is true, children of this path are included + * unconditionally. Otherwise, including a given path means that the value of + * this path is needed, but not necessarily its children if it is an object. + * Example: + * if (x.a.b) { ... } + * would include the path ['a','b'] of the variable x but none of its children + * because those are not needed to get the literal value. + * On the other hand to include all children, we extend the path with + * "UnknownNode". */ - include( + includePath( + path: ObjectPath, context: InclusionContext, includeChildrenRecursively: IncludeChildren, options?: InclusionOptions @@ -131,7 +139,7 @@ export interface ExpressionNode extends ExpressionEntity, Node, Partial= 0; index--) { const { key, kind, property } = properties[index]; allProperties.push(property); diff --git a/src/ast/nodes/shared/ObjectMember.ts b/src/ast/nodes/shared/ObjectMember.ts index 676b7548c..8bda71fbc 100644 --- a/src/ast/nodes/shared/ObjectMember.ts +++ b/src/ast/nodes/shared/ObjectMember.ts @@ -1,13 +1,13 @@ import type { DeoptimizableEntity } from '../../DeoptimizableEntity'; import type { HasEffectsContext } from '../../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../../NodeInteractions'; -import type { ObjectPath, PathTracker } from '../../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; import { ExpressionEntity, type LiteralValueOrUnknown } from './Expression'; export class ObjectMember extends ExpressionEntity { constructor( private readonly object: ExpressionEntity, - private readonly key: string + private readonly path: ObjectPath ) { super(); } @@ -15,35 +15,35 @@ export class ObjectMember extends ExpressionEntity { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { this.object.deoptimizeArgumentsOnInteractionAtPath( interaction, - [this.key, ...path], + [...this.path, ...path], recursionTracker ); } deoptimizePath(path: ObjectPath): void { - this.object.deoptimizePath([this.key, ...path]); + this.object.deoptimizePath([...this.path, ...path]); } getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { - return this.object.getLiteralValueAtPath([this.key, ...path], recursionTracker, origin); + return this.object.getLiteralValueAtPath([...this.path, ...path], recursionTracker, origin); } getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.object.getReturnExpressionWhenCalledAtPath( - [this.key, ...path], + [...this.path, ...path], interaction, recursionTracker, origin @@ -55,6 +55,6 @@ export class ObjectMember extends ExpressionEntity { interaction: NodeInteraction, context: HasEffectsContext ): boolean { - return this.object.hasEffectsOnInteractionAtPath([this.key, ...path], interaction, context); + return this.object.hasEffectsOnInteractionAtPath([...this.path, ...path], interaction, context); } } diff --git a/src/ast/nodes/shared/Pattern.ts b/src/ast/nodes/shared/Pattern.ts index e53385d28..4591bf46e 100644 --- a/src/ast/nodes/shared/Pattern.ts +++ b/src/ast/nodes/shared/Pattern.ts @@ -1,10 +1,34 @@ import type { WritableEntity } from '../../Entity'; +import type { HasEffectsContext, InclusionContext } from '../../ExecutionContext'; +import type { ObjectPath } from '../../utils/PathTracker'; import type LocalVariable from '../../variables/LocalVariable'; import type { ExpressionEntity } from './Expression'; import type { Node } from './Node'; import type { VariableKind } from './VariableKinds'; export interface PatternNode extends WritableEntity, Node { - declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[]; + // This should deoptimize both the left-hand and right-hand side + deoptimizeAssignment(destructuredInitPath: ObjectPath, init: ExpressionEntity): void; + + hasEffectsWhenDestructuring( + context: HasEffectsContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean; + + includeDestructuredIfNecessary( + context: InclusionContext, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): boolean; +} + +export interface DeclarationPatternNode extends PatternNode { + declare( + kind: VariableKind, + destructuredInitPath: ObjectPath, + init: ExpressionEntity + ): LocalVariable[]; + markDeclarationReached(): void; } diff --git a/src/ast/nodes/shared/chainElements.ts b/src/ast/nodes/shared/chainElements.ts index d7baa0a25..390e4029a 100644 --- a/src/ast/nodes/shared/chainElements.ts +++ b/src/ast/nodes/shared/chainElements.ts @@ -1,5 +1,5 @@ import type { DeoptimizableEntity } from '../../DeoptimizableEntity'; -import type { ObjectPath, PathTracker } from '../../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; import { EMPTY_PATH, SHARED_RECURSION_TRACKER } from '../../utils/PathTracker'; import type CallExpression from '../CallExpression'; import type MemberExpression from '../MemberExpression'; @@ -11,7 +11,7 @@ export function getChainElementLiteralValueAtPath( element: CallExpression | MemberExpression, object: ExpressionNode, path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown | SkippedChain { if ('getLiteralValueAtPathAsChainElement' in object) { diff --git a/src/ast/nodes/shared/jsxHelpers.ts b/src/ast/nodes/shared/jsxHelpers.ts index bdbdfe8ef..f90376e82 100644 --- a/src/ast/nodes/shared/jsxHelpers.ts +++ b/src/ast/nodes/shared/jsxHelpers.ts @@ -1,3 +1,5 @@ +import { createInclusionContext } from '../../ExecutionContext'; +import { UNKNOWN_PATH } from '../../utils/PathTracker'; import LocalVariable from '../../variables/LocalVariable'; import type Variable from '../../variables/Variable'; import type JSXElement from '../JSXElement'; @@ -35,14 +37,14 @@ export function getAndIncludeFactoryVariable( if (preserve) { // This pretends we are accessing an included global variable of the same name const globalVariable = node.scope.findGlobal(baseName); - globalVariable.include(); + globalVariable.includePath(UNKNOWN_PATH, createInclusionContext()); // This excludes this variable from renaming factoryVariable.globalName = baseName; } } else { factoryVariable = node.scope.findGlobal(baseName); } - node.scope.context.includeVariableInModule(factoryVariable); + node.scope.context.includeVariableInModule(factoryVariable, UNKNOWN_PATH); if (factoryVariable instanceof LocalVariable) { factoryVariable.consolidateInitializers(); factoryVariable.addUsedPlace(node); diff --git a/src/ast/nodes/shared/loops.ts b/src/ast/nodes/shared/loops.ts index dcc2dc6b4..0782260de 100644 --- a/src/ast/nodes/shared/loops.ts +++ b/src/ast/nodes/shared/loops.ts @@ -1,4 +1,5 @@ import type { HasEffectsContext, InclusionContext } from '../../ExecutionContext'; +import { UNKNOWN_PATH } from '../../utils/PathTracker'; import type { StatementNode } from './Node'; export function hasLoopBodyEffects(context: HasEffectsContext, body: StatementNode): boolean { @@ -25,7 +26,7 @@ export function includeLoopBody( const { brokenFlow, hasBreak, hasContinue } = context; context.hasBreak = false; context.hasContinue = false; - body.include(context, includeChildrenRecursively, { asSingleStatement: true }); + body.includePath(UNKNOWN_PATH, context, includeChildrenRecursively, { asSingleStatement: true }); context.hasBreak = hasBreak; context.hasContinue = hasContinue; context.brokenFlow = brokenFlow; diff --git a/src/ast/scopes/BlockScope.ts b/src/ast/scopes/BlockScope.ts index 77e9f5307..278e4096c 100644 --- a/src/ast/scopes/BlockScope.ts +++ b/src/ast/scopes/BlockScope.ts @@ -3,6 +3,7 @@ import { logRedeclarationError } from '../../utils/logs'; import type Identifier from '../nodes/Identifier'; import type { ExpressionEntity } from '../nodes/shared/Expression'; import type { VariableKind } from '../nodes/shared/VariableKinds'; +import type { ObjectPath } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; import ChildScope from './ChildScope'; @@ -15,6 +16,7 @@ export default class BlockScope extends ChildScope { identifier: Identifier, context: AstContext, init: ExpressionEntity, + destructuredInitPath: ObjectPath, kind: VariableKind ): LocalVariable { if (kind === 'var') { @@ -31,7 +33,13 @@ export default class BlockScope extends ChildScope { } return context.error(logRedeclarationError(name), identifier.start); } - const declaredVariable = this.parent.addDeclaration(identifier, context, init, kind); + const declaredVariable = this.parent.addDeclaration( + identifier, + context, + init, + destructuredInitPath, + kind + ); // Necessary to make sure the init is deoptimized for conditional declarations. // We cannot call deoptimizePath here. declaredVariable.markInitializersForDeoptimization(); @@ -39,6 +47,6 @@ export default class BlockScope extends ChildScope { this.addHoistedVariable(name, declaredVariable); return declaredVariable; } - return super.addDeclaration(identifier, context, init, kind); + return super.addDeclaration(identifier, context, init, destructuredInitPath, kind); } } diff --git a/src/ast/scopes/CatchBodyScope.ts b/src/ast/scopes/CatchBodyScope.ts index 40dad8cae..46cc32b68 100644 --- a/src/ast/scopes/CatchBodyScope.ts +++ b/src/ast/scopes/CatchBodyScope.ts @@ -4,6 +4,7 @@ import type Identifier from '../nodes/Identifier'; import * as NodeType from '../nodes/NodeType'; import type { ExpressionEntity } from '../nodes/shared/Expression'; import type { VariableKind } from '../nodes/shared/VariableKinds'; +import type { ObjectPath } from '../utils/PathTracker'; import { UNDEFINED_EXPRESSION } from '../values'; import type LocalVariable from '../variables/LocalVariable'; import ChildScope from './ChildScope'; @@ -18,6 +19,7 @@ export default class CatchBodyScope extends ChildScope { identifier: Identifier, context: AstContext, init: ExpressionEntity, + destructuredInitPath: ObjectPath, kind: VariableKind ): LocalVariable { if (kind === 'var') { @@ -39,6 +41,7 @@ export default class CatchBodyScope extends ChildScope { identifier, context, UNDEFINED_EXPRESSION, + destructuredInitPath, kind ); // To avoid the need to rewrite the declaration, we link the variable @@ -59,7 +62,13 @@ export default class CatchBodyScope extends ChildScope { return context.error(logRedeclarationError(name), identifier.start); } // We only add parameters to parameter scopes - const declaredVariable = this.parent.parent.addDeclaration(identifier, context, init, kind); + const declaredVariable = this.parent.parent.addDeclaration( + identifier, + context, + init, + destructuredInitPath, + kind + ); // Necessary to make sure the init is deoptimized for conditional declarations. // We cannot call deoptimizePath here. declaredVariable.markInitializersForDeoptimization(); @@ -67,6 +76,6 @@ export default class CatchBodyScope extends ChildScope { this.addHoistedVariable(name, declaredVariable); return declaredVariable; } - return super.addDeclaration(identifier, context, init, kind); + return super.addDeclaration(identifier, context, init, destructuredInitPath, kind); } } diff --git a/src/ast/scopes/ClassBodyScope.ts b/src/ast/scopes/ClassBodyScope.ts index c8f7f1f62..a9c086444 100644 --- a/src/ast/scopes/ClassBodyScope.ts +++ b/src/ast/scopes/ClassBodyScope.ts @@ -1,4 +1,5 @@ import type ClassNode from '../nodes/shared/ClassNode'; +import { EMPTY_PATH } from '../utils/PathTracker'; import LocalVariable from '../variables/LocalVariable'; import ThisVariable from '../variables/ThisVariable'; import ChildScope from './ChildScope'; @@ -12,7 +13,7 @@ export default class ClassBodyScope extends ChildScope { super(parent, context); this.variables.set( 'this', - (this.thisVariable = new LocalVariable('this', null, classNode, context, 'other')) + (this.thisVariable = new LocalVariable('this', null, classNode, EMPTY_PATH, context, 'other')) ); this.instanceScope = new ChildScope(this, context); this.instanceScope.variables.set('this', new ThisVariable(context)); diff --git a/src/ast/scopes/FunctionBodyScope.ts b/src/ast/scopes/FunctionBodyScope.ts index 7343ac033..6edab0c0b 100644 --- a/src/ast/scopes/FunctionBodyScope.ts +++ b/src/ast/scopes/FunctionBodyScope.ts @@ -3,6 +3,7 @@ import { logRedeclarationError } from '../../utils/logs'; import type Identifier from '../nodes/Identifier'; import type { ExpressionEntity } from '../nodes/shared/Expression'; import type { VariableKind } from '../nodes/shared/VariableKinds'; +import type { ObjectPath } from '../utils/PathTracker'; import LocalVariable from '../variables/LocalVariable'; import ChildScope from './ChildScope'; import type ParameterScope from './ParameterScope'; @@ -18,6 +19,7 @@ export default class FunctionBodyScope extends ChildScope { identifier: Identifier, context: AstContext, init: ExpressionEntity, + destructuredInitPath: ObjectPath, kind: VariableKind ): LocalVariable { const name = identifier.name; @@ -34,7 +36,14 @@ export default class FunctionBodyScope extends ChildScope { } context.error(logRedeclarationError(name), identifier.start); } - const newVariable = new LocalVariable(identifier.name, identifier, init, context, kind); + const newVariable = new LocalVariable( + identifier.name, + identifier, + init, + destructuredInitPath, + context, + kind + ); this.variables.set(name, newVariable); return newVariable; } diff --git a/src/ast/scopes/FunctionScope.ts b/src/ast/scopes/FunctionScope.ts index ed1d91863..b6bbd6cef 100644 --- a/src/ast/scopes/FunctionScope.ts +++ b/src/ast/scopes/FunctionScope.ts @@ -1,6 +1,7 @@ import type { InclusionContext } from '../ExecutionContext'; -import type SpreadElement from '../nodes/SpreadElement'; +import type { NodeInteractionCalled } from '../NodeInteractions'; import type { ExpressionEntity } from '../nodes/shared/Expression'; +import { UNKNOWN_PATH } from '../utils/PathTracker'; import ArgumentsVariable from '../variables/ArgumentsVariable'; import ThisVariable from '../variables/ThisVariable'; import type ChildScope from './ChildScope'; @@ -11,8 +12,8 @@ export default class FunctionScope extends ReturnValueScope { readonly thisVariable: ThisVariable; constructor(parent: ChildScope) { - const { context } = parent; super(parent, false); + const { context } = parent; this.variables.set('arguments', (this.argumentsVariable = new ArgumentsVariable(context))); this.variables.set('this', (this.thisVariable = new ThisVariable(context))); } @@ -21,17 +22,17 @@ export default class FunctionScope extends ReturnValueScope { return this; } - includeCallArguments( - context: InclusionContext, - parameters: readonly (ExpressionEntity | SpreadElement)[] - ): void { - super.includeCallArguments(context, parameters); + includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { + super.includeCallArguments(context, interaction); if (this.argumentsVariable.included) { - for (const argument of parameters) { - if (!argument.included) { - argument.include(context, false); - } + const { args } = interaction; + for (let argumentIndex = 1; argumentIndex < args.length; argumentIndex++) { + args[argumentIndex]?.includePath(UNKNOWN_PATH, context, false); } } } + + protected addArgumentToBeDeoptimized(argument: ExpressionEntity) { + this.argumentsVariable.addArgumentToBeDeoptimized(argument); + } } diff --git a/src/ast/scopes/ModuleScope.ts b/src/ast/scopes/ModuleScope.ts index 2902fe666..af7ba7235 100644 --- a/src/ast/scopes/ModuleScope.ts +++ b/src/ast/scopes/ModuleScope.ts @@ -5,6 +5,8 @@ import type ExportDefaultDeclaration from '../nodes/ExportDefaultDeclaration'; import type Identifier from '../nodes/Identifier'; import type { ExpressionEntity } from '../nodes/shared/Expression'; import type { VariableKind } from '../nodes/shared/VariableKinds'; +import type { ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH } from '../utils/PathTracker'; import { UNDEFINED_EXPRESSION } from '../values'; import ExportDefaultVariable from '../variables/ExportDefaultVariable'; import GlobalVariable from '../variables/GlobalVariable'; @@ -20,7 +22,7 @@ export default class ModuleScope extends ChildScope { super(parent, context); this.variables.set( 'this', - new LocalVariable('this', null, UNDEFINED_EXPRESSION, context, 'other') + new LocalVariable('this', null, UNDEFINED_EXPRESSION, EMPTY_PATH, context, 'other') ); } @@ -28,12 +30,13 @@ export default class ModuleScope extends ChildScope { identifier: Identifier, context: AstContext, init: ExpressionEntity, + destructuredInitPath: ObjectPath, kind: VariableKind ): LocalVariable { if (this.context.module.importDescriptions.has(identifier.name)) { context.error(logRedeclarationError(identifier.name), identifier.start); } - return super.addDeclaration(identifier, context, init, kind); + return super.addDeclaration(identifier, context, init, destructuredInitPath, kind); } addExportDefaultDeclaration( diff --git a/src/ast/scopes/ParameterScope.ts b/src/ast/scopes/ParameterScope.ts index 8cc03c02c..de5ccdd74 100644 --- a/src/ast/scopes/ParameterScope.ts +++ b/src/ast/scopes/ParameterScope.ts @@ -1,8 +1,10 @@ import { logDuplicateArgumentNameError } from '../../utils/logs'; import type { InclusionContext } from '../ExecutionContext'; +import type { NodeInteractionCalled } from '../NodeInteractions'; import type Identifier from '../nodes/Identifier'; import SpreadElement from '../nodes/SpreadElement'; -import type { ExpressionEntity } from '../nodes/shared/Expression'; +import type { ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, UNKNOWN_PATH } from '../utils/PathTracker'; import ParameterVariable from '../variables/ParameterVariable'; import CatchBodyScope from './CatchBodyScope'; import ChildScope from './ChildScope'; @@ -10,9 +12,9 @@ import FunctionBodyScope from './FunctionBodyScope'; export default class ParameterScope extends ChildScope { readonly bodyScope: ChildScope; - parameters: readonly ParameterVariable[][] = []; - private hasRest = false; + protected hasRest = false; + protected parameters: readonly ParameterVariable[][] = []; constructor(parent: ChildScope, isCatchScope: boolean) { super(parent, parent.context); @@ -23,13 +25,13 @@ export default class ParameterScope extends ChildScope { * Adds a parameter to this scope. Parameters must be added in the correct * order, i.e. from left to right. */ - addParameterDeclaration(identifier: Identifier): ParameterVariable { + addParameterDeclaration(identifier: Identifier, argumentPath: ObjectPath): ParameterVariable { const { name, start } = identifier; const existingParameter = this.variables.get(name); if (existingParameter) { return this.context.error(logDuplicateArgumentNameError(name), start); } - const variable = new ParameterVariable(name, identifier, this.context); + const variable = new ParameterVariable(name, identifier, argumentPath, this.context); this.variables.set(name, variable); // We also add it to the body scope to detect name conflicts with local // variables. We still need the intermediate scope, though, as parameter @@ -49,45 +51,52 @@ export default class ParameterScope extends ChildScope { this.hasRest = hasRest; } - includeCallArguments( - context: InclusionContext, - parameters: readonly (ExpressionEntity | SpreadElement)[] - ): void { + includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { let calledFromTryStatement = false; let argumentIncluded = false; const restParameter = this.hasRest && this.parameters[this.parameters.length - 1]; - for (const checkedArgument of parameters) { - if (checkedArgument instanceof SpreadElement) { - for (const argument of parameters) { - argument.include(context, false); - } - break; + const { args } = interaction; + let lastExplicitlyIncludedIndex = args.length - 1; + // If there is a SpreadElement, we need to include all arguments after it + // because we no longer know which argument corresponds to which parameter. + for (let argumentIndex = 1; argumentIndex < args.length; argumentIndex++) { + if (args[argumentIndex] instanceof SpreadElement && !argumentIncluded) { + argumentIncluded = true; + lastExplicitlyIncludedIndex = argumentIndex - 1; + } + if (argumentIncluded) { + args[argumentIndex]!.includePath(UNKNOWN_PATH, context, false); } } - for (let index = parameters.length - 1; index >= 0; index--) { - const parameterVariables = this.parameters[index] || restParameter; - const argument = parameters[index]; + // Now we go backwards either starting from the last argument or before the + // first SpreadElement to ensure all arguments before are included as needed + for (let index = lastExplicitlyIncludedIndex; index >= 1; index--) { + const parameterVariables = this.parameters[index - 1] || restParameter; + const argument = args[index]!; if (parameterVariables) { calledFromTryStatement = false; if (parameterVariables.length === 0) { - // handle empty destructuring + // handle empty destructuring to avoid destructuring undefined argumentIncluded = true; } else { for (const variable of parameterVariables) { - if (variable.included) { - argumentIncluded = true; - } if (variable.calledFromTryStatement) { calledFromTryStatement = true; } + if (variable.included) { + argumentIncluded = true; + if (calledFromTryStatement) { + argument.includePath(UNKNOWN_PATH, context, true); + } else { + variable.includeArgumentPaths(argument, context); + } + } } } } - if (!argumentIncluded && argument.shouldBeIncluded(context)) { + if (!argument.included && (argumentIncluded || argument.shouldBeIncluded(context))) { argumentIncluded = true; - } - if (argumentIncluded) { - argument.include(context, calledFromTryStatement); + argument.includePath(EMPTY_PATH, context, calledFromTryStatement); } } } diff --git a/src/ast/scopes/ReturnValueScope.ts b/src/ast/scopes/ReturnValueScope.ts index be4a522f3..0aedb66e8 100644 --- a/src/ast/scopes/ReturnValueScope.ts +++ b/src/ast/scopes/ReturnValueScope.ts @@ -1,5 +1,8 @@ +import type { NodeInteractionCalled } from '../NodeInteractions'; import { type ExpressionEntity, UNKNOWN_EXPRESSION } from '../nodes/shared/Expression'; +import SpreadElement from '../nodes/SpreadElement'; import { UNKNOWN_PATH } from '../utils/PathTracker'; +import { UNDEFINED_EXPRESSION } from '../values'; import ParameterScope from './ParameterScope'; export default class ReturnValueScope extends ParameterScope { @@ -10,11 +13,64 @@ export default class ReturnValueScope extends ParameterScope { this.returnExpressions.push(expression); } + deoptimizeArgumentsOnCall(interaction: NodeInteractionCalled): void { + const { parameters } = this; + const { args } = interaction; + let position = 0; + for (; position < args.length - 1; position++) { + // Only the "this" argument arg[0] can be null + const argument = args[position + 1]!; + if (argument instanceof SpreadElement) { + // This deoptimizes the current and remaining parameters and arguments + for (; position < parameters.length; position++) { + args[position + 1]?.deoptimizePath(UNKNOWN_PATH); + parameters[position].forEach(variable => variable.markReassigned()); + } + break; + } + if (this.hasRest && position >= parameters.length - 1) { + argument.deoptimizePath(UNKNOWN_PATH); + } else { + const variables = parameters[position]; + if (variables) { + for (const variable of variables) { + variable.addArgumentValue(argument); + } + } + this.addArgumentToBeDeoptimized(argument); + } + } + for (; position < parameters.length; position++) { + for (const variable of parameters[position]) { + variable.addArgumentValue(UNDEFINED_EXPRESSION); + } + } + } + getReturnExpression(): ExpressionEntity { if (this.returnExpression === null) this.updateReturnExpression(); return this.returnExpression!; } + deoptimizeAllParameters() { + for (const parameter of this.parameters) { + for (const variable of parameter) { + variable.deoptimizePath(UNKNOWN_PATH); + variable.markReassigned(); + } + } + } + + reassignAllParameters() { + for (const parameter of this.parameters) { + for (const variable of parameter) { + variable.markReassigned(); + } + } + } + + protected addArgumentToBeDeoptimized(_argument: ExpressionEntity) {} + private updateReturnExpression() { if (this.returnExpressions.length === 1) { this.returnExpression = this.returnExpressions[0]; diff --git a/src/ast/scopes/Scope.ts b/src/ast/scopes/Scope.ts index eaeea352d..3be6b6429 100644 --- a/src/ast/scopes/Scope.ts +++ b/src/ast/scopes/Scope.ts @@ -3,6 +3,7 @@ import { logRedeclarationError } from '../../utils/logs'; import type Identifier from '../nodes/Identifier'; import type { ExpressionEntity } from '../nodes/shared/Expression'; import type { VariableKind } from '../nodes/shared/VariableKinds'; +import type { ObjectPath } from '../utils/PathTracker'; import LocalVariable from '../variables/LocalVariable'; import type Variable from '../variables/Variable'; import type ChildScope from './ChildScope'; @@ -28,20 +29,27 @@ export default class Scope { identifier: Identifier, context: AstContext, init: ExpressionEntity, + destructuredInitPath: ObjectPath, kind: VariableKind ): LocalVariable { const name = identifier.name; const existingVariable = this.hoistedVariables?.get(name) || (this.variables.get(name) as LocalVariable); if (existingVariable) { - const existingKind = existingVariable.kind; - if (kind === 'var' && existingKind === 'var') { + if (kind === 'var' && existingVariable.kind === 'var') { existingVariable.addDeclaration(identifier, init); return existingVariable; } context.error(logRedeclarationError(name), identifier.start); } - const newVariable = new LocalVariable(identifier.name, identifier, init, context, kind); + const newVariable = new LocalVariable( + identifier.name, + identifier, + init, + destructuredInitPath, + context, + kind + ); this.variables.set(name, newVariable); return newVariable; } diff --git a/src/ast/scopes/TrackingScope.ts b/src/ast/scopes/TrackingScope.ts index 7449c5e0f..616fcafaf 100644 --- a/src/ast/scopes/TrackingScope.ts +++ b/src/ast/scopes/TrackingScope.ts @@ -2,6 +2,7 @@ import type { AstContext } from '../../Module'; import type Identifier from '../nodes/Identifier'; import type { ExpressionEntity } from '../nodes/shared/Expression'; import type { VariableKind } from '../nodes/shared/VariableKinds'; +import type { ObjectPath } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; import BlockScope from './BlockScope'; @@ -12,9 +13,10 @@ export default class TrackingScope extends BlockScope { identifier: Identifier, context: AstContext, init: ExpressionEntity, + destructuredInitPath: ObjectPath, kind: VariableKind ): LocalVariable { this.hoistedDeclarations.push(identifier); - return super.addDeclaration(identifier, context, init, kind); + return super.addDeclaration(identifier, context, init, destructuredInitPath, kind); } } diff --git a/src/ast/utils/PathTracker.ts b/src/ast/utils/PathTracker.ts index 406ffd661..3b70c4add 100644 --- a/src/ast/utils/PathTracker.ts +++ b/src/ast/utils/PathTracker.ts @@ -1,5 +1,8 @@ +import { EMPTY_OBJECT } from '../../utils/blank'; import { getNewSet, getOrCreate } from '../../utils/getOrCreate'; import type { Entity } from '../Entity'; +import type { InclusionContext } from '../ExecutionContext'; +import type { ExpressionEntity } from '../nodes/shared/Expression'; export const UnknownKey = Symbol('Unknown Key'); export const UnknownNonAccessorKey = Symbol('Unknown Non-Accessor Key'); @@ -34,7 +37,7 @@ interface EntityPaths { [UnknownNonAccessorKey]?: EntityPaths; } -export class PathTracker { +export class EntityPathTracker { private entityPaths: EntityPaths = Object.create(null, { [EntitiesKey]: { value: new Set() } }); @@ -63,15 +66,15 @@ export class PathTracker { private getEntities(path: ObjectPath): Set { let currentPaths = this.entityPaths; for (const pathSegment of path) { - currentPaths = currentPaths[pathSegment] = - currentPaths[pathSegment] || - Object.create(null, { [EntitiesKey]: { value: new Set() } }); + currentPaths = currentPaths[pathSegment] ||= Object.create(null, { + [EntitiesKey]: { value: new Set() } + }); } return currentPaths[EntitiesKey]; } } -export const SHARED_RECURSION_TRACKER = new PathTracker(); +export const SHARED_RECURSION_TRACKER = new EntityPathTracker(); interface DiscriminatedEntityPaths { [pathSegment: string]: DiscriminatedEntityPaths; @@ -94,9 +97,9 @@ export class DiscriminatedPathTracker { ): boolean { let currentPaths = this.entityPaths; for (const pathSegment of path) { - currentPaths = currentPaths[pathSegment] = - currentPaths[pathSegment] || - Object.create(null, { [EntitiesKey]: { value: new Map>() } }); + currentPaths = currentPaths[pathSegment] ||= Object.create(null, { + [EntitiesKey]: { value: new Map>() } + }); } const trackedEntities = getOrCreate( currentPaths[EntitiesKey], @@ -108,3 +111,65 @@ export class DiscriminatedPathTracker { return false; } } + +interface IncludedPaths { + [pathSegment: string]: IncludedPaths; + [UnknownKey]?: IncludedPaths; +} + +const UNKNOWN_INCLUDED_PATH: IncludedPaths = Object.freeze({ [UnknownKey]: EMPTY_OBJECT }); + +export class IncludedPathTracker { + private includedPaths: IncludedPaths | null = null; + + includePathAndGetIfIncluded(path: ObjectPath): boolean { + let included = true; + let parent = this as unknown as IncludedPaths; + let parentSegment = 'includedPaths'; + let currentPaths: IncludedPaths = (this.includedPaths ||= + ((included = false), Object.create(null))); + for (const pathSegment of path) { + // This means from here, all paths are included + if (currentPaths[UnknownKey]) { + return true; + } + // Including UnknownKey automatically includes all nested paths. + // From above, we know that UnknownKey is not included yet. + if (typeof pathSegment === 'symbol') { + // Hopefully, this saves some memory over just setting + // currentPaths[UnknownKey] = EMPTY_OBJECT + parent[parentSegment] = UNKNOWN_INCLUDED_PATH; + return false; + } + parent = currentPaths; + parentSegment = pathSegment; + currentPaths = currentPaths[pathSegment] ||= ((included = false), Object.create(null)); + } + return included; + } + + includeAllPaths(entity: ExpressionEntity, context: InclusionContext, basePath: ObjectPath) { + const { includedPaths } = this; + if (includedPaths) { + includeAllPaths(entity, context, basePath, includedPaths); + } + } +} + +function includeAllPaths( + entity: ExpressionEntity, + context: InclusionContext, + basePath: ObjectPath, + currentPaths: IncludedPaths +): void { + if (currentPaths[UnknownKey]) { + return entity.includePath([...basePath, UnknownKey], context, false); + } + const keys = Object.keys(currentPaths); + if (keys.length === 0) { + return entity.includePath(basePath, context, false); + } + for (const key of keys) { + includeAllPaths(entity, context, [...basePath, key], currentPaths[key]); + } +} diff --git a/src/ast/utils/limitPathLength.ts b/src/ast/utils/limitPathLength.ts new file mode 100644 index 000000000..d1fe8871b --- /dev/null +++ b/src/ast/utils/limitPathLength.ts @@ -0,0 +1,23 @@ +import type { ObjectPath } from './PathTracker'; + +// To avoid infinite recursions +export const MAX_PATH_DEPTH = 6; + +// If a path is longer than MAX_PATH_DEPTH, it is truncated so that it is at +// most MAX_PATH_DEPTH long. The last element is always UnknownKey +export const limitPathDepth = (path: ObjectPath): ObjectPath => + path.length > MAX_PATH_DEPTH ? [...path.slice(0, MAX_PATH_DEPTH - 1), 'UnknownKey'] : path; + +// If a path is longer than MAX_PATH_DEPTH, it is truncated so that it is at +// most MAX_PATH_DEPTH long. The last element is always UnknownKey +export const limitConcatenatedPathDepth = (path1: ObjectPath, path2: ObjectPath): ObjectPath => { + const { length: length1 } = path1; + const { length: length2 } = path2; + return length1 === 0 + ? path2 + : length2 === 0 + ? path1 + : length1 + length2 > MAX_PATH_DEPTH + ? [...path1, ...path2.slice(0, MAX_PATH_DEPTH - 1 - path1.length), 'UnknownKey'] + : [...path1, ...path2]; +}; diff --git a/src/ast/variables/ArgumentsVariable.ts b/src/ast/variables/ArgumentsVariable.ts index a623b235b..531d5e7f9 100644 --- a/src/ast/variables/ArgumentsVariable.ts +++ b/src/ast/variables/ArgumentsVariable.ts @@ -1,17 +1,18 @@ import type { AstContext } from '../../Module'; +import type { InclusionContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ACCESSED } from '../NodeInteractions'; import type { ExpressionEntity } from '../nodes/shared/Expression'; import { UNKNOWN_EXPRESSION } from '../nodes/shared/Expression'; import type { ObjectPath } from '../utils/PathTracker'; -import { UNKNOWN_PATH } from '../utils/PathTracker'; +import { EMPTY_PATH, UNKNOWN_PATH } from '../utils/PathTracker'; import LocalVariable from './LocalVariable'; export default class ArgumentsVariable extends LocalVariable { private deoptimizedArguments: ExpressionEntity[] = []; constructor(context: AstContext) { - super('arguments', null, UNKNOWN_EXPRESSION, context, 'other'); + super('arguments', null, UNKNOWN_EXPRESSION, EMPTY_PATH, context, 'other'); } addArgumentToBeDeoptimized(argument: ExpressionEntity): void { @@ -26,8 +27,8 @@ export default class ArgumentsVariable extends LocalVariable { return type !== INTERACTION_ACCESSED || path.length > 1; } - include() { - super.include(); + includePath(path: ObjectPath, context: InclusionContext) { + super.includePath(path, context); for (const argument of this.deoptimizedArguments) { argument.deoptimizePath(UNKNOWN_PATH); } diff --git a/src/ast/variables/ExportDefaultVariable.ts b/src/ast/variables/ExportDefaultVariable.ts index baed0873c..f619de19b 100644 --- a/src/ast/variables/ExportDefaultVariable.ts +++ b/src/ast/variables/ExportDefaultVariable.ts @@ -5,6 +5,7 @@ import FunctionDeclaration from '../nodes/FunctionDeclaration'; import Identifier, { type IdentifierWithVariable } from '../nodes/Identifier'; import type IdentifierBase from '../nodes/shared/IdentifierBase'; import type { NodeBase } from '../nodes/shared/Node'; +import { EMPTY_PATH } from '../utils/PathTracker'; import LocalVariable from './LocalVariable'; import UndefinedVariable from './UndefinedVariable'; import type Variable from './Variable'; @@ -12,7 +13,7 @@ import type Variable from './Variable'; export default class ExportDefaultVariable extends LocalVariable { hasId = false; - private originalId: IdentifierWithVariable | null = null; + private readonly originalId: IdentifierWithVariable | null = null; private originalVariable: Variable | null = null; constructor( @@ -20,7 +21,14 @@ export default class ExportDefaultVariable extends LocalVariable { exportDefaultDeclaration: ExportDefaultDeclaration, context: AstContext ) { - super(name, exportDefaultDeclaration, exportDefaultDeclaration.declaration, context, 'other'); + super( + name, + exportDefaultDeclaration, + exportDefaultDeclaration.declaration, + EMPTY_PATH, + context, + 'other' + ); const declaration = exportDefaultDeclaration.declaration; if ( (declaration instanceof FunctionDeclaration || declaration instanceof ClassDeclaration) && diff --git a/src/ast/variables/ExportShimVariable.ts b/src/ast/variables/ExportShimVariable.ts index f473c49c8..f936e32c3 100644 --- a/src/ast/variables/ExportShimVariable.ts +++ b/src/ast/variables/ExportShimVariable.ts @@ -1,5 +1,7 @@ import type Module from '../../Module'; import { MISSING_EXPORT_SHIM_VARIABLE } from '../../utils/variableNames'; +import type { InclusionContext } from '../ExecutionContext'; +import type { ObjectPath } from '../utils/PathTracker'; import Variable from './Variable'; export default class ExportShimVariable extends Variable { @@ -10,8 +12,8 @@ export default class ExportShimVariable extends Variable { this.module = module; } - include(): void { - super.include(); + includePath(path: ObjectPath, context: InclusionContext): void { + super.includePath(path, context); this.module.needsExportShim = true; } } diff --git a/src/ast/variables/ExternalVariable.ts b/src/ast/variables/ExternalVariable.ts index 0775fc036..ceda7380b 100644 --- a/src/ast/variables/ExternalVariable.ts +++ b/src/ast/variables/ExternalVariable.ts @@ -1,8 +1,9 @@ import type ExternalModule from '../../ExternalModule'; +import type { InclusionContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ACCESSED } from '../NodeInteractions'; import type IdentifierBase from '../nodes/shared/IdentifierBase'; -import type { ObjectPath } from '../utils/PathTracker'; +import { type ObjectPath } from '../utils/PathTracker'; import Variable from './Variable'; export default class ExternalVariable extends Variable { @@ -27,8 +28,8 @@ export default class ExternalVariable extends Variable { return type !== INTERACTION_ACCESSED || path.length > (this.isNamespace ? 1 : 0); } - include(): void { - super.include(); + includePath(path: ObjectPath, context: InclusionContext): void { + super.includePath(path, context); this.module.used = true; } } diff --git a/src/ast/variables/GlobalVariable.ts b/src/ast/variables/GlobalVariable.ts index 4f168ac8f..d6be0ebab 100644 --- a/src/ast/variables/GlobalVariable.ts +++ b/src/ast/variables/GlobalVariable.ts @@ -9,7 +9,7 @@ import { import type { LiteralValueOrUnknown } from '../nodes/shared/Expression'; import { UnknownValue } from '../nodes/shared/Expression'; import { getGlobalAtPath } from '../nodes/shared/knownGlobals'; -import type { ObjectPath, PathTracker } from '../utils/PathTracker'; +import { type EntityPathTracker, type ObjectPath } from '../utils/PathTracker'; import Variable from './Variable'; export default class GlobalVariable extends Variable { @@ -23,7 +23,7 @@ export default class GlobalVariable extends Variable { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ) { switch (interaction.type) { // While there is no point in testing these cases as at the moment, they @@ -49,7 +49,7 @@ export default class GlobalVariable extends Variable { getLiteralValueAtPath( path: ObjectPath, - _recursionTracker: PathTracker, + _recursionTracker: EntityPathTracker, _origin: DeoptimizableEntity ): LiteralValueOrUnknown { const globalAtPath = getGlobalAtPath([this.name, ...path]); diff --git a/src/ast/variables/LocalVariable.ts b/src/ast/variables/LocalVariable.ts index 83b32a81e..97d262a4c 100644 --- a/src/ast/variables/LocalVariable.ts +++ b/src/ast/variables/LocalVariable.ts @@ -2,7 +2,6 @@ import type { AstContext, default as Module } from '../../Module'; import { EMPTY_ARRAY } from '../../utils/blank'; import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; -import { createInclusionContext } from '../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions'; import { INTERACTION_ACCESSED, @@ -12,7 +11,6 @@ import { import type ExportDefaultDeclaration from '../nodes/ExportDefaultDeclaration'; import type Identifier from '../nodes/Identifier'; import * as NodeType from '../nodes/NodeType'; -import type SpreadElement from '../nodes/SpreadElement'; import { deoptimizeInteraction, type ExpressionEntity, @@ -23,33 +21,43 @@ import { } from '../nodes/shared/Expression'; import type { Node } from '../nodes/shared/Node'; import type { VariableKind } from '../nodes/shared/VariableKinds'; -import { type ObjectPath, type PathTracker, UNKNOWN_PATH } from '../utils/PathTracker'; +import { limitConcatenatedPathDepth, MAX_PATH_DEPTH } from '../utils/limitPathLength'; +import { + EMPTY_PATH, + type EntityPathTracker, + IncludedPathTracker, + type ObjectPath, + UNKNOWN_PATH, + UnknownKey +} from '../utils/PathTracker'; import Variable from './Variable'; export default class LocalVariable extends Variable { calledFromTryStatement = false; + readonly declarations: (Identifier | ExportDefaultDeclaration)[]; readonly module: Module; - readonly kind: VariableKind; protected additionalInitializers: ExpressionEntity[] | null = null; // Caching and deoptimization: // We track deoptimization when we do not return something unknown - protected deoptimizationTracker: PathTracker; + protected deoptimizationTracker: EntityPathTracker; + protected includedPathTracker = new IncludedPathTracker(); private expressionsToBeDeoptimized: DeoptimizableEntity[] = []; constructor( name: string, declarator: Identifier | ExportDefaultDeclaration | null, private init: ExpressionEntity, + /** if this is non-empty, the actual init is this path of this.init */ + protected initPath: ObjectPath, context: AstContext, - kind: VariableKind + readonly kind: VariableKind ) { super(name); this.declarations = declarator ? [declarator] : []; this.deoptimizationTracker = context.deoptimizationTracker; this.module = context.module; - this.kind = kind; } addDeclaration(identifier: Identifier, init: ExpressionEntity): void { @@ -62,23 +70,28 @@ export default class LocalVariable extends Variable { for (const initializer of this.additionalInitializers) { initializer.deoptimizePath(UNKNOWN_PATH); } - this.additionalInitializers = null; } } deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { - if (this.isReassigned) { + if (this.isReassigned || path.length + this.initPath.length > MAX_PATH_DEPTH) { deoptimizeInteraction(interaction); return; } recursionTracker.withTrackedEntityAtPath( path, this.init, - () => this.init.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker), + () => { + this.init.deoptimizeArgumentsOnInteractionAtPath( + interaction, + [...this.initPath, ...path], + recursionTracker + ); + }, undefined ); } @@ -97,18 +110,18 @@ export default class LocalVariable extends Variable { for (const expression of expressionsToBeDeoptimized) { expression.deoptimizeCache(); } - this.init.deoptimizePath(UNKNOWN_PATH); + this.init.deoptimizePath([...this.initPath, UnknownKey]); } else { - this.init.deoptimizePath(path); + this.init.deoptimizePath(limitConcatenatedPathDepth(this.initPath, path)); } } getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { - if (this.isReassigned) { + if (this.isReassigned || path.length + this.initPath.length > MAX_PATH_DEPTH) { return UnknownValue; } return recursionTracker.withTrackedEntityAtPath( @@ -116,7 +129,11 @@ export default class LocalVariable extends Variable { this.init, () => { this.expressionsToBeDeoptimized.push(origin); - return this.init.getLiteralValueAtPath(path, recursionTracker, origin); + return this.init.getLiteralValueAtPath( + [...this.initPath, ...path], + recursionTracker, + origin + ); }, UnknownValue ); @@ -125,10 +142,10 @@ export default class LocalVariable extends Variable { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { - if (this.isReassigned) { + if (this.isReassigned || path.length + this.initPath.length > MAX_PATH_DEPTH) { return UNKNOWN_RETURN_EXPRESSION; } return recursionTracker.withTrackedEntityAtPath( @@ -137,7 +154,7 @@ export default class LocalVariable extends Variable { () => { this.expressionsToBeDeoptimized.push(origin); return this.init.getReturnExpressionWhenCalledAtPath( - path, + [...this.initPath, ...path], interaction, recursionTracker, origin @@ -152,12 +169,15 @@ export default class LocalVariable extends Variable { interaction: NodeInteraction, context: HasEffectsContext ): boolean { + if (path.length + this.initPath.length > MAX_PATH_DEPTH) { + return true; + } switch (interaction.type) { case INTERACTION_ACCESSED: { if (this.isReassigned) return true; return ( !context.accessed.trackEntityAtPathAndGetIfTracked(path, this) && - this.init.hasEffectsOnInteractionAtPath(path, interaction, context) + this.init.hasEffectsOnInteractionAtPath([...this.initPath, ...path], interaction, context) ); } case INTERACTION_ASSIGNED: { @@ -166,7 +186,7 @@ export default class LocalVariable extends Variable { if (this.isReassigned) return true; return ( !context.assigned.trackEntityAtPathAndGetIfTracked(path, this) && - this.init.hasEffectsOnInteractionAtPath(path, interaction, context) + this.init.hasEffectsOnInteractionAtPath([...this.initPath, ...path], interaction, context) ); } case INTERACTION_CALLED: { @@ -175,18 +195,18 @@ export default class LocalVariable extends Variable { !( interaction.withNew ? context.instantiated : context.called ).trackEntityAtPathAndGetIfTracked(path, interaction.args, this) && - this.init.hasEffectsOnInteractionAtPath(path, interaction, context) + this.init.hasEffectsOnInteractionAtPath([...this.initPath, ...path], interaction, context) ); } } } - include(): void { - if (!this.included) { - super.include(); + includePath(path: ObjectPath, context: InclusionContext): void { + if (!this.includedPathTracker.includePathAndGetIfIncluded(path)) { + super.includePath(path, context); for (const declaration of this.declarations) { // If node is a default export, it can save a tree-shaking run to include the full declaration now - if (!declaration.included) declaration.include(createInclusionContext(), false); + if (!declaration.included) declaration.includePath(EMPTY_PATH, context, false); let node = declaration.parent as Node; while (!node.included) { // We do not want to properly include parents in case they are part of a dead branch @@ -196,20 +216,30 @@ export default class LocalVariable extends Variable { node = node.parent as Node; } } + // We need to make sure we include the correct path of the init + if (path.length > 0) { + this.init.includePath(limitConcatenatedPathDepth(this.initPath, path), context, false); + this.additionalInitializers?.forEach(initializer => + initializer.includePath(UNKNOWN_PATH, context, false) + ); + } } } - includeCallArguments( - context: InclusionContext, - parameters: readonly (ExpressionEntity | SpreadElement)[] - ): void { - if (this.isReassigned || context.includedCallArguments.has(this.init)) { - for (const argument of parameters) { - argument.include(context, false); + includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { + if ( + this.isReassigned || + context.includedCallArguments.has(this.init) || + // This can be removed again once we can include arguments when called at + // a specific path + this.initPath.length > 0 + ) { + for (const argument of interaction.args) { + argument?.includePath(UNKNOWN_PATH, context, false); } } else { context.includedCallArguments.add(this.init); - this.init.includeCallArguments(context, parameters); + this.init.includeCallArguments(context, interaction); context.includedCallArguments.delete(this.init); } } diff --git a/src/ast/variables/NamespaceVariable.ts b/src/ast/variables/NamespaceVariable.ts index 6085c435b..91006e536 100644 --- a/src/ast/variables/NamespaceVariable.ts +++ b/src/ast/variables/NamespaceVariable.ts @@ -3,14 +3,14 @@ import { stringifyObjectKeyIfNeeded } from '../../utils/identifierHelpers'; import { getToStringTagValue, MERGE_NAMESPACES_VARIABLE } from '../../utils/interopHelpers'; import type { RenderOptions } from '../../utils/renderHelpers'; import { getSystemExportStatement } from '../../utils/systemJsRendering'; -import type { HasEffectsContext } from '../ExecutionContext'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ASSIGNED, INTERACTION_CALLED } from '../NodeInteractions'; import type { LiteralValueOrUnknown } from '../nodes/shared/Expression'; import { deoptimizeInteraction, UnknownValue } from '../nodes/shared/Expression'; import type IdentifierBase from '../nodes/shared/IdentifierBase'; import type ChildScope from '../scopes/ChildScope'; -import type { ObjectPath, PathTracker } from '../utils/PathTracker'; +import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; import { SymbolToStringTag } from '../utils/PathTracker'; import Variable from './Variable'; @@ -38,7 +38,7 @@ export default class NamespaceVariable extends Variable { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ) { if (path.length > 1 || (path.length === 1 && interaction.type === INTERACTION_CALLED)) { const key = path[0]; @@ -113,8 +113,8 @@ export default class NamespaceVariable extends Variable { ); } - include(): void { - super.include(); + includePath(path: ObjectPath, context: InclusionContext): void { + super.includePath(path, context); this.context.includeAllExports(); } diff --git a/src/ast/variables/ParameterVariable.ts b/src/ast/variables/ParameterVariable.ts index a17bf3b11..b9f354fcf 100644 --- a/src/ast/variables/ParameterVariable.ts +++ b/src/ast/variables/ParameterVariable.ts @@ -1,7 +1,8 @@ import type { AstContext } from '../../Module'; import { EMPTY_ARRAY } from '../../utils/blank'; import type { DeoptimizableEntity } from '../DeoptimizableEntity'; -import type { HasEffectsContext } from '../ExecutionContext'; +import type { InclusionContext } from '../ExecutionContext'; +import { type HasEffectsContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ASSIGNED, INTERACTION_CALLED } from '../NodeInteractions'; import type ExportDefaultDeclaration from '../nodes/ExportDefaultDeclaration'; @@ -13,10 +14,10 @@ import { UNKNOWN_RETURN_EXPRESSION, UnknownValue } from '../nodes/shared/Expression'; +import { MAX_PATH_DEPTH } from '../utils/limitPathLength'; import type { ObjectPath, ObjectPathKey } from '../utils/PathTracker'; import { - EMPTY_PATH, - PathTracker, + EntityPathTracker, SHARED_RECURSION_TRACKER, UNKNOWN_PATH, UnknownKey @@ -31,31 +32,33 @@ interface DeoptimizationInteraction { const MAX_TRACKED_INTERACTIONS = 20; const NO_INTERACTIONS = EMPTY_ARRAY as unknown as DeoptimizationInteraction[]; const UNKNOWN_DEOPTIMIZED_FIELD = new Set([UnknownKey]); -const EMPTY_PATH_TRACKER = new PathTracker(); +const EMPTY_PATH_TRACKER = new EntityPathTracker(); const UNKNOWN_DEOPTIMIZED_ENTITY = new Set([UNKNOWN_EXPRESSION]); export default class ParameterVariable extends LocalVariable { private deoptimizationInteractions: DeoptimizationInteraction[] = []; - private deoptimizations = new PathTracker(); + private deoptimizations = new EntityPathTracker(); private deoptimizedFields = new Set(); - private entitiesToBeDeoptimized = new Set(); - private expressionsUseTheKnownValue: DeoptimizableEntity[] = []; + private argumentsToBeDeoptimized = new Set(); + private expressionsDependingOnKnownValue: DeoptimizableEntity[] = []; constructor( name: string, declarator: Identifier | ExportDefaultDeclaration | null, + argumentPath: ObjectPath, context: AstContext ) { - super(name, declarator, UNKNOWN_EXPRESSION, context, 'parameter'); + super(name, declarator, UNKNOWN_EXPRESSION, argumentPath, context, 'parameter'); } - addEntityToBeDeoptimized(entity: ExpressionEntity): void { + addArgumentValue(entity: ExpressionEntity): void { + this.updateKnownValue(entity); if (entity === UNKNOWN_EXPRESSION) { // As unknown expressions fully deoptimize all interactions, we can clear // the interaction cache at this point provided we keep this optimization // in mind when adding new interactions - if (!this.entitiesToBeDeoptimized.has(UNKNOWN_EXPRESSION)) { - this.entitiesToBeDeoptimized.add(UNKNOWN_EXPRESSION); + if (!this.argumentsToBeDeoptimized.has(UNKNOWN_EXPRESSION)) { + this.argumentsToBeDeoptimized.add(UNKNOWN_EXPRESSION); for (const { interaction } of this.deoptimizationInteractions) { deoptimizeInteraction(interaction); } @@ -64,27 +67,38 @@ export default class ParameterVariable extends LocalVariable { } else if (this.deoptimizedFields.has(UnknownKey)) { // This means that we already deoptimized all interactions and no longer // track them - entity.deoptimizePath(UNKNOWN_PATH); - } else if (!this.entitiesToBeDeoptimized.has(entity)) { - this.entitiesToBeDeoptimized.add(entity); + entity.deoptimizePath([...this.initPath, UnknownKey]); + } else if (!this.argumentsToBeDeoptimized.has(entity)) { + this.argumentsToBeDeoptimized.add(entity); for (const field of this.deoptimizedFields) { - entity.deoptimizePath([field]); + entity.deoptimizePath([...this.initPath, field]); } for (const { interaction, path } of this.deoptimizationInteractions) { - entity.deoptimizeArgumentsOnInteractionAtPath(interaction, path, SHARED_RECURSION_TRACKER); + if (this.initPath.length + path.length > MAX_PATH_DEPTH) { + deoptimizeInteraction(interaction); + continue; + } + entity.deoptimizeArgumentsOnInteractionAtPath( + interaction, + [...this.initPath, ...path], + SHARED_RECURSION_TRACKER + ); } } } + /** This says we should not make assumptions about the value of the parameter. + * This is different from deoptimization that will also cause argument values + * to be deoptimized. */ markReassigned(): void { if (this.isReassigned) { return; } super.markReassigned(); - for (const expression of this.expressionsUseTheKnownValue) { + for (const expression of this.expressionsDependingOnKnownValue) { expression.deoptimizeCache(); } - this.expressionsUseTheKnownValue = EMPTY_ARRAY as unknown as DeoptimizableEntity[]; + this.expressionsDependingOnKnownValue = EMPTY_ARRAY as unknown as DeoptimizableEntity[]; } deoptimizeCache(): void { @@ -99,7 +113,7 @@ export default class ParameterVariable extends LocalVariable { * and deoptimizeCache itself to mark reassigned if the argument is changed. * @param argument The argument of the function call */ - updateKnownValue(argument: ExpressionEntity) { + private updateKnownValue(argument: ExpressionEntity) { if (this.isReassigned) { return; } @@ -107,7 +121,7 @@ export default class ParameterVariable extends LocalVariable { if (this.knownValue === null) { this.knownValue = argument; this.knownValueLiteral = argument.getLiteralValueAtPath( - EMPTY_PATH, + this.initPath, SHARED_RECURSION_TRACKER, this ); @@ -130,7 +144,7 @@ export default class ParameterVariable extends LocalVariable { return; } // add tracking for the new argument - const newValue = argument.getLiteralValueAtPath(EMPTY_PATH, SHARED_RECURSION_TRACKER, this); + const newValue = argument.getLiteralValueAtPath(this.initPath, SHARED_RECURSION_TRACKER, this); if (newValue !== oldValue) { this.markReassigned(); } @@ -152,18 +166,18 @@ export default class ParameterVariable extends LocalVariable { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { - if (this.isReassigned) { + if (this.isReassigned || path.length + this.initPath.length > MAX_PATH_DEPTH) { return UnknownValue; } const knownValue = this.getKnownValue(); - this.expressionsUseTheKnownValue.push(origin); + this.expressionsDependingOnKnownValue.push(origin); return recursionTracker.withTrackedEntityAtPath( path, knownValue, - () => knownValue.getLiteralValueAtPath(path, recursionTracker, origin), + () => knownValue.getLiteralValueAtPath([...this.initPath, ...path], recursionTracker, origin), UnknownValue ); } @@ -173,18 +187,34 @@ export default class ParameterVariable extends LocalVariable { interaction: NodeInteraction, context: HasEffectsContext ): boolean { - if (this.isReassigned || interaction.type === INTERACTION_ASSIGNED) { + const { type } = interaction; + if ( + this.isReassigned || + type === INTERACTION_ASSIGNED || + path.length + this.initPath.length > MAX_PATH_DEPTH + ) { return super.hasEffectsOnInteractionAtPath(path, interaction, context); } - const knownValue = this.getKnownValue(); - return knownValue.hasEffectsOnInteractionAtPath(path, interaction, context); + return ( + !(type === INTERACTION_CALLED + ? (interaction.withNew + ? context.instantiated + : context.called + ).trackEntityAtPathAndGetIfTracked(path, interaction.args, this) + : context.accessed.trackEntityAtPathAndGetIfTracked(path, this)) && + this.getKnownValue().hasEffectsOnInteractionAtPath( + [...this.initPath, ...path], + interaction, + context + ) + ); } deoptimizeArgumentsOnInteractionAtPath(interaction: NodeInteraction, path: ObjectPath): void { // For performance reasons, we fully deoptimize all deeper interactions if ( path.length >= 2 || - this.entitiesToBeDeoptimized.has(UNKNOWN_EXPRESSION) || + this.argumentsToBeDeoptimized.has(UNKNOWN_EXPRESSION) || this.deoptimizationInteractions.length >= MAX_TRACKED_INTERACTIONS || (path.length === 1 && (this.deoptimizedFields.has(UnknownKey) || @@ -194,10 +224,14 @@ export default class ParameterVariable extends LocalVariable { return; } if (!this.deoptimizations.trackEntityAtPathAndGetIfTracked(path, interaction.args)) { - for (const entity of this.entitiesToBeDeoptimized) { - entity.deoptimizeArgumentsOnInteractionAtPath(interaction, path, SHARED_RECURSION_TRACKER); + for (const entity of this.argumentsToBeDeoptimized) { + entity.deoptimizeArgumentsOnInteractionAtPath( + interaction, + [...this.initPath, ...path], + SHARED_RECURSION_TRACKER + ); } - if (!this.entitiesToBeDeoptimized.has(UNKNOWN_EXPRESSION)) { + if (!this.argumentsToBeDeoptimized.has(UNKNOWN_EXPRESSION)) { this.deoptimizationInteractions.push({ interaction, path @@ -219,17 +253,17 @@ export default class ParameterVariable extends LocalVariable { return; } this.deoptimizedFields.add(key); - for (const entity of this.entitiesToBeDeoptimized) { + for (const entity of this.argumentsToBeDeoptimized) { // We do not need a recursion tracker here as we already track whether // this field is deoptimized - entity.deoptimizePath([key]); + entity.deoptimizePath([...this.initPath, key]); } if (key === UnknownKey) { // save some memory this.deoptimizationInteractions = NO_INTERACTIONS; this.deoptimizations = EMPTY_PATH_TRACKER; this.deoptimizedFields = UNKNOWN_DEOPTIMIZED_FIELD; - this.entitiesToBeDeoptimized = UNKNOWN_DEOPTIMIZED_ENTITY; + this.argumentsToBeDeoptimized = UNKNOWN_DEOPTIMIZED_ENTITY; } } @@ -246,4 +280,8 @@ export default class ParameterVariable extends LocalVariable { } return UNKNOWN_RETURN_EXPRESSION; } + + includeArgumentPaths(entity: ExpressionEntity, context: InclusionContext) { + this.includedPathTracker.includeAllPaths(entity, context, this.initPath); + } } diff --git a/src/ast/variables/SyntheticNamedExportVariable.ts b/src/ast/variables/SyntheticNamedExportVariable.ts index ce10f442e..685b4e78b 100644 --- a/src/ast/variables/SyntheticNamedExportVariable.ts +++ b/src/ast/variables/SyntheticNamedExportVariable.ts @@ -1,5 +1,7 @@ import type Module from '../../Module'; import type { AstContext } from '../../Module'; +import type { InclusionContext } from '../ExecutionContext'; +import { type ObjectPath } from '../utils/PathTracker'; import ExportDefaultVariable from './ExportDefaultVariable'; import Variable from './Variable'; @@ -44,9 +46,9 @@ export default class SyntheticNamedExportVariable extends Variable { return `${this.syntheticNamespace.getName(getPropertyAccess)}${getPropertyAccess(this.name)}`; } - include(): void { - super.include(); - this.context.includeVariableInModule(this.syntheticNamespace); + includePath(path: ObjectPath, context: InclusionContext): void { + super.includePath(path, context); + this.context.includeVariableInModule(this.syntheticNamespace, path); } setRenderNames(baseName: string | null, name: string | null): void { diff --git a/src/ast/variables/ThisVariable.ts b/src/ast/variables/ThisVariable.ts index 1d8ea9bcd..b55b2c1f4 100644 --- a/src/ast/variables/ThisVariable.ts +++ b/src/ast/variables/ThisVariable.ts @@ -2,12 +2,12 @@ import type { AstContext } from '../../Module'; import type { HasEffectsContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { UNKNOWN_EXPRESSION } from '../nodes/shared/Expression'; -import { type ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, type ObjectPath } from '../utils/PathTracker'; import ParameterVariable from './ParameterVariable'; export default class ThisVariable extends ParameterVariable { constructor(context: AstContext) { - super('this', null, context); + super('this', null, EMPTY_PATH, context); } hasEffectsOnInteractionAtPath( diff --git a/src/ast/variables/Variable.ts b/src/ast/variables/Variable.ts index 2b8a1924f..319608171 100644 --- a/src/ast/variables/Variable.ts +++ b/src/ast/variables/Variable.ts @@ -1,7 +1,7 @@ import type ExternalModule from '../../ExternalModule'; import type Module from '../../Module'; import type { RenderOptions } from '../../utils/renderHelpers'; -import type { HasEffectsContext } from '../ExecutionContext'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ACCESSED } from '../NodeInteractions'; import type CallExpression from '../nodes/CallExpression'; @@ -112,9 +112,9 @@ export default class Variable extends ExpressionEntity { * has not been included previously. Once a variable is included, it should * take care all its declarations are included. */ - include(): void { + includePath(path: ObjectPath, context: InclusionContext): void { this.included = true; - this.renderedLikeHoisted?.include(); + this.renderedLikeHoisted?.includePath(path, context); } /** diff --git a/src/utils/hashPlaceholders.ts b/src/utils/hashPlaceholders.ts index df83d3af4..cac7e7912 100644 --- a/src/utils/hashPlaceholders.ts +++ b/src/utils/hashPlaceholders.ts @@ -8,7 +8,7 @@ const hashPlaceholderRight = '}~'; const hashPlaceholderOverhead = hashPlaceholderLeft.length + hashPlaceholderRight.length; // This is the size of a 128-bits xxhash with base64url encoding -const MAX_HASH_SIZE = 22; +const MAX_HASH_SIZE = 21; export const DEFAULT_HASH_SIZE = 8; export type HashPlaceholderGenerator = (optionName: string, hashSize: number) => string; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_config.js b/test/chunking-form/samples/dynamic-import-with-namespace/_config.js new file mode 100644 index 000000000..e2744888c --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_config.js @@ -0,0 +1,22 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +const moduleContent = fs.readFileSync(path.resolve(__dirname, './module.js'), 'utf8'); +let count = 1; +module.exports = defineTest({ + description: 'The all cases of tree-shaking for dynamic import with namespace', + options: { + plugins: [ + { + resolveId(id) { + if (id.startsWith('./module')) return id + count++; + return this.resolve(id); + }, + load(id) { + if (id.endsWith('main.js')) return null; + return moduleContent; + } + } + ] + } +}); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module1.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module1.js new file mode 100644 index 000000000..0317e08ce --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module1.js @@ -0,0 +1,13 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + const bar = () => {}; + const baz = () => {}; + const qux = () => {}; + + exports.bar = bar; + exports.baz = baz; + exports.foo = foo; + exports.qux = qux; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module10.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module10.js new file mode 100644 index 000000000..0317e08ce --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module10.js @@ -0,0 +1,13 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + const bar = () => {}; + const baz = () => {}; + const qux = () => {}; + + exports.bar = bar; + exports.baz = baz; + exports.foo = foo; + exports.qux = qux; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module2.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module2.js new file mode 100644 index 000000000..0317e08ce --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module2.js @@ -0,0 +1,13 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + const bar = () => {}; + const baz = () => {}; + const qux = () => {}; + + exports.bar = bar; + exports.baz = baz; + exports.foo = foo; + exports.qux = qux; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module3.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module3.js new file mode 100644 index 000000000..3123adef9 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module3.js @@ -0,0 +1,7 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + + exports.foo = foo; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module4.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module4.js new file mode 100644 index 000000000..0317e08ce --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module4.js @@ -0,0 +1,13 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + const bar = () => {}; + const baz = () => {}; + const qux = () => {}; + + exports.bar = bar; + exports.baz = baz; + exports.foo = foo; + exports.qux = qux; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module5.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module5.js new file mode 100644 index 000000000..0317e08ce --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module5.js @@ -0,0 +1,13 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + const bar = () => {}; + const baz = () => {}; + const qux = () => {}; + + exports.bar = bar; + exports.baz = baz; + exports.foo = foo; + exports.qux = qux; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module6.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module6.js new file mode 100644 index 000000000..3123adef9 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module6.js @@ -0,0 +1,7 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + + exports.foo = foo; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module7.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module7.js new file mode 100644 index 000000000..0317e08ce --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module7.js @@ -0,0 +1,13 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + const bar = () => {}; + const baz = () => {}; + const qux = () => {}; + + exports.bar = bar; + exports.baz = baz; + exports.foo = foo; + exports.qux = qux; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module8.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module8.js new file mode 100644 index 000000000..0317e08ce --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module8.js @@ -0,0 +1,13 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + const bar = () => {}; + const baz = () => {}; + const qux = () => {}; + + exports.bar = bar; + exports.baz = baz; + exports.foo = foo; + exports.qux = qux; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module9.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module9.js new file mode 100644 index 000000000..3123adef9 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module9.js @@ -0,0 +1,7 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + + exports.foo = foo; + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/main.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/main.js new file mode 100644 index 000000000..012563bd0 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/main.js @@ -0,0 +1,87 @@ +define(['require'], (function (require) { 'use strict'; + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module1'], resolve, reject); }); + module.foo(); + // disabled + module[global.unknown](); + module.baz(); + })(); + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module2'], resolve, reject); }); + const module1 = module; + module1.foo(); + })(); + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module3'], resolve, reject); }); + const { foo } = module; + foo(); + })(); + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module4'], resolve, reject); }); + // disabled + const { foo, ...rest } = module; + foo(); + rest.bar(); + })(); + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module5'], resolve, reject); }); + readFoo({ foo: () => {} }); + readFoo(module); + function readFoo(module1) { + module1.foo(); + } + function readBar(module2) { + module2.bar(); + } + readBar(module); + })(); + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module6'], resolve, reject); }); + function b({ foo }) { + foo(); + } + b(module); + })(); + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module7'], resolve, reject); }); + // disabled + function b({ foo, ...rest }) { + foo(); + assert.ok(rest); + } + b(module); + })(); + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module8'], resolve, reject); }); + // disabled + function b(o1, ...rest) { + assert.ok(rest); + } + b(o1, o2, module); + })(); + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module9'], resolve, reject); }); + // disabled + function b({ foo = 1 }) { + assert.ok(foo); + } + b(module); + })(); + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module10'], resolve, reject); }); + (module).bar(); + (global.unknown && module).foo(); + (global.unknown ? module : 'foo').baz(); + })(); + +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module1.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module1.js new file mode 100644 index 000000000..32b8c4cc0 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module1.js @@ -0,0 +1,11 @@ +'use strict'; + +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +exports.bar = bar; +exports.baz = baz; +exports.foo = foo; +exports.qux = qux; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module10.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module10.js new file mode 100644 index 000000000..32b8c4cc0 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module10.js @@ -0,0 +1,11 @@ +'use strict'; + +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +exports.bar = bar; +exports.baz = baz; +exports.foo = foo; +exports.qux = qux; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module2.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module2.js new file mode 100644 index 000000000..32b8c4cc0 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module2.js @@ -0,0 +1,11 @@ +'use strict'; + +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +exports.bar = bar; +exports.baz = baz; +exports.foo = foo; +exports.qux = qux; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module3.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module3.js new file mode 100644 index 000000000..02ce8a98f --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module3.js @@ -0,0 +1,5 @@ +'use strict'; + +const foo = () => {}; + +exports.foo = foo; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module4.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module4.js new file mode 100644 index 000000000..32b8c4cc0 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module4.js @@ -0,0 +1,11 @@ +'use strict'; + +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +exports.bar = bar; +exports.baz = baz; +exports.foo = foo; +exports.qux = qux; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module5.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module5.js new file mode 100644 index 000000000..32b8c4cc0 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module5.js @@ -0,0 +1,11 @@ +'use strict'; + +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +exports.bar = bar; +exports.baz = baz; +exports.foo = foo; +exports.qux = qux; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module6.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module6.js new file mode 100644 index 000000000..02ce8a98f --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module6.js @@ -0,0 +1,5 @@ +'use strict'; + +const foo = () => {}; + +exports.foo = foo; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module7.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module7.js new file mode 100644 index 000000000..32b8c4cc0 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module7.js @@ -0,0 +1,11 @@ +'use strict'; + +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +exports.bar = bar; +exports.baz = baz; +exports.foo = foo; +exports.qux = qux; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module8.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module8.js new file mode 100644 index 000000000..32b8c4cc0 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module8.js @@ -0,0 +1,11 @@ +'use strict'; + +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +exports.bar = bar; +exports.baz = baz; +exports.foo = foo; +exports.qux = qux; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module9.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module9.js new file mode 100644 index 000000000..02ce8a98f --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module9.js @@ -0,0 +1,5 @@ +'use strict'; + +const foo = () => {}; + +exports.foo = foo; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/main.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/main.js new file mode 100644 index 000000000..9afab5592 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/main.js @@ -0,0 +1,85 @@ +'use strict'; + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module1.js'); }); + module.foo(); + // disabled + module[global.unknown](); + module.baz(); +})(); + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module2.js'); }); + const module1 = module; + module1.foo(); +})(); + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module3.js'); }); + const { foo } = module; + foo(); +})(); + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module4.js'); }); + // disabled + const { foo, ...rest } = module; + foo(); + rest.bar(); +})(); + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module5.js'); }); + readFoo({ foo: () => {} }); + readFoo(module); + function readFoo(module1) { + module1.foo(); + } + function readBar(module2) { + module2.bar(); + } + readBar(module); +})(); + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module6.js'); }); + function b({ foo }) { + foo(); + } + b(module); +})(); + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module7.js'); }); + // disabled + function b({ foo, ...rest }) { + foo(); + assert.ok(rest); + } + b(module); +})(); + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module8.js'); }); + // disabled + function b(o1, ...rest) { + assert.ok(rest); + } + b(o1, o2, module); +})(); + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module9.js'); }); + // disabled + function b({ foo = 1 }) { + assert.ok(foo); + } + b(module); +})(); + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module10.js'); }); + (module).bar(); + (global.unknown && module).foo(); + (global.unknown ? module : 'foo').baz(); +})(); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module1.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module1.js new file mode 100644 index 000000000..79e040814 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module1.js @@ -0,0 +1,6 @@ +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +export { bar, baz, foo, qux }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module10.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module10.js new file mode 100644 index 000000000..79e040814 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module10.js @@ -0,0 +1,6 @@ +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +export { bar, baz, foo, qux }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module2.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module2.js new file mode 100644 index 000000000..79e040814 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module2.js @@ -0,0 +1,6 @@ +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +export { bar, baz, foo, qux }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module3.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module3.js new file mode 100644 index 000000000..4ac31b619 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module3.js @@ -0,0 +1,3 @@ +const foo = () => {}; + +export { foo }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module4.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module4.js new file mode 100644 index 000000000..79e040814 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module4.js @@ -0,0 +1,6 @@ +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +export { bar, baz, foo, qux }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module5.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module5.js new file mode 100644 index 000000000..79e040814 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module5.js @@ -0,0 +1,6 @@ +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +export { bar, baz, foo, qux }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module6.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module6.js new file mode 100644 index 000000000..4ac31b619 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module6.js @@ -0,0 +1,3 @@ +const foo = () => {}; + +export { foo }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module7.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module7.js new file mode 100644 index 000000000..79e040814 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module7.js @@ -0,0 +1,6 @@ +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +export { bar, baz, foo, qux }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module8.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module8.js new file mode 100644 index 000000000..79e040814 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module8.js @@ -0,0 +1,6 @@ +const foo = () => {}; +const bar = () => {}; +const baz = () => {}; +const qux = () => {}; + +export { bar, baz, foo, qux }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module9.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module9.js new file mode 100644 index 000000000..4ac31b619 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module9.js @@ -0,0 +1,3 @@ +const foo = () => {}; + +export { foo }; diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/main.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/main.js new file mode 100644 index 000000000..9d78ab9ac --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/main.js @@ -0,0 +1,83 @@ +(async () => { + const module = await import('./generated-module1.js'); + module.foo(); + // disabled + module[global.unknown](); + module.baz(); +})(); + +(async () => { + const module = await import('./generated-module2.js'); + const module1 = module; + module1.foo(); +})(); + +(async () => { + const module = await import('./generated-module3.js'); + const { foo } = module; + foo(); +})(); + +(async () => { + const module = await import('./generated-module4.js'); + // disabled + const { foo, ...rest } = module; + foo(); + rest.bar(); +})(); + +(async () => { + const module = await import('./generated-module5.js'); + readFoo({ foo: () => {} }); + readFoo(module); + function readFoo(module1) { + module1.foo(); + } + function readBar(module2) { + module2.bar(); + } + readBar(module); +})(); + +(async () => { + const module = await import('./generated-module6.js'); + function b({ foo }) { + foo(); + } + b(module); +})(); + +(async () => { + const module = await import('./generated-module7.js'); + // disabled + function b({ foo, ...rest }) { + foo(); + assert.ok(rest); + } + b(module); +})(); + +(async () => { + const module = await import('./generated-module8.js'); + // disabled + function b(o1, ...rest) { + assert.ok(rest); + } + b(o1, o2, module); +})(); + +(async () => { + const module = await import('./generated-module9.js'); + // disabled + function b({ foo = 1 }) { + assert.ok(foo); + } + b(module); +})(); + +(async () => { + const module = await import('./generated-module10.js'); + (module).bar(); + (global.unknown && module).foo(); + (global.unknown ? module : 'foo').baz(); +})(); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module1.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module1.js new file mode 100644 index 000000000..e683d313e --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module1.js @@ -0,0 +1,13 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + const bar = exports("bar", () => {}); + const baz = exports("baz", () => {}); + const qux = exports("qux", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module10.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module10.js new file mode 100644 index 000000000..e683d313e --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module10.js @@ -0,0 +1,13 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + const bar = exports("bar", () => {}); + const baz = exports("baz", () => {}); + const qux = exports("qux", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module2.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module2.js new file mode 100644 index 000000000..e683d313e --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module2.js @@ -0,0 +1,13 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + const bar = exports("bar", () => {}); + const baz = exports("baz", () => {}); + const qux = exports("qux", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module3.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module3.js new file mode 100644 index 000000000..72f26099c --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module3.js @@ -0,0 +1,10 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module4.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module4.js new file mode 100644 index 000000000..e683d313e --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module4.js @@ -0,0 +1,13 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + const bar = exports("bar", () => {}); + const baz = exports("baz", () => {}); + const qux = exports("qux", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module5.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module5.js new file mode 100644 index 000000000..e683d313e --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module5.js @@ -0,0 +1,13 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + const bar = exports("bar", () => {}); + const baz = exports("baz", () => {}); + const qux = exports("qux", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module6.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module6.js new file mode 100644 index 000000000..72f26099c --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module6.js @@ -0,0 +1,10 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module7.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module7.js new file mode 100644 index 000000000..e683d313e --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module7.js @@ -0,0 +1,13 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + const bar = exports("bar", () => {}); + const baz = exports("baz", () => {}); + const qux = exports("qux", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module8.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module8.js new file mode 100644 index 000000000..e683d313e --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module8.js @@ -0,0 +1,13 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + const bar = exports("bar", () => {}); + const baz = exports("baz", () => {}); + const qux = exports("qux", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module9.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module9.js new file mode 100644 index 000000000..72f26099c --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module9.js @@ -0,0 +1,10 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/main.js b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/main.js new file mode 100644 index 000000000..e5eda4d49 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/main.js @@ -0,0 +1,92 @@ +System.register([], (function (exports, module) { + 'use strict'; + return { + execute: (function () { + + (async () => { + const module$1 = await module.import('./generated-module1.js'); + module$1.foo(); + // disabled + module$1[global.unknown](); + module$1.baz(); + })(); + + (async () => { + const module$1 = await module.import('./generated-module2.js'); + const module1 = module$1; + module1.foo(); + })(); + + (async () => { + const module$1 = await module.import('./generated-module3.js'); + const { foo } = module$1; + foo(); + })(); + + (async () => { + const module$1 = await module.import('./generated-module4.js'); + // disabled + const { foo, ...rest } = module$1; + foo(); + rest.bar(); + })(); + + (async () => { + const module$1 = await module.import('./generated-module5.js'); + readFoo({ foo: () => {} }); + readFoo(module$1); + function readFoo(module1) { + module1.foo(); + } + function readBar(module2) { + module2.bar(); + } + readBar(module$1); + })(); + + (async () => { + const module$1 = await module.import('./generated-module6.js'); + function b({ foo }) { + foo(); + } + b(module$1); + })(); + + (async () => { + const module$1 = await module.import('./generated-module7.js'); + // disabled + function b({ foo, ...rest }) { + foo(); + assert.ok(rest); + } + b(module$1); + })(); + + (async () => { + const module$1 = await module.import('./generated-module8.js'); + // disabled + function b(o1, ...rest) { + assert.ok(rest); + } + b(o1, o2, module$1); + })(); + + (async () => { + const module$1 = await module.import('./generated-module9.js'); + // disabled + function b({ foo = 1 }) { + assert.ok(foo); + } + b(module$1); + })(); + + (async () => { + const module$1 = await module.import('./generated-module10.js'); + (module$1).bar(); + (global.unknown && module$1).foo(); + (global.unknown ? module$1 : 'foo').baz(); + })(); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/main.js b/test/chunking-form/samples/dynamic-import-with-namespace/main.js new file mode 100644 index 000000000..d7ad776d7 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/main.js @@ -0,0 +1,83 @@ +(async () => { + const module = await import('./module'); + module.foo(); + // disabled + module[global.unknown](); + module.baz(); +})(); + +(async () => { + const module = await import('./module'); + const module1 = module; + module1.foo(); +})(); + +(async () => { + const module = await import('./module'); + const { foo } = module; + foo(); +})(); + +(async () => { + const module = await import('./module'); + // disabled + const { foo, ...rest } = module; + foo(); + rest.bar(); +})(); + +(async () => { + const module = await import('./module'); + readFoo({ foo: () => {} }); + readFoo(module); + function readFoo(module1) { + module1.foo(); + } + function readBar(module2) { + module2.bar(); + } + readBar(module); +})(); + +(async () => { + const module = await import('./module'); + function b({ foo }) { + foo(); + } + b(module); +})(); + +(async () => { + const module = await import('./module'); + // disabled + function b({ foo, ...rest }) { + foo(); + assert.ok(rest); + } + b(module); +})(); + +(async () => { + const module = await import('./module'); + // disabled + function b(o1, ...rest) { + assert.ok(rest); + } + b(o1, o2, module); +})(); + +(async () => { + const module = await import('./module'); + // disabled + function b({ foo = 1 }) { + assert.ok(foo); + } + b(module); +})(); + +(async () => { + const module = await import('./module'); + ('foo', module).bar(); + (global.unknown && module).foo(); + (global.unknown ? module : 'foo').baz(); +})(); diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/module.js b/test/chunking-form/samples/dynamic-import-with-namespace/module.js new file mode 100644 index 000000000..711d56916 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-with-namespace/module.js @@ -0,0 +1,4 @@ +export const foo = () => {}; +export const bar = () => {}; +export const baz = () => {}; +export const qux = () => {}; diff --git a/test/form/samples/argument-deoptimization/no-default-deoptimization/_expected.js b/test/form/samples/argument-deoptimization/no-default-deoptimization/_expected.js index d8c0c92e3..4825187a8 100644 --- a/test/form/samples/argument-deoptimization/no-default-deoptimization/_expected.js +++ b/test/form/samples/argument-deoptimization/no-default-deoptimization/_expected.js @@ -1,4 +1,4 @@ -const obj = { mutated: false, noEffect() {} }; +const obj = { mutated: false}; function updateObj(target) { target.mutated = true; diff --git a/test/form/samples/computed-properties/_expected/es.js b/test/form/samples/computed-properties/_expected.js similarity index 100% rename from test/form/samples/computed-properties/_expected/es.js rename to test/form/samples/computed-properties/_expected.js diff --git a/test/form/samples/computed-properties/_expected/amd.js b/test/form/samples/computed-properties/_expected/amd.js deleted file mode 100644 index 2d2e59712..000000000 --- a/test/form/samples/computed-properties/_expected/amd.js +++ /dev/null @@ -1,19 +0,0 @@ -define(['exports'], (function (exports) { 'use strict'; - - var foo = 'foo'; - var bar = 'bar'; - var baz = 'baz'; - var bam = 'bam'; - - var x = { [foo]: 'bar' }; - - class X { - [bar] () {} - get [baz] () {} - set [bam] ( value ) {} - } - - exports.X = X; - exports.x = x; - -})); diff --git a/test/form/samples/computed-properties/_expected/cjs.js b/test/form/samples/computed-properties/_expected/cjs.js deleted file mode 100644 index 605d4a3e5..000000000 --- a/test/form/samples/computed-properties/_expected/cjs.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -var foo = 'foo'; -var bar = 'bar'; -var baz = 'baz'; -var bam = 'bam'; - -var x = { [foo]: 'bar' }; - -class X { - [bar] () {} - get [baz] () {} - set [bam] ( value ) {} -} - -exports.X = X; -exports.x = x; diff --git a/test/form/samples/computed-properties/_expected/iife.js b/test/form/samples/computed-properties/_expected/iife.js deleted file mode 100644 index 07c70d5cc..000000000 --- a/test/form/samples/computed-properties/_expected/iife.js +++ /dev/null @@ -1,22 +0,0 @@ -var computedProperties = (function (exports) { - 'use strict'; - - var foo = 'foo'; - var bar = 'bar'; - var baz = 'baz'; - var bam = 'bam'; - - var x = { [foo]: 'bar' }; - - class X { - [bar] () {} - get [baz] () {} - set [bam] ( value ) {} - } - - exports.X = X; - exports.x = x; - - return exports; - -})({}); diff --git a/test/form/samples/computed-properties/_expected/system.js b/test/form/samples/computed-properties/_expected/system.js deleted file mode 100644 index ec34fc724..000000000 --- a/test/form/samples/computed-properties/_expected/system.js +++ /dev/null @@ -1,21 +0,0 @@ -System.register('computedProperties', [], (function (exports) { - 'use strict'; - return { - execute: (function () { - - var foo = 'foo'; - var bar = 'bar'; - var baz = 'baz'; - var bam = 'bam'; - - var x = exports("x", { [foo]: 'bar' }); - - class X { - [bar] () {} - get [baz] () {} - set [bam] ( value ) {} - } exports("X", X); - - }) - }; -})); diff --git a/test/form/samples/computed-properties/_expected/umd.js b/test/form/samples/computed-properties/_expected/umd.js deleted file mode 100644 index 44eec078c..000000000 --- a/test/form/samples/computed-properties/_expected/umd.js +++ /dev/null @@ -1,23 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.computedProperties = {})); -})(this, (function (exports) { 'use strict'; - - var foo = 'foo'; - var bar = 'bar'; - var baz = 'baz'; - var bam = 'bam'; - - var x = { [foo]: 'bar' }; - - class X { - [bar] () {} - get [baz] () {} - set [bam] ( value ) {} - } - - exports.X = X; - exports.x = x; - -})); diff --git a/test/form/samples/destructured-known-arguments/_config.js b/test/form/samples/destructured-known-arguments/_config.js new file mode 100644 index 000000000..62a46d2e9 --- /dev/null +++ b/test/form/samples/destructured-known-arguments/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'tracks known argument values through destructuring' +}); diff --git a/test/form/samples/destructured-known-arguments/_expected.js b/test/form/samples/destructured-known-arguments/_expected.js new file mode 100644 index 000000000..cfd0627c1 --- /dev/null +++ b/test/form/samples/destructured-known-arguments/_expected.js @@ -0,0 +1,28 @@ +function test1({ a, noEffect, effect }) { + console.log('OK'); + effect(); +} + +test1({ + a: true, + noEffect() {}, + effect() { + console.log('effect'); + } +}); + +function test2({ a, noEffect, effect }) { + console.log('OK'); + effect(); +} + +const obj2 = { + a: true, + noEffect() {}, + effect() { + console.log('effect'); + } +}; + +test2(obj2); +test2(obj2); diff --git a/test/form/samples/destructured-known-arguments/main.js b/test/form/samples/destructured-known-arguments/main.js new file mode 100644 index 000000000..c4a22063c --- /dev/null +++ b/test/form/samples/destructured-known-arguments/main.js @@ -0,0 +1,32 @@ +function test1({ a, noEffect, effect }) { + if (a) console.log('OK'); + else console.log('REMOVED'); + noEffect(); + effect(); +} + +test1({ + a: true, + noEffect() {}, + effect() { + console.log('effect'); + } +}); + +function test2({ a, noEffect, effect }) { + if (a) console.log('OK'); + else console.log('REMOVED'); + noEffect(); + effect(); +} + +const obj2 = { + a: true, + noEffect() {}, + effect() { + console.log('effect'); + } +}; + +test2(obj2); +test2(obj2); diff --git a/test/form/samples/early-bind-member-expressions/_expected.js b/test/form/samples/early-bind-member-expressions/_expected.js index 9fefa5dfc..b3755754a 100644 --- a/test/form/samples/early-bind-member-expressions/_expected.js +++ b/test/form/samples/early-bind-member-expressions/_expected.js @@ -1,3 +1,3 @@ import * as stuff from 'external'; -stuff.y(); +const {x} = stuff.y(); diff --git a/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_config.js b/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_config.js new file mode 100644 index 000000000..2871b74fd --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_config.js @@ -0,0 +1,4 @@ +module.exports = defineTest({ + description: 'ignores property read side effects via option', + options: { treeshake: { propertyReadSideEffects: false } } +}); diff --git a/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_expected.js b/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_expected.js new file mode 100644 index 000000000..59b5fe8e2 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_expected.js @@ -0,0 +1,4 @@ +const { a} = { + a: true}; + +console.log(a.b); diff --git a/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/main.js b/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/main.js new file mode 100644 index 000000000..2a5bf6c92 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/main.js @@ -0,0 +1,8 @@ +const { a, b } = { + a: true, + get b() { + console.log('effect'); + } +}; + +console.log(a.b); diff --git a/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_config.js b/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_config.js new file mode 100644 index 000000000..a9129d7fb --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'only includes destructured parameter props' +}); diff --git a/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_expected.js b/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_expected.js new file mode 100644 index 000000000..b822667d2 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_expected.js @@ -0,0 +1,7 @@ +function test({ a, d: { e } }) { + console.log(a, e); +} + +test({ + a: { b: 1, c: 2 }, + d: { e: 4}}); diff --git a/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/main.js b/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/main.js new file mode 100644 index 000000000..d7e782dbd --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/main.js @@ -0,0 +1,9 @@ +function test({ a, d: { e } }) { + console.log(a, e); +} + +test({ + a: { b: 1, c: 2 }, + d: { e: 4, f: 5 }, + g: 6 +}); diff --git a/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_config.js b/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_config.js new file mode 100644 index 000000000..c25c9638e --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'removes unused nested properties through destructuring declarations' +}); diff --git a/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_expected.js b/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_expected.js new file mode 100644 index 000000000..48ef3c76d --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_expected.js @@ -0,0 +1,9 @@ +const { + a: { b } +} = { a: { b: { c: 1}}}; +console.log(b.c); + +const { + a: { ...rest } +} = { a: { b: { c: 1, d: 1 }, e: 1 }}; +console.log(rest); diff --git a/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/main.js b/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/main.js new file mode 100644 index 000000000..be4bfe8b7 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/main.js @@ -0,0 +1,9 @@ +const { + a: { b } +} = { a: { b: { c: 1, d: 1 }, e: 1 }, f: 1 }; +console.log(b.c); + +const { + a: { ...rest } +} = { a: { b: { c: 1, d: 1 }, e: 1 }, f: 1 }; +console.log(rest); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_config.js b/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_config.js new file mode 100644 index 000000000..b0f0e703a --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'removes unused nested properties' +}); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_expected.js b/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_expected.js new file mode 100644 index 000000000..a87f92cf0 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_expected.js @@ -0,0 +1,2 @@ +const obj = { y: { a: 1} }; +console.log(obj.y.a); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/main.js b/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/main.js new file mode 100644 index 000000000..1a46a5cf6 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/main.js @@ -0,0 +1,2 @@ +const obj = { x: 1, y: { a: 1, b: 2 } }; +console.log(obj.y.a); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_config.js b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_config.js new file mode 100644 index 000000000..780dd0a50 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_config.js @@ -0,0 +1,4 @@ +module.exports = defineTest({ + description: + 'removes props that are not used in a function when part of the parameter is passed to an external function' +}); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_expected.js b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_expected.js new file mode 100644 index 000000000..7e3e405c4 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_expected.js @@ -0,0 +1,6 @@ +function test(obj) { + externalFunc(obj.a); +} + +test({ + a: { b: 1, c: 2 }}); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/main.js b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/main.js new file mode 100644 index 000000000..4f29aed21 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/main.js @@ -0,0 +1,8 @@ +function test(obj) { + externalFunc(obj.a); +} + +test({ + a: { b: 1, c: 2 }, + d: { e: 4, f: 5 } +}); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_config.js b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_config.js new file mode 100644 index 000000000..8a1447349 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'removes props that are not used in a function' +}); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_expected.js b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_expected.js new file mode 100644 index 000000000..ed6b0d512 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_expected.js @@ -0,0 +1,18 @@ +function test1(obj) { + return [obj.a, obj.d.e]; +} + +console.log( + test1({ + a: { b: 1, c: 2 }, + d: { e: 4}}) +); + +function test2(obj) { + console.log(obj.a); + console.log(obj.d.e); +} + +test2({ + a: { b: 1, c: 2 }, + d: { e: 4}}); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/main.js b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/main.js new file mode 100644 index 000000000..3c381fe96 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/main.js @@ -0,0 +1,22 @@ +function test1(obj) { + return [obj.a, obj.d.e]; +} + +console.log( + test1({ + a: { b: 1, c: 2 }, + d: { e: 4, f: 5 }, + g: 6 + }) +); + +function test2(obj) { + console.log(obj.a); + console.log(obj.d.e); +} + +test2({ + a: { b: 1, c: 2 }, + d: { e: 4, f: 5 }, + g: 6 +}); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-props/_config.js b/test/form/samples/object-expression-treeshaking/remove-unused-props/_config.js new file mode 100644 index 000000000..efeec207d --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-props/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'removes unused object properties' +}); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-props/_expected.js b/test/form/samples/object-expression-treeshaking/remove-unused-props/_expected.js new file mode 100644 index 000000000..f73015887 --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-props/_expected.js @@ -0,0 +1,5 @@ +const obj1 = { x: 1}; +const obj2 = { y: { a: 1, b: 2 } }; +const obj3 = { y: { a: 1} }; +const obj4 = { }; +console.log(obj1.x, obj2.y, obj3.y.a, obj4.z); diff --git a/test/form/samples/object-expression-treeshaking/remove-unused-props/main.js b/test/form/samples/object-expression-treeshaking/remove-unused-props/main.js new file mode 100644 index 000000000..3b2940aaa --- /dev/null +++ b/test/form/samples/object-expression-treeshaking/remove-unused-props/main.js @@ -0,0 +1,5 @@ +const obj1 = { x: 1, y: { a: 1, b: 2 } }; +const obj2 = { x: 1, y: { a: 1, b: 2 } }; +const obj3 = { x: 1, y: { a: 1, b: 2 } }; +const obj4 = { x: 1, y: { a: 1, b: 2 } }; +console.log(obj1.x, obj2.y, obj3.y.a, obj4.z); diff --git a/test/form/samples/object-literal-property-overwrites/_expected.js b/test/form/samples/object-literal-property-overwrites/_expected.js index ecd783e1d..0d2ad4b1d 100644 --- a/test/form/samples/object-literal-property-overwrites/_expected.js +++ b/test/form/samples/object-literal-property-overwrites/_expected.js @@ -18,20 +18,14 @@ const retained3 = { retained3.bar(); const retained4 = { - foo: {}, foo: globalThis.unknown }; retained4.foo.bar = 1; const retained5 = { - foo: {}, - ['f' + 'oo']: globalThis.unknown, - ['b' + 'ar']: {}, -}; + ['f' + 'oo']: globalThis.unknown}; retained5.foo.bar = 1; const retained6 = { - ['fo' + 'o']: {}, - ['f' + 'oo']: {} -}; + }; retained6.bar.baz = 1; diff --git a/test/form/samples/optional-chaining-namespace/_expected.js b/test/form/samples/optional-chaining-namespace/_expected.js index fcffb6f8f..3a638fc2e 100644 --- a/test/form/samples/optional-chaining-namespace/_expected.js +++ b/test/form/samples/optional-chaining-namespace/_expected.js @@ -1,4 +1,4 @@ -const foo = { nullVal: null }; +const foo = { }; foo?.x.x; // retained diff --git a/test/form/samples/recursive-destructuring/_config.js b/test/form/samples/recursive-destructuring/_config.js new file mode 100644 index 000000000..36b2eac78 --- /dev/null +++ b/test/form/samples/recursive-destructuring/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'does not fail for recursive variables declarations with destructuring' +}); diff --git a/test/form/samples/recursive-destructuring/_expected.js b/test/form/samples/recursive-destructuring/_expected.js new file mode 100644 index 000000000..5e79f768e --- /dev/null +++ b/test/form/samples/recursive-destructuring/_expected.js @@ -0,0 +1 @@ +var { x } = x; // retained as this should throw diff --git a/test/form/samples/recursive-destructuring/main.js b/test/form/samples/recursive-destructuring/main.js new file mode 100644 index 000000000..5e79f768e --- /dev/null +++ b/test/form/samples/recursive-destructuring/main.js @@ -0,0 +1 @@ +var { x } = x; // retained as this should throw diff --git a/test/form/samples/recursive-property-access/_config.js b/test/form/samples/recursive-property-access/_config.js new file mode 100644 index 000000000..8191c563d --- /dev/null +++ b/test/form/samples/recursive-property-access/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'does not fail for recursive variables declarations with property access' +}); diff --git a/test/form/samples/recursive-property-access/_expected.js b/test/form/samples/recursive-property-access/_expected.js new file mode 100644 index 000000000..52c583f54 --- /dev/null +++ b/test/form/samples/recursive-property-access/_expected.js @@ -0,0 +1 @@ +var x = x.x; // retained as this should throw diff --git a/test/form/samples/recursive-property-access/main.js b/test/form/samples/recursive-property-access/main.js new file mode 100644 index 000000000..52c583f54 --- /dev/null +++ b/test/form/samples/recursive-property-access/main.js @@ -0,0 +1 @@ +var x = x.x; // retained as this should throw diff --git a/test/form/samples/side-effects-getters-and-setters/_expected.js b/test/form/samples/side-effects-getters-and-setters/_expected.js index 31917077c..b7214fb1d 100644 --- a/test/form/samples/side-effects-getters-and-setters/_expected.js +++ b/test/form/samples/side-effects-getters-and-setters/_expected.js @@ -1,12 +1,7 @@ const retained1 = { get effect() { console.log('effect'); - }, - get noEffect() { - const x = 1; - return x; - } -}; + }}; //retained retained1.effect; diff --git a/test/form/samples/side-effects-logical-expressions/_expected/amd.js b/test/form/samples/side-effects-logical-expressions/_expected/amd.js index 5cd0363fa..fc94706a8 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/amd.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/amd.js @@ -9,9 +9,7 @@ define((function () { 'use strict'; const foo = { get effect () { console.log( 'effect' ); - }, - get noEffect () {} - }; + }}; // effect (foo).effect; diff --git a/test/form/samples/side-effects-logical-expressions/_expected/cjs.js b/test/form/samples/side-effects-logical-expressions/_expected/cjs.js index 2efd87aac..a607ca44b 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/cjs.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/cjs.js @@ -9,9 +9,7 @@ console.log( 'effect' ) && {}; const foo = { get effect () { console.log( 'effect' ); - }, - get noEffect () {} -}; + }}; // effect (foo).effect; diff --git a/test/form/samples/side-effects-logical-expressions/_expected/es.js b/test/form/samples/side-effects-logical-expressions/_expected/es.js index 0814df63c..cb958dd28 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/es.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/es.js @@ -7,9 +7,7 @@ console.log( 'effect' ) && {}; const foo = { get effect () { console.log( 'effect' ); - }, - get noEffect () {} -}; + }}; // effect (foo).effect; diff --git a/test/form/samples/side-effects-logical-expressions/_expected/iife.js b/test/form/samples/side-effects-logical-expressions/_expected/iife.js index fb4bea51a..173c1f306 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/iife.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/iife.js @@ -10,9 +10,7 @@ const foo = { get effect () { console.log( 'effect' ); - }, - get noEffect () {} - }; + }}; // effect (foo).effect; diff --git a/test/form/samples/side-effects-logical-expressions/_expected/system.js b/test/form/samples/side-effects-logical-expressions/_expected/system.js index 8853d5df6..c4b22d8ac 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/system.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/system.js @@ -12,9 +12,7 @@ System.register([], (function () { const foo = { get effect () { console.log( 'effect' ); - }, - get noEffect () {} - }; + }}; // effect (foo).effect; diff --git a/test/form/samples/side-effects-logical-expressions/_expected/umd.js b/test/form/samples/side-effects-logical-expressions/_expected/umd.js index 6ff0de937..b9ceed374 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/umd.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/umd.js @@ -12,9 +12,7 @@ const foo = { get effect () { console.log( 'effect' ); - }, - get noEffect () {} - }; + }}; // effect (foo).effect; diff --git a/test/form/samples/side-effects-object-literal-mutation/_expected.js b/test/form/samples/side-effects-object-literal-mutation/_expected.js index f42a72e8a..f2e8bbcc0 100644 --- a/test/form/samples/side-effects-object-literal-mutation/_expected.js +++ b/test/form/samples/side-effects-object-literal-mutation/_expected.js @@ -2,7 +2,7 @@ const retained1 = { x: {} }; retained1.y = 1; retained1.x.y = 2; -const retained2 = { x: {} }; +const retained2 = { }; retained2.y.z = 1; const retained3 = { x: {} }; diff --git a/test/form/samples/side-effects-pattern-assignment/_expected.js b/test/form/samples/side-effects-pattern-assignment/_expected.js index 16c6cefd9..a86b21530 100644 --- a/test/form/samples/side-effects-pattern-assignment/_expected.js +++ b/test/form/samples/side-effects-pattern-assignment/_expected.js @@ -1,3 +1,13 @@ +var a = {}; +({x: a} = globalThis.unknown); + +var b = {}; +({b} = globalThis.unknown); + +var {x: c} = globalThis.unknown; + +var {d} = globalThis.unknown; + var e = {}; ({x: e} = globalThis.unknown); e.foo = 1; @@ -12,6 +22,16 @@ g.foo = 1; var {h} = globalThis.unknown; h.foo = 1; +var i = {}; +[i] = globalThis.unknown; + +var [j] = globalThis.unknown; + +var k = {}; +[,...k] = globalThis.unknown; + +var [,...l] = globalThis.unknown; + var m = {}; [m] = globalThis.unknown; m.foo = 1; diff --git a/test/form/samples/system-export-declarations/_expected.js b/test/form/samples/system-export-declarations/_expected.js index 81f189c97..80e548b6c 100644 --- a/test/form/samples/system-export-declarations/_expected.js +++ b/test/form/samples/system-export-declarations/_expected.js @@ -23,7 +23,7 @@ System.register([], (function (exports) { console.log(e1, e2, e3); // destructuring declaration - let {f1, f2} = globalThis.obj; exports("f2", f2); + let {f1, f2} = globalThis.obj, {f3} = globalThis.obj; exports("f2", f2); }) }; diff --git a/test/form/samples/treeshake-deterministic-dynamic-import/_expected.js b/test/form/samples/treeshake-deterministic-dynamic-import/_expected.js index 8a69e51d0..c7115bf3b 100644 --- a/test/form/samples/treeshake-deterministic-dynamic-import/_expected.js +++ b/test/form/samples/treeshake-deterministic-dynamic-import/_expected.js @@ -18,15 +18,15 @@ function _mergeNamespaces(n, m) { async function entry() { // simple const { foo1: foo } = await Promise.resolve().then(function () { return sub1; }); - await Promise.resolve().then(function () { return sub1; }); + const { doesNotExists } = await Promise.resolve().then(function () { return sub1; }); (await Promise.resolve().then(function () { return sub2; })).bar2(); - await Promise.resolve().then(function () { return sub2; }); - await Promise.resolve().then(function () { return sub2; }); + const { foo2 } = await Promise.resolve().then(function () { return sub2; }); + const { foo3 } = await Promise.resolve().then(function () { return sub2; }); Promise.resolve().then(function () { return sub2; }).then(({ baz2 }) => baz2); Promise.resolve().then(function () { return sub2; }).then(function({ reexported }) { }); // external with unknown namespace - await Promise.resolve().then(function () { return sub4; }); + const { foo4, x } = await Promise.resolve().then(function () { return sub4; }); // side-effect only Promise.resolve().then(function () { return effect1; }); @@ -37,10 +37,10 @@ async function entry() { Promise.resolve().then(function () { return effect6; }).finally(() => {}); // bail out - await Promise.resolve().then(function () { return bail1$1; }); + const { named1 } = await Promise.resolve().then(function () { return bail1$1; }); Promise.resolve().then(function () { return bail1$1; }); // this make it bail out - await Promise.resolve().then(function () { return bail2$1; }) + const { ...named2 } = await Promise.resolve().then(function () { return bail2$1; }) (await Promise.resolve().then(function () { return bail3$1; }))[foo]; @@ -55,9 +55,13 @@ async function entry() { Promise.resolve().then(function () { return bail8$1; }), ]; - await Promise.resolve().then(function () { return bail9$1; }); + const { [foo]: bar } = await Promise.resolve().then(function () { return bail9$1; }); Promise.resolve().then(function () { return bail10$1; }).then(({ [foo]: bar }) => {}); + + { + const [name11] = await Promise.resolve().then(function () { return bail11$1; }); + } } function foo1() { @@ -236,4 +240,13 @@ var bail10$1 = /*#__PURE__*/Object.freeze({ named10: named10 }); +var bail11 = '@included-bail-11'; +const named11 = 'bail11'; + +var bail11$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + default: bail11, + named11: named11 +}); + export { entry }; diff --git a/test/form/samples/treeshake-deterministic-dynamic-import/main.js b/test/form/samples/treeshake-deterministic-dynamic-import/main.js index 48bd026a6..45bdcf41d 100644 --- a/test/form/samples/treeshake-deterministic-dynamic-import/main.js +++ b/test/form/samples/treeshake-deterministic-dynamic-import/main.js @@ -43,4 +43,8 @@ export async function entry() { const { [foo]: bar } = await import('./bail-9.js') import('./bail-10.js').then(({ [foo]: bar }) => {}) + + { + const [name11] = await import('./bail-11.js'); + } } diff --git a/test/function/samples/deoptimize-nested-function-arg/_config.js b/test/function/samples/deoptimize-nested-function-arg/_config.js new file mode 100644 index 000000000..c35dc775c --- /dev/null +++ b/test/function/samples/deoptimize-nested-function-arg/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'deoptimizes call arguments to functions nested in function properties' +}); diff --git a/test/function/samples/deoptimize-nested-function-arg/main.js b/test/function/samples/deoptimize-nested-function-arg/main.js new file mode 100644 index 000000000..ce66ae745 --- /dev/null +++ b/test/function/samples/deoptimize-nested-function-arg/main.js @@ -0,0 +1,8 @@ +function test() {} + +test.mutate = a => (a.mutated = true); + +const obj = { mutated: false }; +test.mutate(obj); + +assert.strictEqual(obj.mutated ? 'OK' : 'FAIL', 'OK'); diff --git a/test/function/samples/deoptimize-via-arguments/main.js b/test/function/samples/deoptimize-via-arguments/main.js index 1b9072bb0..9bebcde68 100644 --- a/test/function/samples/deoptimize-via-arguments/main.js +++ b/test/function/samples/deoptimize-via-arguments/main.js @@ -13,5 +13,5 @@ var obj2 = { mutate(obj1, obj2); -assert.ok(obj1.x ? true : false); -assert.ok(obj2.x ? true : false); +assert.ok(obj1.x ? true : false, 'obj1'); +assert.ok(obj2.x ? true : false, 'obj2'); diff --git a/test/function/samples/hashing/maximum-hash-size/_config.js b/test/function/samples/hashing/maximum-hash-size/_config.js index 1c4d82f79..ac8a747c9 100644 --- a/test/function/samples/hashing/maximum-hash-size/_config.js +++ b/test/function/samples/hashing/maximum-hash-size/_config.js @@ -1,9 +1,9 @@ module.exports = defineTest({ description: 'throws when the maximum hash size is exceeded', - options: { output: { chunkFileNames: '[hash:23].js' } }, + options: { output: { chunkFileNames: '[hash:22].js' } }, generateError: { code: 'VALIDATION_ERROR', message: - 'Hashes cannot be longer than 22 characters, received 23. Check the "output.chunkFileNames" option.' + 'Hashes cannot be longer than 21 characters, received 22. Check the "output.chunkFileNames" option.' } }); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/_config.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/_config.js new file mode 100644 index 000000000..cdeea3d02 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'makes sure to deconflict all variables in an array pattern if included' +}); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/dep.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/dep.js new file mode 100644 index 000000000..9bcefbb31 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/dep.js @@ -0,0 +1,2 @@ +const Foo = { ok: true }; +export { Foo as default }; diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/main.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/main.js new file mode 100644 index 000000000..473a5dc8d --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/main.js @@ -0,0 +1,6 @@ +import bar from './dep.js'; + +const [Foo, Bar] = [1, 2]; + +assert.deepStrictEqual(bar, { ok: true }); +assert.deepStrictEqual(Bar, 2); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/_config.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/_config.js new file mode 100644 index 000000000..777822790 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/_config.js @@ -0,0 +1,4 @@ +module.exports = defineTest({ + description: + 'makes sure to deconflict variables that are destructured for side effects in their array pattern default values only' +}); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/dep.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/dep.js new file mode 100644 index 000000000..9bcefbb31 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/dep.js @@ -0,0 +1,2 @@ +const Foo = { ok: true }; +export { Foo as default }; diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/main.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/main.js new file mode 100644 index 000000000..cbcf4a15b --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/main.js @@ -0,0 +1,9 @@ +import bar from './dep.js'; +let mutated = false; +const [ + Foo = (() => { + mutated = true; + })() +] = []; +assert.ok(mutated); +assert.deepStrictEqual(bar, { ok: true }); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/_config.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/_config.js new file mode 100644 index 000000000..df011c9a1 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/_config.js @@ -0,0 +1,4 @@ +module.exports = defineTest({ + description: + 'makes sure to deconflict variables that are destructured for side effects in their default values only' +}); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/dep.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/dep.js new file mode 100644 index 000000000..9bcefbb31 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/dep.js @@ -0,0 +1,2 @@ +const Foo = { ok: true }; +export { Foo as default }; diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/main.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/main.js new file mode 100644 index 000000000..c39128e9f --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/main.js @@ -0,0 +1,9 @@ +import bar from './dep.js'; +let mutated = false; +const { + Foo = (() => { + mutated = true; + })() +} = {}; +assert.ok(mutated); +assert.deepStrictEqual(bar, { ok: true }); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/_config.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/_config.js new file mode 100644 index 000000000..c7e939766 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/_config.js @@ -0,0 +1,4 @@ +module.exports = defineTest({ + description: + 'makes sure to deconflict variables that are destructured for side effects in their key only' +}); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/dep.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/dep.js new file mode 100644 index 000000000..9bcefbb31 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/dep.js @@ -0,0 +1,2 @@ +const Foo = { ok: true }; +export { Foo as default }; diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/main.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/main.js new file mode 100644 index 000000000..2809a2f9e --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/main.js @@ -0,0 +1,10 @@ +import bar from './dep.js'; +let mutated = false; +const { + [(() => { + mutated = true; + return 'Foo'; + })()]: Foo +} = { Foo: true }; +assert.ok(mutated); +assert.deepStrictEqual(bar, { ok: true }); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/_config.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/_config.js new file mode 100644 index 000000000..575f09159 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'makes sure to deconflict variables that are destructured for side effects only' +}); diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/dep.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/dep.js new file mode 100644 index 000000000..9bcefbb31 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/dep.js @@ -0,0 +1,2 @@ +const Foo = { ok: true }; +export { Foo as default }; diff --git a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/main.js b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/main.js new file mode 100644 index 000000000..29c4a2fc5 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/main.js @@ -0,0 +1,9 @@ +import bar from './dep.js'; +let mutated = false; +const { Foo } = { + get Foo() { + mutated = true; + } +}; +assert.ok(mutated); +assert.deepStrictEqual(bar, { ok: true }); diff --git a/test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/_config.js b/test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/_config.js new file mode 100644 index 000000000..44ddb5912 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'deoptimizes arguments of calls to destructured functions' +}); diff --git a/test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/main.js b/test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/main.js new file mode 100644 index 000000000..95a2287a8 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/main.js @@ -0,0 +1,13 @@ +var { a } = { + a: { + b(x) { + x.mutated = true; + } + }, + b() {} +}; + +var obj = { mutated: false }; +a.b(obj); + +assert.strictEqual(obj.mutated ? 'OK' : 'FAILED', 'OK'); diff --git a/test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/_config.js b/test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/_config.js new file mode 100644 index 000000000..d7e2cced9 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'deoptimizes the object if a mutating getter is destructured' +}); diff --git a/test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/main.js b/test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/main.js new file mode 100644 index 000000000..60b29d3d5 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/main.js @@ -0,0 +1,9 @@ +const { a, b } = { + get a() { + // We cannot remove a as destructuring will trigger this getter + this.b = true; + }, + b: false +}; + +assert.strictEqual(b ? 'OK' : 'FAILED', 'OK'); diff --git a/test/function/samples/object-expression-treeshaking/do-not-destructure-unused/_config.js b/test/function/samples/object-expression-treeshaking/do-not-destructure-unused/_config.js new file mode 100644 index 000000000..394122326 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/do-not-destructure-unused/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'ensures that unused properties that are potentially removed are not destructure' +}); diff --git a/test/function/samples/object-expression-treeshaking/do-not-destructure-unused/main.js b/test/function/samples/object-expression-treeshaking/do-not-destructure-unused/main.js new file mode 100644 index 000000000..8ccfd46e6 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/do-not-destructure-unused/main.js @@ -0,0 +1,17 @@ +const { + x: { + y: { z } + }, + a +} = { x: { y: { z: true } }, a: true }; +assert.ok(a); + +function test({ + x: { + y: { z } + }, + a +}) { + return a; +} +assert.ok(test({ x: { y: { z: true } }, a: true })); diff --git a/test/function/samples/object-expression-treeshaking/exports/_config.js b/test/function/samples/object-expression-treeshaking/exports/_config.js new file mode 100644 index 000000000..9c660cbb7 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/exports/_config.js @@ -0,0 +1,13 @@ +const assert = require('node:assert'); + +module.exports = defineTest({ + description: 'includes all paths of exported objects', + exports(exports) { + assert.deepStrictEqual(exports, { + foo: { + a: 1, + b: { c: 2 } + } + }); + } +}); diff --git a/test/function/samples/object-expression-treeshaking/exports/main.js b/test/function/samples/object-expression-treeshaking/exports/main.js new file mode 100644 index 000000000..f905aa8f9 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/exports/main.js @@ -0,0 +1,4 @@ +export const foo = { + a: 1, + b: { c: 2 } +}; diff --git a/test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/_config.js b/test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/_config.js new file mode 100644 index 000000000..283d1dc57 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'retrieves literal values from destructured variables' +}); diff --git a/test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/main.js b/test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/main.js new file mode 100644 index 000000000..fea5611f9 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/main.js @@ -0,0 +1,6 @@ +var { a } = { + a: { b: true }, + b: false +}; + +assert.strictEqual(a.b ? 'OK' : 'FAILED', 'OK'); diff --git a/test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/_config.js b/test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/_config.js new file mode 100644 index 000000000..573a3cd45 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'retrieves return expressions from destructured variables' +}); diff --git a/test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/main.js b/test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/main.js new file mode 100644 index 000000000..0f800d083 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/main.js @@ -0,0 +1,6 @@ +var { a } = { + a: { b: () => true }, + b: () => false +}; + +assert.strictEqual(a.b() ? 'OK' : 'FAILED', 'OK'); diff --git a/test/function/samples/object-expression-treeshaking/include-call-arguments-path/_config.js b/test/function/samples/object-expression-treeshaking/include-call-arguments-path/_config.js new file mode 100644 index 000000000..3021ca724 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-call-arguments-path/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'uses the correct path when including call arguments' +}); diff --git a/test/function/samples/object-expression-treeshaking/include-call-arguments-path/main.js b/test/function/samples/object-expression-treeshaking/include-call-arguments-path/main.js new file mode 100644 index 000000000..57709cc1a --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-call-arguments-path/main.js @@ -0,0 +1,9 @@ +let result = null; + +function test() {} +test.a = value => (result = value); + +const { a } = test; +a(1); + +assert.strictEqual(result, 1); diff --git a/test/function/samples/object-expression-treeshaking/include-destructuring-rest/_config.js b/test/function/samples/object-expression-treeshaking/include-destructuring-rest/_config.js new file mode 100644 index 000000000..a3e2d8dd3 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-destructuring-rest/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'includes rest properties in destructuring declarations' +}); diff --git a/test/function/samples/object-expression-treeshaking/include-destructuring-rest/main.js b/test/function/samples/object-expression-treeshaking/include-destructuring-rest/main.js new file mode 100644 index 000000000..1909f73bb --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-destructuring-rest/main.js @@ -0,0 +1,15 @@ +const { + a: { e: e1, ...rest1 } +} = { a: { b: { c: 1, d: 1 }, e: 1 }, f: 1 }; +assert.deepStrictEqual(rest1, { b: { c: 1, d: 1 } }); + +const { ...rest2 } = { a: { b: { c: 1, d: 1 }, e: 1 }, f: 1 }; +assert.deepStrictEqual(rest2, { a: { b: { c: 1, d: 1 }, e: 1 }, f: 1 }); + +const { + a: { e: e3, ...rest3 }, + f +} = { a: { b: { c: 1, d: 1 }, e: 1 }, f: 1 }; +assert.strictEqual(e3, 1); +assert.strictEqual(f, 1); +assert.deepStrictEqual(rest3, { b: { c: 1, d: 1 } }); diff --git a/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/_config.js b/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/_config.js new file mode 100644 index 000000000..8b1109d88 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/_config.js @@ -0,0 +1,6 @@ +module.exports = defineTest({ + description: 'includes used dynamic import properties with await', + async exports({ assertImport }) { + await assertImport(); + } +}); diff --git a/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/dep.js b/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/dep.js new file mode 100644 index 000000000..5fb7ac565 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/dep.js @@ -0,0 +1 @@ +export const foo = { bar: { baz: 42 } }; diff --git a/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/main.js b/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/main.js new file mode 100644 index 000000000..089c744b1 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/main.js @@ -0,0 +1,4 @@ +export async function assertImport() { + const { foo } = await import('./dep.js'); + assert.strictEqual(foo.bar.baz, 42); +} diff --git a/test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/_config.js b/test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/_config.js new file mode 100644 index 000000000..6fde0ab66 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'includes redeclared destructured variable paths' +}); diff --git a/test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/main.js b/test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/main.js new file mode 100644 index 000000000..7959bf5e3 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/main.js @@ -0,0 +1,5 @@ +var { a } = { a: { b: 1 } }; +assert.strictEqual(a.b, 1); + +var { a } = { a: { b: 2 } }; +assert.strictEqual(a.b, 2); diff --git a/test/function/samples/object-expression-treeshaking/include-this-unknown-function/_config.js b/test/function/samples/object-expression-treeshaking/include-this-unknown-function/_config.js new file mode 100644 index 000000000..5d6a6ecac --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-this-unknown-function/_config.js @@ -0,0 +1,8 @@ +module.exports = defineTest({ + description: 'includes all context properties if the handler is unknown', + context: { + external() { + this.bar(); + } + } +}); diff --git a/test/function/samples/object-expression-treeshaking/include-this-unknown-function/main.js b/test/function/samples/object-expression-treeshaking/include-this-unknown-function/main.js new file mode 100644 index 000000000..f25e812ab --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-this-unknown-function/main.js @@ -0,0 +1,11 @@ +let mutated = false; + +const obj = { + foo: external, + bar() { + mutated = true; + } +}; + +obj.foo(); +assert.ok(mutated ? true : false); diff --git a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/_config.js b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/_config.js new file mode 100644 index 000000000..7669a3d47 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'destructures unused properties with getter side effects in assignments' +}); diff --git a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/main.js b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/main.js new file mode 100644 index 000000000..3049bcb52 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/main.js @@ -0,0 +1,36 @@ +const effects1 = []; +let x1; +({ x1 } = { + get x1() { + effects1.push('x1'); + }, + get y1() { + effects1.push('y1'); + } +}); +assert.deepStrictEqual(effects1, ['x1'], 'effects1'); + +const effects2 = []; +let y2; +({ + x2: { y2 } +} = { + x2: { + get y2() { + effects2.push('y2'); + } + } +}); +assert.deepStrictEqual(effects2, ['y2'], 'effects2'); + +const effects3 = []; +let x3, rest3; +({ x3, ...rest3 } = { + get x3() { + effects3.push('x3'); + }, + get y3() { + effects3.push('y3'); + } +}); +assert.deepStrictEqual(effects3, ['x3', 'y3'], 'effects3'); diff --git a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/_config.js b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/_config.js new file mode 100644 index 000000000..7368c76c3 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'destructures unused properties with getter side effects in calls' +}); diff --git a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/main.js b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/main.js new file mode 100644 index 000000000..8597d0263 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/main.js @@ -0,0 +1,34 @@ +const effects1 = []; +function test1({ x1 }) {} +test1({ + get x1() { + effects1.push('x1'); + }, + get y1() { + effects1.push('y1'); + } +}); +assert.deepStrictEqual(effects1, ['x1'], 'effects1'); + +const effects2 = []; +function test2({ x2: { y2 } }) {} +test2({ + x2: { + get y2() { + effects2.push('y2'); + } + } +}); +assert.deepStrictEqual(effects2, ['y2'], 'effects2'); + +const effects3 = []; +function test3( { x3, ...rest3 }){} +test3({ + get x3() { + effects3.push('x3'); + }, + get y3() { + effects3.push('y3'); + } +}); +assert.deepStrictEqual(effects3, ['x3', 'y3'], 'effects3'); diff --git a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/_config.js b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/_config.js new file mode 100644 index 000000000..1f855971b --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'destructures unused properties with getter side effects' +}); diff --git a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/main.js b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/main.js new file mode 100644 index 000000000..9750d2228 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/main.js @@ -0,0 +1,33 @@ +const effects1 = []; +const { x1 } = { + get x1() { + effects1.push('x1'); + }, + get y1() { + effects1.push('y1'); + } +}; +assert.deepStrictEqual(effects1, ['x1'], 'effects1'); + +const effects2 = []; +const { + x2: { y2 } +} = { + x2: { + get y2() { + effects2.push('y2'); + } + } +}; +assert.deepStrictEqual(effects2, ['y2'], 'effects2'); + +const effects3 = []; +const { x3, ...rest3 } = { + get x3() { + effects3.push('x3'); + }, + get y3() { + effects3.push('y3'); + } +}; +assert.deepStrictEqual(effects3, ['x3', 'y3'], 'effects3'); diff --git a/test/function/samples/object-expression-treeshaking/object-side-effects/_config.js b/test/function/samples/object-expression-treeshaking/object-side-effects/_config.js new file mode 100644 index 000000000..dfb161d6e --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/object-side-effects/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'considers side effects in unused property declarations' +}); diff --git a/test/function/samples/object-expression-treeshaking/object-side-effects/main.js b/test/function/samples/object-expression-treeshaking/object-side-effects/main.js new file mode 100644 index 000000000..d32c4546a --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/object-side-effects/main.js @@ -0,0 +1,22 @@ +let modified1 = false; +let modified2 = false; + +function effect1() { + modified1 = true; + return 'keyEffect'; +} + +function effect2() { + modified2 = true; + return 4; +} + +const obj = { + used: 1, + unused: 2, + [effect1()]: 3, + valueEffect: effect2() +}; +assert.strictEqual(obj.used, 1); +assert.ok(modified1, 'first'); +assert.ok(modified2, 'second'); diff --git a/test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/_config.js b/test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/_config.js new file mode 100644 index 000000000..07cdb40b8 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'tracks property access side effects for destructured variables' +}); diff --git a/test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/main.js b/test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/main.js new file mode 100644 index 000000000..eff4d3124 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/main.js @@ -0,0 +1,14 @@ +let mutated = false; + +var { a } = { + a: { + get b() { + mutated = true; + return {}; + } + }, + b: {} +}; + +a.b; +assert.ok(mutated); diff --git a/test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/_config.js b/test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/_config.js new file mode 100644 index 000000000..5f3148007 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'tracks property assignment side effects for destructured variables' +}); diff --git a/test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/main.js b/test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/main.js new file mode 100644 index 000000000..1fae33053 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/main.js @@ -0,0 +1,13 @@ +let mutated = false; + +var { a } = { + a: { + set b(value) { + mutated = value; + } + }, + b: {} +}; + +a.b = true; +assert.ok(mutated); diff --git a/test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/_config.js b/test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/_config.js new file mode 100644 index 000000000..993182b77 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'tracks call side effects for destructured variables' +}); diff --git a/test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/main.js b/test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/main.js new file mode 100644 index 000000000..09711eded --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/main.js @@ -0,0 +1,13 @@ +let mutated = false; + +var { a } = { + a: { + b() { + mutated = true; + } + }, + b() {} +}; + +a.b(); +assert.ok(mutated); diff --git a/test/function/samples/object-expression-treeshaking/track-destructured-setter/_config.js b/test/function/samples/object-expression-treeshaking/track-destructured-setter/_config.js new file mode 100644 index 000000000..6268facf7 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/track-destructured-setter/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'tracks side effects from setters in destructuring assignments' +}); diff --git a/test/function/samples/object-expression-treeshaking/track-destructured-setter/main.js b/test/function/samples/object-expression-treeshaking/track-destructured-setter/main.js new file mode 100644 index 000000000..58d231674 --- /dev/null +++ b/test/function/samples/object-expression-treeshaking/track-destructured-setter/main.js @@ -0,0 +1,8 @@ +let effect = false; +const obj = { + set foo(value) { + effect = value; + } +}; +({ foo: obj.foo } = { foo: 'value' }); +assert.strictEqual(effect, 'value'); diff --git a/test/function/samples/object-tree-shaking-for-global-assignment/_config.js b/test/function/samples/object-tree-shaking-for-global-assignment/_config.js new file mode 100644 index 000000000..342682dfe --- /dev/null +++ b/test/function/samples/object-tree-shaking-for-global-assignment/_config.js @@ -0,0 +1,6 @@ +module.exports = defineTest({ + description: 'preserve the object on the right side of the global assignment', + context: { + b: {} + } +}); diff --git a/test/function/samples/object-tree-shaking-for-global-assignment/main.js b/test/function/samples/object-tree-shaking-for-global-assignment/main.js new file mode 100644 index 000000000..2b3e70c2b --- /dev/null +++ b/test/function/samples/object-tree-shaking-for-global-assignment/main.js @@ -0,0 +1,6 @@ +function foo() { + const a = (b.c = { e: 1 }); +} +foo(); + +assert.deepEqual(b.c.e, 1); diff --git a/test/function/samples/object-tree-shaking-for-parameter/_config.js b/test/function/samples/object-tree-shaking-for-parameter/_config.js new file mode 100644 index 000000000..78d27f625 --- /dev/null +++ b/test/function/samples/object-tree-shaking-for-parameter/_config.js @@ -0,0 +1,8 @@ +module.exports = defineTest({ + description: 'preserve the object argument', + context: { + externalFunc(input) { + return input; + } + } +}); diff --git a/test/function/samples/object-tree-shaking-for-parameter/main.js b/test/function/samples/object-tree-shaking-for-parameter/main.js new file mode 100644 index 000000000..0d18beb54 --- /dev/null +++ b/test/function/samples/object-tree-shaking-for-parameter/main.js @@ -0,0 +1,15 @@ +function foo() { + function bar(input2) { + return input2; + } + + function Baz(input) { + this.value = bar(input); + } + + externalFunc(Baz); + + return new Baz({ next: 2 }); +} + +assert.deepEqual(foo().value.next, 2); diff --git a/test/function/samples/object-tree-shaking-in-function-self-call/_config.js b/test/function/samples/object-tree-shaking-in-function-self-call/_config.js new file mode 100644 index 000000000..0148514e6 --- /dev/null +++ b/test/function/samples/object-tree-shaking-in-function-self-call/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'avoid maximum call stack size exceeded' +}); diff --git a/test/function/samples/object-tree-shaking-in-function-self-call/main.js b/test/function/samples/object-tree-shaking-in-function-self-call/main.js new file mode 100644 index 000000000..bb5d1b013 --- /dev/null +++ b/test/function/samples/object-tree-shaking-in-function-self-call/main.js @@ -0,0 +1,7 @@ +function foo(v) { + if (v.a) { + foo(v.b); + foo(v.c); + } +} +foo({ b: 1 }); diff --git a/test/function/samples/object-tree-shaking-with-destructed-export/_config.js b/test/function/samples/object-tree-shaking-with-destructed-export/_config.js new file mode 100644 index 000000000..7fc0d9a35 --- /dev/null +++ b/test/function/samples/object-tree-shaking-with-destructed-export/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'export the full object which in the ObjectPattern' +}); diff --git a/test/function/samples/object-tree-shaking-with-destructed-export/main.js b/test/function/samples/object-tree-shaking-with-destructed-export/main.js new file mode 100644 index 000000000..7d821e50c --- /dev/null +++ b/test/function/samples/object-tree-shaking-with-destructed-export/main.js @@ -0,0 +1,3 @@ +import { bar } from './module'; + +assert(bar.baz, 1); diff --git a/test/function/samples/object-tree-shaking-with-destructed-export/module.js b/test/function/samples/object-tree-shaking-with-destructed-export/module.js new file mode 100644 index 000000000..b60c753a2 --- /dev/null +++ b/test/function/samples/object-tree-shaking-with-destructed-export/module.js @@ -0,0 +1,2 @@ +const foo = { bar: { baz: 1 } }; +export const { bar } = foo; diff --git a/test/function/samples/object-tree-shaking-with-duplicated-function-call/_config.js b/test/function/samples/object-tree-shaking-with-duplicated-function-call/_config.js new file mode 100644 index 000000000..19f844a1e --- /dev/null +++ b/test/function/samples/object-tree-shaking-with-duplicated-function-call/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'get the full object which as a parameter passed to duplicated function call' +}); diff --git a/test/function/samples/object-tree-shaking-with-duplicated-function-call/main.js b/test/function/samples/object-tree-shaking-with-duplicated-function-call/main.js new file mode 100644 index 000000000..4a920f168 --- /dev/null +++ b/test/function/samples/object-tree-shaking-with-duplicated-function-call/main.js @@ -0,0 +1,6 @@ +function foo(c) { + assert.ok(c.a); +} + +foo({ a: 1 }); +foo({ a: 1 }); diff --git a/test/function/samples/preserve-exported-object-in-namespace/_config.js b/test/function/samples/preserve-exported-object-in-namespace/_config.js new file mode 100644 index 000000000..c87382ae0 --- /dev/null +++ b/test/function/samples/preserve-exported-object-in-namespace/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'preserve the exported object that imported by namespace' +}); diff --git a/test/function/samples/preserve-exported-object-in-namespace/main.js b/test/function/samples/preserve-exported-object-in-namespace/main.js new file mode 100644 index 000000000..596387027 --- /dev/null +++ b/test/function/samples/preserve-exported-object-in-namespace/main.js @@ -0,0 +1,2 @@ +import * as module from './module'; +assert.deepEqual(module.foo.bar, 1); diff --git a/test/function/samples/preserve-exported-object-in-namespace/module.js b/test/function/samples/preserve-exported-object-in-namespace/module.js new file mode 100644 index 000000000..1399af4bb --- /dev/null +++ b/test/function/samples/preserve-exported-object-in-namespace/module.js @@ -0,0 +1 @@ +export const foo = { bar: 1 }; diff --git a/test/function/samples/preserve-var-declaration/_config.js b/test/function/samples/preserve-var-declaration/_config.js new file mode 100644 index 000000000..5281c9eb6 --- /dev/null +++ b/test/function/samples/preserve-var-declaration/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'preserve the variableDeclaration that declared by var' +}); diff --git a/test/function/samples/preserve-var-declaration/main.js b/test/function/samples/preserve-var-declaration/main.js new file mode 100644 index 000000000..92df84abc --- /dev/null +++ b/test/function/samples/preserve-var-declaration/main.js @@ -0,0 +1,5 @@ +{ + var a = { c: 1 }; + var b = { a }; + assert.deepEqual(b.a.c, 1); +} diff --git a/test/function/samples/recursive-calls-without-treeshake/_config.js b/test/function/samples/recursive-calls-without-treeshake/_config.js new file mode 100644 index 000000000..9e859a0ff --- /dev/null +++ b/test/function/samples/recursive-calls-without-treeshake/_config.js @@ -0,0 +1,6 @@ +module.exports = defineTest({ + description: 'Avoid maximum call stack error with recursive calls when treeshake is disabled', + options: { + treeshake: false + } +}); diff --git a/test/function/samples/recursive-calls-without-treeshake/main.js b/test/function/samples/recursive-calls-without-treeshake/main.js new file mode 100644 index 000000000..df7cfe3f7 --- /dev/null +++ b/test/function/samples/recursive-calls-without-treeshake/main.js @@ -0,0 +1,10 @@ +function test(callback, index) { + if (index > 0) { + test(callback, index - 1); + } + callback(); +} + +let count = 0; +test(() => count++, 3); +assert.strictEqual(count, 4); diff --git a/test/function/samples/wrap-empty-object-with-double-brackets/_config.js b/test/function/samples/wrap-empty-object-with-double-brackets/_config.js new file mode 100644 index 000000000..51d3a0c6c --- /dev/null +++ b/test/function/samples/wrap-empty-object-with-double-brackets/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'wrap double brackets to empty object' +}); diff --git a/test/function/samples/wrap-empty-object-with-double-brackets/main.js b/test/function/samples/wrap-empty-object-with-double-brackets/main.js new file mode 100644 index 000000000..de9628c93 --- /dev/null +++ b/test/function/samples/wrap-empty-object-with-double-brackets/main.js @@ -0,0 +1,2 @@ +Object.prototype.customize_fn = () => {}; +const c = {}.customize_fn(); diff --git a/wasm/bindings_wasm.d.ts b/wasm/bindings_wasm.d.ts index 2c00d9fee..0ce003799 100644 --- a/wasm/bindings_wasm.d.ts +++ b/wasm/bindings_wasm.d.ts @@ -1,26 +1,26 @@ /* tslint:disable */ /* eslint-disable */ /** -* @param {string} code -* @param {boolean} allow_return_outside_function -* @param {boolean} jsx -* @returns {Uint8Array} -*/ + * @param {string} code + * @param {boolean} allow_return_outside_function + * @param {boolean} jsx + * @returns {Uint8Array} + */ export function parse(code: string, allow_return_outside_function: boolean, jsx: boolean): Uint8Array; /** -* @param {Uint8Array} input -* @returns {string} -*/ + * @param {Uint8Array} input + * @returns {string} + */ export function xxhashBase64Url(input: Uint8Array): string; /** -* @param {Uint8Array} input -* @returns {string} -*/ + * @param {Uint8Array} input + * @returns {string} + */ export function xxhashBase36(input: Uint8Array): string; /** -* @param {Uint8Array} input -* @returns {string} -*/ + * @param {Uint8Array} input + * @returns {string} + */ export function xxhashBase16(input: Uint8Array): string; export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;