From 7ec926c84e290f254418c8efca0c3d9c03fbab55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 01:56:11 +0000 Subject: [PATCH 01/12] chore(deps): lock file maintenance minor/patch updates (#5725) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 941 +++++++++++++++++++------------------- package.json | 10 +- rust/Cargo.lock | 32 +- rust/parse_ast/Cargo.toml | 2 +- 4 files changed, 489 insertions(+), 496 deletions(-) diff --git a/package-lock.json b/package-lock.json index a60100adb..d45a9472e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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..4da7af0e5 100644 --- a/package.json +++ b/package.json @@ -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..ac8feb3b9 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", @@ -1597,9 +1597,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "url" diff --git a/rust/parse_ast/Cargo.toml b/rust/parse_ast/Cargo.toml index 74d60e1a6..6a6985b97 100644 --- a/rust/parse_ast/Cargo.toml +++ b/rust/parse_ast/Cargo.toml @@ -6,7 +6,7 @@ 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"] } From a9acb5730e9c5f02598088af311fb3a02686e471 Mon Sep 17 00:00:00 2001 From: XiaoPi <530257315@qq.com> Date: Fri, 15 Nov 2024 13:35:12 +0800 Subject: [PATCH 02/12] feat: implement object tree-shaking (#5420) * Replace include with includePath * Tree-shaking * Reduce performance overhead * Tree-shaking the namespace if it is passed to function * Optimize tree-shaking for ParameterVariable * refactor: track the usage of namespace * Track arguments * Improve tree-shaking and Tweak tests * make path unconditional * make code more concise * Try to include all properties of arguments in ParameterScope * Add comment to explain includePath semantics * Ensure we do not confuse arguments and parameters by name arguments = what is passed to a call parameters = what is declared in a function * Implement basic object expression tree-shaking This will allow us to more easily track if the inclusion logic is correct. * include UNKNOWN_PATH for externalVariable and syntheticNamespace * extend the includeVariableInModule function to take path * preserve additionalInitializers * wrap double brackets to empty object * include UNKNOWN_PATH for resolved properties when subPath is empty * clear cached path for paramter variable * extends the includePath of Variable to take context * extends InclusionContext to include currentIncludedParameter * record the included Path of trackedArugment * add a recursion tracker to the hasEffectsOnInteractionAtPath call in parameterVariable * preserve the object on the right side of the global assignment * avoid excessive object tree-shaking for parameters * Move Identifier inclusion logic to IdentifierBase To see in one place what happens here and avoid duplication * Use a dedicated tracker so that each path of a variable is included once. * Make the path a required parameter for includeVariableInModule This will result in including the given path. However, if a variable is later called with different paths, we also want to include those paths. Thus if the variable is included, we still call includePath on the variable and rely on the included path tracking logic of the variable to prevent infinite cycles. * Improve order of properties in file * Only explicitly re-include non-empty paths An empty path is equivalent to making something "included" * Rename PathTracker -> EntityPathTracker for better differentiation. * Refine recursion detection Use existing context function and differentiate between different interaction types. This will also allow us to treat recursions as "false" rather than "true". * Improve name We are tracking which parts of an argument are included via destructuring. * Forward destructuring paths when declaring variables The goal is to make it easy then to properly associate included paths with variables declared via destructuring. This will happen in subsequent commits. * Improve handling of unknown keys in inclusion tracker - short-circuiting when including them should save some memory - other symbols will now be handled like unknown, making sure they are not overlooked, e.g. when "unknown number" is already included and checking whether a specific number is included. * Rework how destructuring declarations are handled While the lhs of a destructuring declaration is preserved if included, only relevant paths of the rhs are preserved. This works by storing an initPath with each variable that describes which path of the init is actually the init. * Add detailed regression tests for destructured variables * Rewrite initial support for parameter destructuring * Include context with call arguments We will need this as calling a function on an object, we need to make sure all paths on the object accessed by the function are included. At the moment, this includes many more paths as the logic is not yet very refined. In the future, this should probably become includeInteractionArgumentsAtPath * Only include properties of arguments that are used or destructured * Track argument values through destructuring Also, this will use the addArgumentValue method for tracking known values in the same place we generally track values. Note that at the moment, this will only work one level deep as ParameterVariable only tracks top-level deoptimizations. * Consider correct initPath when including call arguments * Move argument deoptimization for function calls to ReturnValueScope * Prevent access to parameter variables * Clean up comments and add test * Remove some leftover unused code * Fix lint error * Replace included paths with a constant when an unknown path is included Hopefully, this saves some memory. * Do not check audit while installing This can fail a release as our audit check relies on a dependency * Track side effects when destructuring This tracks effects when destructuring properties of an object triggers a getter or an access error. * Only selectively deoptimize on pattern assignment * Track side effects when passing objects with getters to functions The destructuring may trigger the getters. * Ensure to fully include objects via dynamic import We want to tree-shake them eventually, but not this PR: * Observe propertyReadSideEffects: false when destructuring * Observe destructured getter side effects When destructuring getters on an object, we need check whether the getter mutates the object it is defined on. In that case, we cannot remove an unused destructured variable. * 4.27.0-0 * Always include variable for destructured variables Especially in the case where we include for destructuring side effects only, this ensures we avoid variable names conflicts. * 4.27.0-1 --------- Co-authored-by: Lukas Taegert-Atkinson --- browser/package.json | 2 +- package-lock.json | 4 +- package.json | 2 +- scripts/ast-types.js | 6 +- scripts/generate-buffer-parsers.js | 1 + scripts/prepare-release.js | 6 +- src/Graph.ts | 4 +- src/Module.ts | 47 ++++--- src/ast/ExecutionContext.ts | 10 +- src/ast/bufferParsers.ts | 12 +- src/ast/nodes/ArrayExpression.ts | 8 +- src/ast/nodes/ArrayPattern.ts | 63 +++++++-- src/ast/nodes/ArrowFunctionExpression.ts | 16 ++- src/ast/nodes/AssignmentExpression.ts | 21 ++- src/ast/nodes/AssignmentPattern.ts | 42 +++++- src/ast/nodes/AwaitExpression.ts | 9 +- src/ast/nodes/BinaryExpression.ts | 4 +- src/ast/nodes/BlockStatement.ts | 9 +- src/ast/nodes/BreakStatement.ts | 11 +- src/ast/nodes/CallExpression.ts | 34 +++-- src/ast/nodes/CatchClause.ts | 9 +- src/ast/nodes/ChainExpression.ts | 4 +- src/ast/nodes/ClassBody.ts | 13 +- src/ast/nodes/ConditionalExpression.ts | 34 ++--- src/ast/nodes/ContinueStatement.ts | 11 +- src/ast/nodes/DoWhileStatement.ts | 9 +- src/ast/nodes/ExportDefaultDeclaration.ts | 12 +- src/ast/nodes/ForInStatement.ts | 18 ++- src/ast/nodes/ForOfStatement.ts | 16 ++- src/ast/nodes/ForStatement.ts | 15 +- src/ast/nodes/Identifier.ts | 128 +++++++++++++----- src/ast/nodes/IfStatement.ts | 27 ++-- src/ast/nodes/ImportExpression.ts | 34 +++-- src/ast/nodes/JSXOpeningFragment.ts | 9 +- src/ast/nodes/LabeledStatement.ts | 17 ++- src/ast/nodes/LogicalExpression.ts | 20 +-- src/ast/nodes/MemberExpression.ts | 109 +++++++++++---- src/ast/nodes/MetaProperty.ts | 2 +- src/ast/nodes/NewExpression.ts | 19 ++- src/ast/nodes/ObjectExpression.ts | 51 +++++-- src/ast/nodes/ObjectPattern.ts | 78 +++++++++-- src/ast/nodes/Program.ts | 9 +- src/ast/nodes/Property.ts | 105 ++++++++++---- src/ast/nodes/PropertyDefinition.ts | 8 +- src/ast/nodes/RestElement.ts | 68 +++++++++- src/ast/nodes/ReturnStatement.ts | 9 +- src/ast/nodes/SequenceExpression.ts | 14 +- src/ast/nodes/SpreadElement.ts | 9 +- src/ast/nodes/StaticBlock.ts | 9 +- src/ast/nodes/Super.ts | 11 +- src/ast/nodes/SwitchCase.ts | 11 +- src/ast/nodes/SwitchStatement.ts | 11 +- src/ast/nodes/TaggedTemplateExpression.ts | 24 ++-- src/ast/nodes/TemplateElement.ts | 2 +- src/ast/nodes/ThisExpression.ts | 12 +- src/ast/nodes/ThrowStatement.ts | 9 +- src/ast/nodes/TryStatement.ts | 14 +- src/ast/nodes/UnaryExpression.ts | 4 +- src/ast/nodes/UnknownNode.ts | 5 +- src/ast/nodes/UpdateExpression.ts | 6 +- src/ast/nodes/VariableDeclaration.ts | 16 ++- src/ast/nodes/VariableDeclarator.ts | 32 +++-- src/ast/nodes/WhileStatement.ts | 9 +- src/ast/nodes/shared/BitFlags.ts | 3 +- src/ast/nodes/shared/CallExpressionBase.ts | 10 +- src/ast/nodes/shared/ClassNode.ts | 35 +++-- src/ast/nodes/shared/Expression.ts | 21 ++- src/ast/nodes/shared/FunctionBase.ts | 117 +++++----------- src/ast/nodes/shared/FunctionNode.ts | 42 +++--- src/ast/nodes/shared/IdentifierBase.ts | 22 ++- src/ast/nodes/shared/JSXElementBase.ts | 9 +- src/ast/nodes/shared/MethodBase.ts | 12 +- src/ast/nodes/shared/MultiExpression.ts | 4 +- src/ast/nodes/shared/Node.ts | 29 ++-- src/ast/nodes/shared/ObjectEntity.ts | 49 ++++++- src/ast/nodes/shared/ObjectMember.ts | 20 +-- src/ast/nodes/shared/Pattern.ts | 26 +++- src/ast/nodes/shared/chainElements.ts | 4 +- src/ast/nodes/shared/jsxHelpers.ts | 6 +- src/ast/nodes/shared/loops.ts | 3 +- src/ast/scopes/BlockScope.ts | 12 +- src/ast/scopes/CatchBodyScope.ts | 13 +- src/ast/scopes/ClassBodyScope.ts | 3 +- src/ast/scopes/FunctionBodyScope.ts | 11 +- src/ast/scopes/FunctionScope.ts | 23 ++-- src/ast/scopes/ModuleScope.ts | 7 +- src/ast/scopes/ParameterScope.ts | 61 +++++---- src/ast/scopes/ReturnValueScope.ts | 56 ++++++++ src/ast/scopes/Scope.ts | 14 +- src/ast/scopes/TrackingScope.ts | 4 +- src/ast/utils/PathTracker.ts | 81 +++++++++-- src/ast/variables/ArgumentsVariable.ts | 9 +- src/ast/variables/ExportDefaultVariable.ts | 12 +- src/ast/variables/ExportShimVariable.ts | 6 +- src/ast/variables/ExternalVariable.ts | 7 +- src/ast/variables/GlobalVariable.ts | 6 +- src/ast/variables/LocalVariable.ts | 87 +++++++----- src/ast/variables/NamespaceVariable.ts | 10 +- src/ast/variables/ParameterVariable.ts | 97 ++++++++----- .../variables/SyntheticNamedExportVariable.ts | 8 +- src/ast/variables/ThisVariable.ts | 4 +- src/ast/variables/Variable.ts | 6 +- .../dynamic-import-with-namespace/_config.js | 22 +++ .../_expected/amd/generated-module1.js | 13 ++ .../_expected/amd/generated-module10.js | 13 ++ .../_expected/amd/generated-module2.js | 13 ++ .../_expected/amd/generated-module3.js | 7 + .../_expected/amd/generated-module4.js | 13 ++ .../_expected/amd/generated-module5.js | 13 ++ .../_expected/amd/generated-module6.js | 7 + .../_expected/amd/generated-module7.js | 13 ++ .../_expected/amd/generated-module8.js | 13 ++ .../_expected/amd/generated-module9.js | 7 + .../_expected/amd/main.js | 87 ++++++++++++ .../_expected/cjs/generated-module1.js | 11 ++ .../_expected/cjs/generated-module10.js | 11 ++ .../_expected/cjs/generated-module2.js | 11 ++ .../_expected/cjs/generated-module3.js | 5 + .../_expected/cjs/generated-module4.js | 11 ++ .../_expected/cjs/generated-module5.js | 11 ++ .../_expected/cjs/generated-module6.js | 5 + .../_expected/cjs/generated-module7.js | 11 ++ .../_expected/cjs/generated-module8.js | 11 ++ .../_expected/cjs/generated-module9.js | 5 + .../_expected/cjs/main.js | 85 ++++++++++++ .../_expected/es/generated-module1.js | 6 + .../_expected/es/generated-module10.js | 6 + .../_expected/es/generated-module2.js | 6 + .../_expected/es/generated-module3.js | 3 + .../_expected/es/generated-module4.js | 6 + .../_expected/es/generated-module5.js | 6 + .../_expected/es/generated-module6.js | 3 + .../_expected/es/generated-module7.js | 6 + .../_expected/es/generated-module8.js | 6 + .../_expected/es/generated-module9.js | 3 + .../_expected/es/main.js | 83 ++++++++++++ .../_expected/system/generated-module1.js | 13 ++ .../_expected/system/generated-module10.js | 13 ++ .../_expected/system/generated-module2.js | 13 ++ .../_expected/system/generated-module3.js | 10 ++ .../_expected/system/generated-module4.js | 13 ++ .../_expected/system/generated-module5.js | 13 ++ .../_expected/system/generated-module6.js | 10 ++ .../_expected/system/generated-module7.js | 13 ++ .../_expected/system/generated-module8.js | 13 ++ .../_expected/system/generated-module9.js | 10 ++ .../_expected/system/main.js | 92 +++++++++++++ .../dynamic-import-with-namespace/main.js | 83 ++++++++++++ .../dynamic-import-with-namespace/module.js | 4 + .../no-default-deoptimization/_expected.js | 2 +- .../{_expected/es.js => _expected.js} | 0 .../computed-properties/_expected/amd.js | 19 --- .../computed-properties/_expected/cjs.js | 17 --- .../computed-properties/_expected/iife.js | 22 --- .../computed-properties/_expected/system.js | 21 --- .../computed-properties/_expected/umd.js | 23 ---- .../deep-properties-access/_expected.js | 2 +- .../form/samples/deep-properties/_expected.js | 4 +- .../destructured-known-arguments/_config.js | 3 + .../destructured-known-arguments/_expected.js | 28 ++++ .../destructured-known-arguments/main.js | 32 +++++ .../_expected.js | 2 +- .../_config.js | 4 + .../_expected.js | 4 + .../ignore-property-read-side-effects/main.js | 8 ++ .../_config.js | 3 + .../_expected.js | 7 + .../main.js | 9 ++ .../remove-props-via-destructuring/_config.js | 3 + .../_expected.js | 9 ++ .../remove-props-via-destructuring/main.js | 9 ++ .../remove-unused-nested-props/_config.js | 3 + .../remove-unused-nested-props/_expected.js | 2 + .../remove-unused-nested-props/main.js | 2 + .../_config.js | 4 + .../_expected.js | 6 + .../main.js | 8 ++ .../remove-unused-parameter-props/_config.js | 3 + .../_expected.js | 18 +++ .../remove-unused-parameter-props/main.js | 22 +++ .../remove-unused-props/_config.js | 3 + .../remove-unused-props/_expected.js | 5 + .../remove-unused-props/main.js | 5 + .../_expected.js | 10 +- .../optional-chaining-namespace/_expected.js | 2 +- .../_expected.js | 7 +- .../_expected/amd.js | 4 +- .../_expected/cjs.js | 4 +- .../_expected/es.js | 4 +- .../_expected/iife.js | 4 +- .../_expected/system.js | 4 +- .../_expected/umd.js | 4 +- .../_expected.js | 2 +- .../_expected.js | 20 +++ .../system-export-declarations/_expected.js | 2 +- .../_expected.js | 27 +++- .../main.js | 4 + .../deoptimize-nested-function-arg/_config.js | 3 + .../deoptimize-nested-function-arg/main.js | 8 ++ .../samples/deoptimize-via-arguments/main.js | 4 +- .../_config.js | 3 + .../dep.js | 2 + .../main.js | 9 ++ .../_config.js | 3 + .../main.js | 13 ++ .../_config.js | 3 + .../main.js | 9 ++ .../do-not-destructure-unused/_config.js | 3 + .../do-not-destructure-unused/main.js | 17 +++ .../exports/_config.js | 13 ++ .../exports/main.js | 4 + .../_config.js | 3 + .../main.js | 6 + .../_config.js | 3 + .../main.js | 6 + .../include-call-arguments-path/_config.js | 3 + .../include-call-arguments-path/main.js | 9 ++ .../include-destructuring-rest/_config.js | 3 + .../include-destructuring-rest/main.js | 15 ++ .../_config.js | 6 + .../dep.js | 1 + .../main.js | 4 + .../_config.js | 3 + .../main.js | 5 + .../include-this-unknown-function/_config.js | 8 ++ .../include-this-unknown-function/main.js | 11 ++ .../_config.js | 3 + .../main.js | 36 +++++ .../_config.js | 3 + .../main.js | 34 +++++ .../_config.js | 3 + .../main.js | 33 +++++ .../object-side-effects/_config.js | 3 + .../object-side-effects/main.js | 22 +++ .../_config.js | 3 + .../main.js | 14 ++ .../_config.js | 3 + .../main.js | 13 ++ .../_config.js | 3 + .../main.js | 13 ++ .../track-destructured-setter/_config.js | 3 + .../track-destructured-setter/main.js | 8 ++ .../_config.js | 6 + .../main.js | 6 + .../_config.js | 8 ++ .../object-tree-shaking-for-parameter/main.js | 15 ++ .../_config.js | 3 + .../main.js | 7 + .../_config.js | 3 + .../main.js | 3 + .../module.js | 2 + .../_config.js | 3 + .../main.js | 6 + .../_config.js | 3 + .../main.js | 2 + .../module.js | 1 + .../preserve-var-declaration/_config.js | 3 + .../samples/preserve-var-declaration/main.js | 5 + .../_config.js | 6 + .../recursive-calls-without-treeshake/main.js | 10 ++ .../_config.js | 3 + .../main.js | 2 + wasm/bindings_wasm.d.ts | 28 ++-- 263 files changed, 3176 insertions(+), 860 deletions(-) create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_config.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module1.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module10.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module2.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module3.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module4.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module5.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module6.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module7.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module8.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module9.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/main.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module1.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module10.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module2.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module3.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module4.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module5.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module6.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module7.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module8.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module9.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/main.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module1.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module10.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module2.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module3.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module4.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module5.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module6.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module7.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module8.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module9.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/main.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module1.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module10.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module2.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module3.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module4.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module5.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module6.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module7.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module8.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module9.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/main.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/main.js create mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/module.js rename test/form/samples/computed-properties/{_expected/es.js => _expected.js} (100%) delete mode 100644 test/form/samples/computed-properties/_expected/amd.js delete mode 100644 test/form/samples/computed-properties/_expected/cjs.js delete mode 100644 test/form/samples/computed-properties/_expected/iife.js delete mode 100644 test/form/samples/computed-properties/_expected/system.js delete mode 100644 test/form/samples/computed-properties/_expected/umd.js create mode 100644 test/form/samples/destructured-known-arguments/_config.js create mode 100644 test/form/samples/destructured-known-arguments/_expected.js create mode 100644 test/form/samples/destructured-known-arguments/main.js create mode 100644 test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_config.js create mode 100644 test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_expected.js create mode 100644 test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/main.js create mode 100644 test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_config.js create mode 100644 test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_expected.js create mode 100644 test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/main.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_config.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_expected.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/main.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_config.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_expected.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-nested-props/main.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_config.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_expected.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/main.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_config.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_expected.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/main.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-props/_config.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-props/_expected.js create mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-props/main.js create mode 100644 test/function/samples/deoptimize-nested-function-arg/_config.js create mode 100644 test/function/samples/deoptimize-nested-function-arg/main.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/dep.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/main.js create mode 100644 test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/main.js create mode 100644 test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/main.js create mode 100644 test/function/samples/object-expression-treeshaking/do-not-destructure-unused/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/do-not-destructure-unused/main.js create mode 100644 test/function/samples/object-expression-treeshaking/exports/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/exports/main.js create mode 100644 test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/main.js create mode 100644 test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/main.js create mode 100644 test/function/samples/object-expression-treeshaking/include-call-arguments-path/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/include-call-arguments-path/main.js create mode 100644 test/function/samples/object-expression-treeshaking/include-destructuring-rest/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/include-destructuring-rest/main.js create mode 100644 test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/dep.js create mode 100644 test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/main.js create mode 100644 test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/main.js create mode 100644 test/function/samples/object-expression-treeshaking/include-this-unknown-function/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/include-this-unknown-function/main.js create mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/main.js create mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/main.js create mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/main.js create mode 100644 test/function/samples/object-expression-treeshaking/object-side-effects/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/object-side-effects/main.js create mode 100644 test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/main.js create mode 100644 test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/main.js create mode 100644 test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/main.js create mode 100644 test/function/samples/object-expression-treeshaking/track-destructured-setter/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/track-destructured-setter/main.js create mode 100644 test/function/samples/object-tree-shaking-for-global-assignment/_config.js create mode 100644 test/function/samples/object-tree-shaking-for-global-assignment/main.js create mode 100644 test/function/samples/object-tree-shaking-for-parameter/_config.js create mode 100644 test/function/samples/object-tree-shaking-for-parameter/main.js create mode 100644 test/function/samples/object-tree-shaking-in-function-self-call/_config.js create mode 100644 test/function/samples/object-tree-shaking-in-function-self-call/main.js create mode 100644 test/function/samples/object-tree-shaking-with-destructed-export/_config.js create mode 100644 test/function/samples/object-tree-shaking-with-destructed-export/main.js create mode 100644 test/function/samples/object-tree-shaking-with-destructed-export/module.js create mode 100644 test/function/samples/object-tree-shaking-with-duplicated-function-call/_config.js create mode 100644 test/function/samples/object-tree-shaking-with-duplicated-function-call/main.js create mode 100644 test/function/samples/preserve-exported-object-in-namespace/_config.js create mode 100644 test/function/samples/preserve-exported-object-in-namespace/main.js create mode 100644 test/function/samples/preserve-exported-object-in-namespace/module.js create mode 100644 test/function/samples/preserve-var-declaration/_config.js create mode 100644 test/function/samples/preserve-var-declaration/main.js create mode 100644 test/function/samples/recursive-calls-without-treeshake/_config.js create mode 100644 test/function/samples/recursive-calls-without-treeshake/main.js create mode 100644 test/function/samples/wrap-empty-object-with-double-brackets/_config.js create mode 100644 test/function/samples/wrap-empty-object-with-double-brackets/main.js diff --git a/browser/package.json b/browser/package.json index 35079582a..3f4b010ff 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "4.26.0", + "version": "4.27.0-1", "description": "Next-generation ES module bundler browser build", "main": "dist/rollup.browser.js", "module": "dist/es/rollup.browser.js", diff --git a/package-lock.json b/package-lock.json index d45a9472e..7e6b59d24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rollup", - "version": "4.26.0", + "version": "4.27.0-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rollup", - "version": "4.26.0", + "version": "4.27.0-1", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" diff --git a/package.json b/package.json index 4da7af0e5..685cff930 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "4.26.0", + "version": "4.27.0-1", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", 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..54dc76b5f 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,28 @@ 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) { + included = + element?.includeDestructuredIfNecessary(context, includedPatternPath, init) || included; + } + 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 c99578c87..649d10e4a 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; @@ -75,11 +75,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..fc32c7e5c 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,30 @@ 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); + } + 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..a65bd37b6 100644 --- a/src/ast/nodes/MemberExpression.ts +++ b/src/ast/nodes/MemberExpression.ts @@ -7,18 +7,23 @@ 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 { EMPTY_PATH, + type EntityPathTracker, type ObjectPath, type ObjectPathKey, - type PathTracker, SHARED_RECURSION_TRACKER, SymbolToStringTag, UNKNOWN_PATH, @@ -33,9 +38,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,7 +49,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'; +import type { PatternNode } from './shared/Pattern'; +import type Super from './Super'; // To avoid infinite recursions const MAX_PATH_DEPTH = 7; @@ -95,7 +100,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 +172,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 +189,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[]; @@ -209,7 +219,7 @@ export default class MemberExpression 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,33 @@ 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], + context, + includeChildrenRecursively + ); } includeAsAssignmentTarget( @@ -343,21 +377,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 +506,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 +547,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..96c2579d3 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,72 @@ 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 { + let included = + (this.value as PatternNode).includeDestructuredIfNecessary( + context, + this.getPathInProperty(destructuredInitPath), + init + ) || this.included; + included ||= this.key.hasEffects(createHasEffectsContext()); + if (included) { + this.key.includePath(EMPTY_PATH, context, false); + } + 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 +111,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..9b0a0da3b 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,7 +18,7 @@ export default class SpreadElement extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: PathTracker + recursionTracker: EntityPathTracker ): void { if (path.length > 0) { this.argument.deoptimizeArgumentsOnInteractionAtPath( 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/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..4eee63f7b 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,42 @@ 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 { + 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,14 +69,13 @@ 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) { deoptimizeInteraction(interaction); @@ -78,7 +84,12 @@ export default class LocalVariable extends Variable { recursionTracker.withTrackedEntityAtPath( path, this.init, - () => this.init.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker), + () => + this.init.deoptimizeArgumentsOnInteractionAtPath( + interaction, + [...this.initPath, ...path], + recursionTracker + ), undefined ); } @@ -97,15 +108,15 @@ 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([...this.initPath, ...path]); } } getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { if (this.isReassigned) { @@ -116,7 +127,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,7 +140,7 @@ export default class LocalVariable extends Variable { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { if (this.isReassigned) { @@ -137,7 +152,7 @@ export default class LocalVariable extends Variable { () => { this.expressionsToBeDeoptimized.push(origin); return this.init.getReturnExpressionWhenCalledAtPath( - path, + [...this.initPath, ...path], interaction, recursionTracker, origin @@ -157,7 +172,7 @@ export default class LocalVariable extends Variable { 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 +181,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 +190,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 +211,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([...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..174866b88 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'; @@ -15,8 +16,7 @@ import { } from '../nodes/shared/Expression'; import type { ObjectPath, ObjectPathKey } from '../utils/PathTracker'; import { - EMPTY_PATH, - PathTracker, + EntityPathTracker, SHARED_RECURSION_TRACKER, UNKNOWN_PATH, UnknownKey @@ -31,31 +31,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 +66,34 @@ 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); + 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 +108,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 +116,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 +139,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 +161,18 @@ export default class ParameterVariable extends LocalVariable { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: PathTracker, + recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { if (this.isReassigned) { 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 +182,30 @@ 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) { 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 +215,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 +244,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 +271,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/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/deep-properties-access/_expected.js b/test/form/samples/deep-properties-access/_expected.js index c1d319eb8..1322aad74 100644 --- a/test/form/samples/deep-properties-access/_expected.js +++ b/test/form/samples/deep-properties-access/_expected.js @@ -1,2 +1,2 @@ -var obj = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}}}; +var obj = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}; console.log(obj.obj.obj.obj.obj.obj.obj.obj.obj.foo); diff --git a/test/form/samples/deep-properties/_expected.js b/test/form/samples/deep-properties/_expected.js index ea78417e0..82e96daae 100644 --- a/test/form/samples/deep-properties/_expected.js +++ b/test/form/samples/deep-properties/_expected.js @@ -4,8 +4,8 @@ console.log(obj1.foo()); var obj2 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}}}; obj2.obj.obj.obj.obj.obj.obj.obj.foo(); -var obj3 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}}}; +var obj3 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}; if (obj3.obj.obj.obj.obj.obj.obj.obj.obj.foo) console.log('nested'); -var obj4 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}}}; +var obj4 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}; obj4.obj.obj.obj.obj.obj.obj.obj.foo = 'nested'; 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/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/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; From 50697b8c05d306af376d107d99c7f356cab6be50 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Fri, 15 Nov 2024 06:36:58 +0100 Subject: [PATCH 03/12] Reduce max hash size to 21 (#5723) In certain cases, the maximum length was actually 21, resulting in unexpected file names and off-by-one-sourcemaps. --- docs/migration/index.md | 2 +- src/utils/hashPlaceholders.ts | 2 +- test/function/samples/hashing/maximum-hash-size/_config.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/migration/index.md b/docs/migration/index.md index fd5a820d2..98542164b 100644 --- a/docs/migration/index.md +++ b/docs/migration/index.md @@ -24,7 +24,7 @@ Rollup now includes native code that is automatically installed (and removed) as The browser build (`@rollup/browser` on NPM) now relies on a WASM artifact that needs to be provided as well. If you are using the browser build with Vite, you'll need to add `"@rollup/browser"` to `optimizeDeps.exclude`, otherwise `npm run dev` fails with an invalid path to the `.wasm` file (see also [vitejs #14609](https://github.com/vitejs/vite/issues/14609)). Otherwise it should work without any specific intervention. -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 22 characters for technical reasons. +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. When bundling CLI apps, Rollup will now automatically preserve shebang comments in entry files if the output [`format`](../configuration-options/index.md#output-format) is `es` or `cjs`. Previously, you would have needed to add the comment via a plugin. 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/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.' } }); From b58e48bd40a95cd331c30e234549b7f96a7ff901 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 10:56:59 +0100 Subject: [PATCH 04/12] fix(deps): update swc monorepo (major) (#5724) fix(deps): update swc monorepo Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- rust/Cargo.lock | 52 +++++++++++++++++++-------------------- rust/parse_ast/Cargo.toml | 8 +++--- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index ac8feb3b9..f70e211dd 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -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", @@ -1597,9 +1597,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "url" diff --git a/rust/parse_ast/Cargo.toml b/rust/parse_ast/Cargo.toml index 6a6985b97..bf9c95170 100644 --- a/rust/parse_ast/Cargo.toml +++ b/rust/parse_ast/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] 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" From c035068dfebeb959a35a8acf3ff008a249e2af73 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Fri, 15 Nov 2024 10:59:55 +0100 Subject: [PATCH 05/12] 4.27.0 --- CHANGELOG.md | 19 +++++++++++++++++++ browser/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 681f4ff28..04f2fe1b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # rollup changelog +## 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 3f4b010ff..a77334b1b 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "4.27.0-1", + "version": "4.27.0", "description": "Next-generation ES module bundler browser build", "main": "dist/rollup.browser.js", "module": "dist/es/rollup.browser.js", diff --git a/package-lock.json b/package-lock.json index 7e6b59d24..8e7ce9683 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rollup", - "version": "4.27.0-1", + "version": "4.27.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rollup", - "version": "4.27.0-1", + "version": "4.27.0", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" diff --git a/package.json b/package.json index 685cff930..d3dea7fc0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "4.27.0-1", + "version": "4.27.0", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", From faeb9054b629652d2993f0e8e02a12a896dff168 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Fri, 15 Nov 2024 16:24:03 +0100 Subject: [PATCH 06/12] Debug out-of-memory issues with Rollup v4.27.0 (#5727) * Add breakoff condition for deep path access. * 4.27.1-0 * Add more breakoff condition for deep path access. * 4.27.1-1 --- browser/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/ast/nodes/MemberExpression.ts | 17 +++++++++----- src/ast/nodes/SpreadElement.ts | 2 +- src/ast/nodes/shared/ObjectEntity.ts | 2 +- src/ast/utils/limitPathLength.ts | 23 +++++++++++++++++++ src/ast/variables/LocalVariable.ts | 19 +++++++++------ src/ast/variables/ParameterVariable.ts | 13 +++++++++-- .../deep-properties-access/_expected.js | 2 +- .../form/samples/deep-properties/_expected.js | 4 ++-- .../recursive-destructuring/_config.js | 3 +++ .../recursive-destructuring/_expected.js | 1 + .../samples/recursive-destructuring/main.js | 1 + .../recursive-property-access/_config.js | 3 +++ .../recursive-property-access/_expected.js | 1 + .../samples/recursive-property-access/main.js | 1 + 17 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 src/ast/utils/limitPathLength.ts create mode 100644 test/form/samples/recursive-destructuring/_config.js create mode 100644 test/form/samples/recursive-destructuring/_expected.js create mode 100644 test/form/samples/recursive-destructuring/main.js create mode 100644 test/form/samples/recursive-property-access/_config.js create mode 100644 test/form/samples/recursive-property-access/_expected.js create mode 100644 test/form/samples/recursive-property-access/main.js diff --git a/browser/package.json b/browser/package.json index a77334b1b..b737e4ddb 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "4.27.0", + "version": "4.27.1-1", "description": "Next-generation ES module bundler browser build", "main": "dist/rollup.browser.js", "module": "dist/es/rollup.browser.js", diff --git a/package-lock.json b/package-lock.json index 8e7ce9683..e044b1e8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rollup", - "version": "4.27.0", + "version": "4.27.1-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rollup", - "version": "4.27.0", + "version": "4.27.1-1", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" diff --git a/package.json b/package.json index d3dea7fc0..39f725e07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "4.27.0", + "version": "4.27.1-1", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", diff --git a/src/ast/nodes/MemberExpression.ts b/src/ast/nodes/MemberExpression.ts index a65bd37b6..740c7ceae 100644 --- a/src/ast/nodes/MemberExpression.ts +++ b/src/ast/nodes/MemberExpression.ts @@ -19,6 +19,7 @@ import { INTERACTION_ASSIGNED, NODE_INTERACTION_UNKNOWN_ACCESS } from '../NodeInteractions'; +import { MAX_PATH_DEPTH } from '../utils/limitPathLength'; import { EMPTY_PATH, type EntityPathTracker, @@ -52,9 +53,6 @@ import { IS_SKIPPED_CHAIN, NodeBase } from './shared/Node'; import type { PatternNode } from './shared/Pattern'; import type Super from './Super'; -// To avoid infinite recursions -const MAX_PATH_DEPTH = 7; - function getResolvablePropertyKey(memberExpression: MemberExpression): string | null { return memberExpression.computed ? getResolvableComputedPropertyKey(memberExpression.property) @@ -208,11 +206,13 @@ 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]) ]); } } @@ -364,7 +364,12 @@ export default class MemberExpression if (!this.deoptimized) this.applyDeoptimizations(); this.includeProperties( path, - [this.getPropertyKey(), ...path], + [ + this.getPropertyKey(), + ...(path.length < MAX_PATH_DEPTH + ? path + : [...path.slice(0, MAX_PATH_DEPTH), UnknownKey as ObjectPathKey]) + ], context, includeChildrenRecursively ); diff --git a/src/ast/nodes/SpreadElement.ts b/src/ast/nodes/SpreadElement.ts index 9b0a0da3b..be6e8c72f 100644 --- a/src/ast/nodes/SpreadElement.ts +++ b/src/ast/nodes/SpreadElement.ts @@ -23,7 +23,7 @@ export default class SpreadElement extends NodeBase { if (path.length > 0) { this.argument.deoptimizeArgumentsOnInteractionAtPath( interaction, - [UnknownKey, ...path], + UNKNOWN_PATH, recursionTracker ); } diff --git a/src/ast/nodes/shared/ObjectEntity.ts b/src/ast/nodes/shared/ObjectEntity.ts index 0cdac6f13..090cae740 100644 --- a/src/ast/nodes/shared/ObjectEntity.ts +++ b/src/ast/nodes/shared/ObjectEntity.ts @@ -247,7 +247,7 @@ export class ObjectEntity extends ExpressionEntity { : this.allProperties) { property.deoptimizePath(subPath); } - this.prototypeExpression?.deoptimizePath(path.length === 1 ? [...path, UnknownKey] : path); + this.prototypeExpression?.deoptimizePath(path.length === 1 ? [path[0], UnknownKey] : path); } getLiteralValueAtPath( 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/LocalVariable.ts b/src/ast/variables/LocalVariable.ts index 4eee63f7b..97d262a4c 100644 --- a/src/ast/variables/LocalVariable.ts +++ b/src/ast/variables/LocalVariable.ts @@ -21,6 +21,7 @@ import { } from '../nodes/shared/Expression'; import type { Node } from '../nodes/shared/Node'; import type { VariableKind } from '../nodes/shared/VariableKinds'; +import { limitConcatenatedPathDepth, MAX_PATH_DEPTH } from '../utils/limitPathLength'; import { EMPTY_PATH, type EntityPathTracker, @@ -77,19 +78,20 @@ export default class LocalVariable extends Variable { path: ObjectPath, 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, [...this.initPath, ...path], recursionTracker - ), + ); + }, undefined ); } @@ -110,7 +112,7 @@ export default class LocalVariable extends Variable { } this.init.deoptimizePath([...this.initPath, UnknownKey]); } else { - this.init.deoptimizePath([...this.initPath, ...path]); + this.init.deoptimizePath(limitConcatenatedPathDepth(this.initPath, path)); } } @@ -119,7 +121,7 @@ export default class LocalVariable extends Variable { recursionTracker: EntityPathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { - if (this.isReassigned) { + if (this.isReassigned || path.length + this.initPath.length > MAX_PATH_DEPTH) { return UnknownValue; } return recursionTracker.withTrackedEntityAtPath( @@ -143,7 +145,7 @@ export default class LocalVariable extends Variable { 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( @@ -167,6 +169,9 @@ 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; @@ -213,7 +218,7 @@ export default class LocalVariable extends Variable { } // We need to make sure we include the correct path of the init if (path.length > 0) { - this.init.includePath([...this.initPath, ...path], context, false); + this.init.includePath(limitConcatenatedPathDepth(this.initPath, path), context, false); this.additionalInitializers?.forEach(initializer => initializer.includePath(UNKNOWN_PATH, context, false) ); diff --git a/src/ast/variables/ParameterVariable.ts b/src/ast/variables/ParameterVariable.ts index 174866b88..b9f354fcf 100644 --- a/src/ast/variables/ParameterVariable.ts +++ b/src/ast/variables/ParameterVariable.ts @@ -14,6 +14,7 @@ import { UNKNOWN_RETURN_EXPRESSION, UnknownValue } from '../nodes/shared/Expression'; +import { MAX_PATH_DEPTH } from '../utils/limitPathLength'; import type { ObjectPath, ObjectPathKey } from '../utils/PathTracker'; import { EntityPathTracker, @@ -73,6 +74,10 @@ export default class ParameterVariable extends LocalVariable { entity.deoptimizePath([...this.initPath, field]); } for (const { interaction, path } of this.deoptimizationInteractions) { + if (this.initPath.length + path.length > MAX_PATH_DEPTH) { + deoptimizeInteraction(interaction); + continue; + } entity.deoptimizeArgumentsOnInteractionAtPath( interaction, [...this.initPath, ...path], @@ -164,7 +169,7 @@ export default class ParameterVariable extends LocalVariable { 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(); @@ -183,7 +188,11 @@ export default class ParameterVariable extends LocalVariable { context: HasEffectsContext ): boolean { const { type } = interaction; - if (this.isReassigned || type === INTERACTION_ASSIGNED) { + if ( + this.isReassigned || + type === INTERACTION_ASSIGNED || + path.length + this.initPath.length > MAX_PATH_DEPTH + ) { return super.hasEffectsOnInteractionAtPath(path, interaction, context); } return ( diff --git a/test/form/samples/deep-properties-access/_expected.js b/test/form/samples/deep-properties-access/_expected.js index 1322aad74..c1d319eb8 100644 --- a/test/form/samples/deep-properties-access/_expected.js +++ b/test/form/samples/deep-properties-access/_expected.js @@ -1,2 +1,2 @@ -var obj = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}; +var obj = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}}}; console.log(obj.obj.obj.obj.obj.obj.obj.obj.obj.foo); diff --git a/test/form/samples/deep-properties/_expected.js b/test/form/samples/deep-properties/_expected.js index 82e96daae..ea78417e0 100644 --- a/test/form/samples/deep-properties/_expected.js +++ b/test/form/samples/deep-properties/_expected.js @@ -4,8 +4,8 @@ console.log(obj1.foo()); var obj2 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}}}; obj2.obj.obj.obj.obj.obj.obj.obj.foo(); -var obj3 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}; +var obj3 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}}}; if (obj3.obj.obj.obj.obj.obj.obj.obj.obj.foo) console.log('nested'); -var obj4 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}; +var obj4 = { obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {obj: {}}}}}}}}}}}; obj4.obj.obj.obj.obj.obj.obj.obj.foo = 'nested'; 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 From aaf38b725dd142b1da4190a91de8b04c006fead5 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Fri, 15 Nov 2024 16:25:45 +0100 Subject: [PATCH 07/12] 4.27.1 --- CHANGELOG.md | 12 ++++++++++++ browser/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04f2fe1b3..36b26ac8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # rollup changelog +## 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_ diff --git a/browser/package.json b/browser/package.json index b737e4ddb..24eb3529b 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "4.27.1-1", + "version": "4.27.1", "description": "Next-generation ES module bundler browser build", "main": "dist/rollup.browser.js", "module": "dist/es/rollup.browser.js", diff --git a/package-lock.json b/package-lock.json index e044b1e8d..10d4e0d5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rollup", - "version": "4.27.1-1", + "version": "4.27.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rollup", - "version": "4.27.1-1", + "version": "4.27.1", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" diff --git a/package.json b/package.json index 39f725e07..f3da81e2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "4.27.1-1", + "version": "4.27.1", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", From 6c6845551c5469fe9e49f2b2b8d6b0d821909c27 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Fri, 15 Nov 2024 17:37:19 +0100 Subject: [PATCH 08/12] Fix more variable deconflicting issues (#5728) * Ensure we also include declared variables if they are included for a key or default side effect * Always include full array patterns --- src/ast/nodes/ArrayPattern.ts | 17 +++++++++++++++-- src/ast/nodes/AssignmentPattern.ts | 6 ++++++ src/ast/nodes/Property.ts | 17 ++++++++++------- .../_config.js | 3 +++ .../dep.js | 2 ++ .../main.js | 6 ++++++ .../_config.js | 4 ++++ .../dep.js | 2 ++ .../main.js | 9 +++++++++ .../_config.js | 4 ++++ .../dep.js | 2 ++ .../main.js | 9 +++++++++ .../_config.js | 4 ++++ .../dep.js | 2 ++ .../main.js | 10 ++++++++++ 15 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/dep.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-array-pattern/main.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/dep.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects-array/main.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/dep.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-default-side-effects/main.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/_config.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/dep.js create mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-key-side-effects/main.js diff --git a/src/ast/nodes/ArrayPattern.ts b/src/ast/nodes/ArrayPattern.ts index 54dc76b5f..daf57eb35 100644 --- a/src/ast/nodes/ArrayPattern.ts +++ b/src/ast/nodes/ArrayPattern.ts @@ -87,8 +87,21 @@ export default class ArrayPattern extends NodeBase implements DeclarationPattern let included = false; const includedPatternPath = getIncludedPatternPath(destructuredInitPath); for (const element of this.elements) { - included = - element?.includeDestructuredIfNecessary(context, includedPatternPath, init) || included; + 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); } diff --git a/src/ast/nodes/AssignmentPattern.ts b/src/ast/nodes/AssignmentPattern.ts index fc32c7e5c..e071fe980 100644 --- a/src/ast/nodes/AssignmentPattern.ts +++ b/src/ast/nodes/AssignmentPattern.ts @@ -70,6 +70,12 @@ export default class AssignmentPattern extends NodeBase implements DeclarationPa 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); } diff --git a/src/ast/nodes/Property.ts b/src/ast/nodes/Property.ts index 96c2579d3..b71f96a9b 100644 --- a/src/ast/nodes/Property.ts +++ b/src/ast/nodes/Property.ts @@ -77,15 +77,18 @@ export default class Property extends MethodBase implements DeclarationPatternNo destructuredInitPath: ObjectPath, init: ExpressionEntity ): boolean { + const path = this.getPathInProperty(destructuredInitPath); let included = - (this.value as PatternNode).includeDestructuredIfNecessary( - context, - this.getPathInProperty(destructuredInitPath), - init - ) || this.included; - included ||= this.key.hasEffects(createHasEffectsContext()); - if (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); } 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 }); From a503a4dd9982bf20fd38aeb171882a27828906ae Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Fri, 15 Nov 2024 17:38:33 +0100 Subject: [PATCH 09/12] 4.27.2 --- CHANGELOG.md | 12 ++++++++++++ browser/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36b26ac8c..5d5b8c3c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # 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_ diff --git a/browser/package.json b/browser/package.json index 24eb3529b..501eaeefd 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "4.27.1", + "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/package-lock.json b/package-lock.json index 10d4e0d5e..057313976 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rollup", - "version": "4.27.1", + "version": "4.27.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rollup", - "version": "4.27.1", + "version": "4.27.2", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" diff --git a/package.json b/package.json index f3da81e2e..833f2cde6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "4.27.1", + "version": "4.27.2", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", From 10bc15096ffe3c155fe5e81f1e474f208adbc667 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Mon, 18 Nov 2024 16:52:52 +0100 Subject: [PATCH 10/12] Revert object tree-shaking (#5420) until some issues have been resolved (#5736) Revert "feat: implement object tree-shaking (#5420)" This reverts commit a9acb5730e9c5f02598088af311fb3a02686e471. --- LICENSE.md | 22 ++- scripts/ast-types.js | 6 +- scripts/generate-buffer-parsers.js | 1 - scripts/prepare-release.js | 6 +- src/Graph.ts | 4 +- src/Module.ts | 47 +++---- src/ast/ExecutionContext.ts | 10 +- src/ast/bufferParsers.ts | 12 +- src/ast/nodes/ArrayExpression.ts | 8 +- src/ast/nodes/ArrayPattern.ts | 76 ++--------- src/ast/nodes/ArrowFunctionExpression.ts | 16 +-- src/ast/nodes/AssignmentExpression.ts | 21 +-- src/ast/nodes/AssignmentPattern.ts | 48 +------ src/ast/nodes/AwaitExpression.ts | 9 +- src/ast/nodes/BinaryExpression.ts | 4 +- src/ast/nodes/BlockStatement.ts | 9 +- src/ast/nodes/BreakStatement.ts | 11 +- src/ast/nodes/CallExpression.ts | 34 ++--- src/ast/nodes/CatchClause.ts | 9 +- src/ast/nodes/ChainExpression.ts | 4 +- src/ast/nodes/ClassBody.ts | 13 +- src/ast/nodes/ConditionalExpression.ts | 34 ++--- src/ast/nodes/ContinueStatement.ts | 11 +- src/ast/nodes/DoWhileStatement.ts | 9 +- src/ast/nodes/ExportDefaultDeclaration.ts | 12 +- src/ast/nodes/ForInStatement.ts | 18 +-- src/ast/nodes/ForOfStatement.ts | 16 +-- src/ast/nodes/ForStatement.ts | 15 +- src/ast/nodes/Identifier.ts | 128 +++++------------- src/ast/nodes/IfStatement.ts | 27 ++-- src/ast/nodes/ImportExpression.ts | 34 ++--- src/ast/nodes/JSXOpeningFragment.ts | 9 +- src/ast/nodes/LabeledStatement.ts | 17 +-- src/ast/nodes/LogicalExpression.ts | 20 ++- src/ast/nodes/MemberExpression.ts | 124 ++++------------- src/ast/nodes/MetaProperty.ts | 2 +- src/ast/nodes/NewExpression.ts | 19 +-- src/ast/nodes/ObjectExpression.ts | 51 ++----- src/ast/nodes/ObjectPattern.ts | 78 ++--------- src/ast/nodes/Program.ts | 9 +- src/ast/nodes/Property.ts | 108 ++++----------- src/ast/nodes/PropertyDefinition.ts | 8 +- src/ast/nodes/RestElement.ts | 68 +--------- src/ast/nodes/ReturnStatement.ts | 9 +- src/ast/nodes/SequenceExpression.ts | 14 +- src/ast/nodes/SpreadElement.ts | 9 +- src/ast/nodes/StaticBlock.ts | 9 +- src/ast/nodes/Super.ts | 11 +- src/ast/nodes/SwitchCase.ts | 11 +- src/ast/nodes/SwitchStatement.ts | 11 +- src/ast/nodes/TaggedTemplateExpression.ts | 24 ++-- src/ast/nodes/TemplateElement.ts | 2 +- src/ast/nodes/ThisExpression.ts | 12 +- src/ast/nodes/ThrowStatement.ts | 9 +- src/ast/nodes/TryStatement.ts | 14 +- src/ast/nodes/UnaryExpression.ts | 4 +- src/ast/nodes/UnknownNode.ts | 5 +- src/ast/nodes/UpdateExpression.ts | 6 +- src/ast/nodes/VariableDeclaration.ts | 16 +-- src/ast/nodes/VariableDeclarator.ts | 32 ++--- src/ast/nodes/WhileStatement.ts | 9 +- src/ast/nodes/shared/BitFlags.ts | 3 +- src/ast/nodes/shared/CallExpressionBase.ts | 10 +- src/ast/nodes/shared/ClassNode.ts | 35 ++--- src/ast/nodes/shared/Expression.ts | 21 +-- src/ast/nodes/shared/FunctionBase.ts | 117 +++++++++++----- src/ast/nodes/shared/FunctionNode.ts | 42 +++--- src/ast/nodes/shared/IdentifierBase.ts | 22 +-- src/ast/nodes/shared/JSXElementBase.ts | 9 +- src/ast/nodes/shared/MethodBase.ts | 12 +- src/ast/nodes/shared/MultiExpression.ts | 4 +- src/ast/nodes/shared/Node.ts | 29 ++-- src/ast/nodes/shared/ObjectEntity.ts | 49 +------ src/ast/nodes/shared/ObjectMember.ts | 20 +-- src/ast/nodes/shared/Pattern.ts | 26 +--- src/ast/nodes/shared/chainElements.ts | 4 +- src/ast/nodes/shared/jsxHelpers.ts | 6 +- src/ast/nodes/shared/loops.ts | 3 +- src/ast/scopes/BlockScope.ts | 12 +- src/ast/scopes/CatchBodyScope.ts | 13 +- src/ast/scopes/ClassBodyScope.ts | 3 +- src/ast/scopes/FunctionBodyScope.ts | 11 +- src/ast/scopes/FunctionScope.ts | 23 ++-- src/ast/scopes/ModuleScope.ts | 7 +- src/ast/scopes/ParameterScope.ts | 61 ++++----- src/ast/scopes/ReturnValueScope.ts | 56 -------- src/ast/scopes/Scope.ts | 14 +- src/ast/scopes/TrackingScope.ts | 4 +- src/ast/utils/PathTracker.ts | 81 ++--------- src/ast/variables/ArgumentsVariable.ts | 9 +- src/ast/variables/ExportDefaultVariable.ts | 12 +- src/ast/variables/ExportShimVariable.ts | 6 +- src/ast/variables/ExternalVariable.ts | 7 +- src/ast/variables/GlobalVariable.ts | 6 +- src/ast/variables/LocalVariable.ts | 98 +++++--------- src/ast/variables/NamespaceVariable.ts | 10 +- src/ast/variables/ParameterVariable.ts | 108 +++++---------- .../variables/SyntheticNamedExportVariable.ts | 8 +- src/ast/variables/ThisVariable.ts | 4 +- src/ast/variables/Variable.ts | 6 +- .../dynamic-import-with-namespace/_config.js | 22 --- .../_expected/amd/generated-module1.js | 13 -- .../_expected/amd/generated-module10.js | 13 -- .../_expected/amd/generated-module2.js | 13 -- .../_expected/amd/generated-module3.js | 7 - .../_expected/amd/generated-module4.js | 13 -- .../_expected/amd/generated-module5.js | 13 -- .../_expected/amd/generated-module6.js | 7 - .../_expected/amd/generated-module7.js | 13 -- .../_expected/amd/generated-module8.js | 13 -- .../_expected/amd/generated-module9.js | 7 - .../_expected/amd/main.js | 87 ------------ .../_expected/cjs/generated-module1.js | 11 -- .../_expected/cjs/generated-module10.js | 11 -- .../_expected/cjs/generated-module2.js | 11 -- .../_expected/cjs/generated-module3.js | 5 - .../_expected/cjs/generated-module4.js | 11 -- .../_expected/cjs/generated-module5.js | 11 -- .../_expected/cjs/generated-module6.js | 5 - .../_expected/cjs/generated-module7.js | 11 -- .../_expected/cjs/generated-module8.js | 11 -- .../_expected/cjs/generated-module9.js | 5 - .../_expected/cjs/main.js | 85 ------------ .../_expected/es/generated-module1.js | 6 - .../_expected/es/generated-module10.js | 6 - .../_expected/es/generated-module2.js | 6 - .../_expected/es/generated-module3.js | 3 - .../_expected/es/generated-module4.js | 6 - .../_expected/es/generated-module5.js | 6 - .../_expected/es/generated-module6.js | 3 - .../_expected/es/generated-module7.js | 6 - .../_expected/es/generated-module8.js | 6 - .../_expected/es/generated-module9.js | 3 - .../_expected/es/main.js | 83 ------------ .../_expected/system/generated-module1.js | 13 -- .../_expected/system/generated-module10.js | 13 -- .../_expected/system/generated-module2.js | 13 -- .../_expected/system/generated-module3.js | 10 -- .../_expected/system/generated-module4.js | 13 -- .../_expected/system/generated-module5.js | 13 -- .../_expected/system/generated-module6.js | 10 -- .../_expected/system/generated-module7.js | 13 -- .../_expected/system/generated-module8.js | 13 -- .../_expected/system/generated-module9.js | 10 -- .../_expected/system/main.js | 92 ------------- .../dynamic-import-with-namespace/main.js | 83 ------------ .../dynamic-import-with-namespace/module.js | 4 - .../no-default-deoptimization/_expected.js | 2 +- .../computed-properties/_expected/amd.js | 19 +++ .../computed-properties/_expected/cjs.js | 17 +++ .../{_expected.js => _expected/es.js} | 0 .../computed-properties/_expected/iife.js | 22 +++ .../computed-properties/_expected/system.js | 21 +++ .../computed-properties/_expected/umd.js | 23 ++++ .../destructured-known-arguments/_config.js | 3 - .../destructured-known-arguments/_expected.js | 28 ---- .../destructured-known-arguments/main.js | 32 ----- .../_expected.js | 2 +- .../_config.js | 4 - .../_expected.js | 4 - .../ignore-property-read-side-effects/main.js | 8 -- .../_config.js | 3 - .../_expected.js | 7 - .../main.js | 9 -- .../remove-props-via-destructuring/_config.js | 3 - .../_expected.js | 9 -- .../remove-props-via-destructuring/main.js | 9 -- .../remove-unused-nested-props/_config.js | 3 - .../remove-unused-nested-props/_expected.js | 2 - .../remove-unused-nested-props/main.js | 2 - .../_config.js | 4 - .../_expected.js | 6 - .../main.js | 8 -- .../remove-unused-parameter-props/_config.js | 3 - .../_expected.js | 18 --- .../remove-unused-parameter-props/main.js | 22 --- .../remove-unused-props/_config.js | 3 - .../remove-unused-props/_expected.js | 5 - .../remove-unused-props/main.js | 5 - .../_expected.js | 10 +- .../optional-chaining-namespace/_expected.js | 2 +- .../recursive-destructuring/_config.js | 3 - .../recursive-destructuring/_expected.js | 1 - .../samples/recursive-destructuring/main.js | 1 - .../_expected.js | 7 +- .../_expected/amd.js | 4 +- .../_expected/cjs.js | 4 +- .../_expected/es.js | 4 +- .../_expected/iife.js | 4 +- .../_expected/system.js | 4 +- .../_expected/umd.js | 4 +- .../_expected.js | 2 +- .../_expected.js | 20 --- .../system-export-declarations/_expected.js | 2 +- .../_expected.js | 27 +--- .../main.js | 4 - .../deoptimize-nested-function-arg/_config.js | 3 - .../deoptimize-nested-function-arg/main.js | 8 -- .../samples/deoptimize-via-arguments/main.js | 4 +- .../_config.js | 3 - .../dep.js | 2 - .../main.js | 9 -- .../_config.js | 3 - .../main.js | 13 -- .../_config.js | 3 - .../main.js | 9 -- .../do-not-destructure-unused/_config.js | 3 - .../do-not-destructure-unused/main.js | 17 --- .../exports/_config.js | 13 -- .../exports/main.js | 4 - .../_config.js | 3 - .../main.js | 6 - .../_config.js | 3 - .../main.js | 6 - .../include-call-arguments-path/_config.js | 3 - .../include-call-arguments-path/main.js | 9 -- .../include-destructuring-rest/_config.js | 3 - .../include-destructuring-rest/main.js | 15 -- .../_config.js | 6 - .../dep.js | 1 - .../main.js | 4 - .../_config.js | 3 - .../main.js | 5 - .../include-this-unknown-function/_config.js | 8 -- .../include-this-unknown-function/main.js | 11 -- .../_config.js | 3 - .../main.js | 36 ----- .../_config.js | 3 - .../main.js | 34 ----- .../_config.js | 3 - .../main.js | 33 ----- .../object-side-effects/_config.js | 3 - .../object-side-effects/main.js | 22 --- .../_config.js | 3 - .../main.js | 14 -- .../_config.js | 3 - .../main.js | 13 -- .../_config.js | 3 - .../main.js | 13 -- .../track-destructured-setter/_config.js | 3 - .../track-destructured-setter/main.js | 8 -- .../_config.js | 6 - .../main.js | 6 - .../_config.js | 8 -- .../object-tree-shaking-for-parameter/main.js | 15 -- .../_config.js | 3 - .../main.js | 7 - .../_config.js | 3 - .../main.js | 3 - .../module.js | 2 - .../_config.js | 3 - .../main.js | 6 - .../_config.js | 3 - .../main.js | 2 - .../module.js | 1 - .../preserve-var-declaration/_config.js | 3 - .../samples/preserve-var-declaration/main.js | 5 - .../_config.js | 6 - .../recursive-calls-without-treeshake/main.js | 10 -- .../_config.js | 3 - .../main.js | 2 - 261 files changed, 869 insertions(+), 3211 deletions(-) delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_config.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module1.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module10.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module2.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module3.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module4.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module5.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module6.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module7.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module8.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module9.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/main.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module1.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module10.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module2.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module3.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module4.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module5.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module6.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module7.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module8.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module9.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/main.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module1.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module10.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module2.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module3.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module4.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module5.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module6.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module7.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module8.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module9.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/main.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module1.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module10.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module2.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module3.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module4.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module5.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module6.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module7.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module8.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module9.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/main.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/main.js delete mode 100644 test/chunking-form/samples/dynamic-import-with-namespace/module.js create mode 100644 test/form/samples/computed-properties/_expected/amd.js create mode 100644 test/form/samples/computed-properties/_expected/cjs.js rename test/form/samples/computed-properties/{_expected.js => _expected/es.js} (100%) create mode 100644 test/form/samples/computed-properties/_expected/iife.js create mode 100644 test/form/samples/computed-properties/_expected/system.js create mode 100644 test/form/samples/computed-properties/_expected/umd.js delete mode 100644 test/form/samples/destructured-known-arguments/_config.js delete mode 100644 test/form/samples/destructured-known-arguments/_expected.js delete mode 100644 test/form/samples/destructured-known-arguments/main.js delete mode 100644 test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_config.js delete mode 100644 test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_expected.js delete mode 100644 test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/main.js delete mode 100644 test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_config.js delete mode 100644 test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_expected.js delete mode 100644 test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/main.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_config.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_expected.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/main.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_config.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_expected.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-nested-props/main.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_config.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_expected.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/main.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_config.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_expected.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/main.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-props/_config.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-props/_expected.js delete mode 100644 test/form/samples/object-expression-treeshaking/remove-unused-props/main.js delete mode 100644 test/form/samples/recursive-destructuring/_config.js delete mode 100644 test/form/samples/recursive-destructuring/_expected.js delete mode 100644 test/form/samples/recursive-destructuring/main.js delete mode 100644 test/function/samples/deoptimize-nested-function-arg/_config.js delete mode 100644 test/function/samples/deoptimize-nested-function-arg/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/dep.js delete mode 100644 test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/do-not-destructure-unused/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/do-not-destructure-unused/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/exports/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/exports/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-call-arguments-path/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-call-arguments-path/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-destructuring-rest/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-destructuring-rest/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/dep.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-this-unknown-function/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-this-unknown-function/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/object-side-effects/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/object-side-effects/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/main.js delete mode 100644 test/function/samples/object-expression-treeshaking/track-destructured-setter/_config.js delete mode 100644 test/function/samples/object-expression-treeshaking/track-destructured-setter/main.js delete mode 100644 test/function/samples/object-tree-shaking-for-global-assignment/_config.js delete mode 100644 test/function/samples/object-tree-shaking-for-global-assignment/main.js delete mode 100644 test/function/samples/object-tree-shaking-for-parameter/_config.js delete mode 100644 test/function/samples/object-tree-shaking-for-parameter/main.js delete mode 100644 test/function/samples/object-tree-shaking-in-function-self-call/_config.js delete mode 100644 test/function/samples/object-tree-shaking-in-function-self-call/main.js delete mode 100644 test/function/samples/object-tree-shaking-with-destructed-export/_config.js delete mode 100644 test/function/samples/object-tree-shaking-with-destructed-export/main.js delete mode 100644 test/function/samples/object-tree-shaking-with-destructed-export/module.js delete mode 100644 test/function/samples/object-tree-shaking-with-duplicated-function-call/_config.js delete mode 100644 test/function/samples/object-tree-shaking-with-duplicated-function-call/main.js delete mode 100644 test/function/samples/preserve-exported-object-in-namespace/_config.js delete mode 100644 test/function/samples/preserve-exported-object-in-namespace/main.js delete mode 100644 test/function/samples/preserve-exported-object-in-namespace/module.js delete mode 100644 test/function/samples/preserve-var-declaration/_config.js delete mode 100644 test/function/samples/preserve-var-declaration/main.js delete mode 100644 test/function/samples/recursive-calls-without-treeshake/_config.js delete mode 100644 test/function/samples/recursive-calls-without-treeshake/main.js delete mode 100644 test/function/samples/wrap-empty-object-with-double-brackets/_config.js delete mode 100644 test/function/samples/wrap-empty-object-with-double-brackets/main.js diff --git a/LICENSE.md b/LICENSE.md index ce44fa22e..868fbf149 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -13,7 +13,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI # Licenses of bundled dependencies The published Rollup artifact additionally contains code with the following licenses: -MIT, ISC +MIT, ISC, 0BSD # Bundled dependencies: ## @jridgewell/sourcemap-codec @@ -632,6 +632,26 @@ Repository: micromatch/to-regex-range --------------------------------------- +## tslib +License: 0BSD +By: Microsoft Corp. +Repository: https://github.com/Microsoft/tslib.git + +> Copyright (c) Microsoft Corporation. +> +> Permission to use, copy, modify, and/or distribute this software for any +> purpose with or without fee is hereby granted. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +> AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +> LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +> OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +> PERFORMANCE OF THIS SOFTWARE. + +--------------------------------------- + ## yargs-parser License: ISC By: Ben Coe diff --git a/scripts/ast-types.js b/scripts/ast-types.js index 831cfc556..d69c45d9d 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', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => parameter.declare('parameter', 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', EMPTY_PATH, UNKNOWN_EXPRESSION)"] + param: ['parameter', "parameter?.declare('parameter', UNKNOWN_EXPRESSION)"] }, scopes: { body: 'scope.bodyScope' @@ -310,7 +310,7 @@ export const AST_NODES = { 'parameters', `scope.addParameterVariables( parameters.map( - parameter => parameter.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => parameter.declare('parameter', 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 051151e05..801079f5b 100644 --- a/scripts/generate-buffer-parsers.js +++ b/scripts/generate-buffer-parsers.js @@ -178,7 +178,6 @@ 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 720bcd68e..87eb2834c 100755 --- a/scripts/prepare-release.js +++ b/scripts/prepare-release.js @@ -192,8 +192,10 @@ function getDummyLogSection(headline, pr) { * @return {Promise} */ async function installDependenciesAndLint() { - await runWithEcho('npm', ['ci', '--ignore-scripts']); - await runWithEcho('npm', ['run', 'check-audit']); + await Promise.all([ + runWithEcho('npm', ['ci', '--ignore-scripts']), + runWithEcho('npm', ['run', 'check-audit']) + ]); await runWithEcho('npm', ['run', 'ci:lint']); } diff --git a/src/Graph.ts b/src/Graph.ts index e940c3639..9e9f22559 100644 --- a/src/Graph.ts +++ b/src/Graph.ts @@ -1,6 +1,6 @@ import flru from 'flru'; import GlobalScope from './ast/scopes/GlobalScope'; -import { EntityPathTracker } from './ast/utils/PathTracker'; +import { PathTracker } 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 EntityPathTracker(); + readonly deoptimizationTracker = new PathTracker(); entryModules: Module[] = []; readonly fileOperationQueue: Queue; readonly moduleLoader: ModuleLoader; diff --git a/src/Module.ts b/src/Module.ts index fbc96ae57..d8c7cddee 100644 --- a/src/Module.ts +++ b/src/Module.ts @@ -20,12 +20,7 @@ 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 { - EMPTY_PATH, - type EntityPathTracker, - type ObjectPath, - UNKNOWN_PATH -} from './ast/utils/PathTracker'; +import { type PathTracker, UNKNOWN_PATH } from './ast/utils/PathTracker'; import ExportDefaultVariable from './ast/variables/ExportDefaultVariable'; import ExportShimVariable from './ast/variables/ExportShimVariable'; import ExternalVariable from './ast/variables/ExternalVariable'; @@ -121,7 +116,7 @@ export interface AstContext { addImportMeta: (node: MetaProperty) => void; addImportSource: (importSource: string) => void; code: string; - deoptimizationTracker: EntityPathTracker; + deoptimizationTracker: PathTracker; error: (properties: RollupLog, pos: number) => never; fileName: string; getExports: () => string[]; @@ -133,7 +128,7 @@ export interface AstContext { importDescriptions: Map; includeAllExports: () => void; includeDynamicImport: (node: ImportExpression) => void; - includeVariableInModule: (variable: Variable, path: ObjectPath) => void; + includeVariableInModule: (variable: Variable) => void; log: (level: LogLevel, properties: RollupLog, pos: number) => void; magicString: MagicString; manualPureFunctions: PureFunctions; @@ -704,7 +699,7 @@ export default class Module { include(): void { const context = createInclusionContext(); - if (this.ast!.shouldBeIncluded(context)) this.ast!.includePath(EMPTY_PATH, context, false); + if (this.ast!.shouldBeIncluded(context)) this.ast!.include(context, false); } includeAllExports(includeNamespaceMembers: boolean): void { @@ -720,7 +715,9 @@ export default class Module { return error(logMissingEntryExport(exportName, this.id)); } variable.deoptimizePath(UNKNOWN_PATH); - this.includeVariable(variable, UNKNOWN_PATH); + if (!variable.included) { + this.includeVariable(variable); + } } } @@ -729,7 +726,7 @@ export default class Module { if (variable) { variable.deoptimizePath(UNKNOWN_PATH); if (!variable.included) { - this.includeVariable(variable, UNKNOWN_PATH); + this.includeVariable(variable); } if (variable instanceof ExternalVariable) { variable.module.reexported = true; @@ -743,7 +740,7 @@ export default class Module { } includeAllInBundle(): void { - this.ast!.includePath(UNKNOWN_PATH, createInclusionContext(), true); + this.ast!.include(createInclusionContext(), true); this.includeAllExports(false); } @@ -760,7 +757,7 @@ export default class Module { if (variable) { variable.deoptimizePath(UNKNOWN_PATH); if (!variable.included) { - this.includeVariable(variable, UNKNOWN_PATH); + this.includeVariable(variable); } } @@ -1339,12 +1336,12 @@ export default class Module { for (const module of [this, ...this.exportAllModules]) { if (module instanceof ExternalModule) { const [externalVariable] = module.getVariableForExportName('*'); - externalVariable.includePath(UNKNOWN_PATH, createInclusionContext()); + externalVariable.include(); this.includedImports.add(externalVariable); externalNamespaces.add(externalVariable); } else if (module.info.syntheticNamedExports) { const syntheticNamespace = module.getSyntheticNamespace(); - syntheticNamespace.includePath(UNKNOWN_PATH, createInclusionContext()); + syntheticNamespace.include(); this.includedImports.add(syntheticNamespace); syntheticNamespaces.add(syntheticNamespace); } @@ -1353,14 +1350,14 @@ export default class Module { } private includeDynamicImport(node: ImportExpression): void { - const resolution = this.dynamicImports.find( - dynamicImport => dynamicImport.node === node - )!.resolution; + const resolution = ( + this.dynamicImports.find(dynamicImport => dynamicImport.node === node) as { + resolution: string | Module | ExternalModule | undefined; + } + ).resolution; if (resolution instanceof Module) { - if (!resolution.includedDynamicImporters.includes(this)) { - resolution.includedDynamicImporters.push(this); - } + resolution.includedDynamicImporters.push(this); const importedNames = this.options.treeshake ? node.getDeterministicImportedNames() @@ -1374,13 +1371,14 @@ export default class Module { } } - private includeVariable(variable: Variable, path: ObjectPath): void { + private includeVariable(variable: Variable): 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) { @@ -1396,11 +1394,10 @@ export default class Module { } } } - variable.includePath(path, createInclusionContext()); } - private includeVariableInModule(variable: Variable, path: ObjectPath): void { - this.includeVariable(variable, path); + private includeVariableInModule(variable: Variable): void { + this.includeVariable(variable); 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 df7bb3637..3906fb7b9 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, EntityPathTracker } from './utils/PathTracker'; +import { DiscriminatedPathTracker, PathTracker } 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: EntityPathTracker; - assigned: EntityPathTracker; + accessed: PathTracker; + assigned: PathTracker; brokenFlow: boolean; called: DiscriminatedPathTracker; ignore: ExecutionContextIgnore; @@ -44,8 +44,8 @@ export function createInclusionContext(): InclusionContext { export function createHasEffectsContext(): HasEffectsContext { return { - accessed: new EntityPathTracker(), - assigned: new EntityPathTracker(), + accessed: new PathTracker(), + assigned: new PathTracker(), brokenFlow: false, called: new DiscriminatedPathTracker(), hasBreak: false, diff --git a/src/ast/bufferParsers.ts b/src/ast/bufferParsers.ts index 385610ed3..8a7b8128f 100644 --- a/src/ast/bufferParsers.ts +++ b/src/ast/bufferParsers.ts @@ -103,7 +103,6 @@ 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( @@ -336,8 +335,7 @@ 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', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => parameter.declare('parameter', UNKNOWN_EXPRESSION) as ParameterVariable[] ), parameters[parameters.length - 1] instanceof RestElement ); @@ -386,7 +384,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', EMPTY_PATH, UNKNOWN_EXPRESSION); + parameter?.declare('parameter', UNKNOWN_EXPRESSION); node.body = convertNode(node, scope.bodyScope, buffer[position + 1], buffer); }, function chainExpression(node: ChainExpression, position, buffer) { @@ -530,8 +528,7 @@ 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', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => parameter.declare('parameter', UNKNOWN_EXPRESSION) as ParameterVariable[] ), parameters[parameters.length - 1] instanceof RestElement ); @@ -549,8 +546,7 @@ 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', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => parameter.declare('parameter', 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 739cde53a..1d731e364 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: EntityPathTracker + recursionTracker: PathTracker ): void { this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -38,7 +38,7 @@ export default class ArrayExpression extends NodeBase { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, 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: EntityPathTracker, + recursionTracker: PathTracker, 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 daf57eb35..1cbb302b8 100644 --- a/src/ast/nodes/ArrayPattern.ts +++ b/src/ast/nodes/ArrayPattern.ts @@ -1,15 +1,15 @@ -import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import type { HasEffectsContext } from '../ExecutionContext'; import type { NodeInteractionAssigned } from '../NodeInteractions'; -import { EMPTY_PATH, type ObjectPath, UnknownInteger, UnknownKey } from '../utils/PathTracker'; +import { EMPTY_PATH, type ObjectPath } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; import type Variable from '../variables/Variable'; import type * as NodeType from './NodeType'; -import type { ExpressionEntity } from './shared/Expression'; +import { UNKNOWN_EXPRESSION } from './shared/Expression'; import { NodeBase } from './shared/Node'; -import type { DeclarationPatternNode, PatternNode } from './shared/Pattern'; +import type { PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class ArrayPattern extends NodeBase implements DeclarationPatternNode { +export default class ArrayPattern extends NodeBase implements PatternNode { declare elements: (PatternNode | null)[]; declare type: NodeType.tArrayPattern; @@ -22,30 +22,16 @@ export default class ArrayPattern extends NodeBase implements DeclarationPattern } } - declare( - kind: VariableKind, - destructuredInitPath: ObjectPath, - init: ExpressionEntity - ): LocalVariable[] { + declare(kind: VariableKind): LocalVariable[] { const variables: LocalVariable[] = []; - const includedPatternPath = getIncludedPatternPath(destructuredInitPath); for (const element of this.elements) { if (element !== null) { - variables.push( - ...(element as DeclarationPatternNode).declare(kind, includedPatternPath, init) - ); + variables.push(...element.declare(kind, UNKNOWN_EXPRESSION)); } } 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) { @@ -53,20 +39,6 @@ export default class ArrayPattern extends NodeBase implements DeclarationPattern } } - 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, @@ -79,41 +51,9 @@ export default class ArrayPattern extends NodeBase implements DeclarationPattern 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 as DeclarationPatternNode)?.markDeclarationReached(); + element?.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 649d10e4a..c99578c87 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, UNKNOWN_PATH } from '../utils/PathTracker'; +import { type ObjectPath } 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 { DeclarationPatternNode } from './shared/Pattern'; +import type { PatternNode } from './shared/Pattern'; export default class ArrowFunctionExpression extends FunctionBase { declare body: BlockStatement | ExpressionNode; - declare params: DeclarationPatternNode[]; + declare params: PatternNode[]; declare preventChildBlockScope: true; declare scope: ReturnValueScope; declare type: NodeType.tArrowFunctionExpression; @@ -75,15 +75,11 @@ export default class ArrowFunctionExpression extends FunctionBase { return isIIFE || super.onlyFunctionCallUsed(); } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { - super.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + super.include(context, includeChildrenRecursively); for (const parameter of this.params) { if (!(parameter instanceof Identifier)) { - parameter.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + parameter.include(context, includeChildrenRecursively); } } } diff --git a/src/ast/nodes/AssignmentExpression.ts b/src/ast/nodes/AssignmentExpression.ts index bce594178..aecdf927e 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: PatternNode; + declare left: ExpressionNode | PatternNode; declare operator: | '=' | '+=' @@ -55,9 +55,7 @@ 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 !== '=') || - this.left.hasEffectsWhenDestructuring?.(context, EMPTY_PATH, right) + right.hasEffects(context) || left.hasEffectsAsAssignmentTarget(context, operator !== '=') ); } @@ -69,25 +67,19 @@ export default class AssignmentExpression extends NodeBase { return this.right.hasEffectsOnInteractionAtPath(path, interaction, context); } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(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(hasEffectsContext, false) || - left.hasEffectsWhenDestructuring?.(hasEffectsContext, EMPTY_PATH, right) + left.hasEffectsAsAssignmentTarget(createHasEffectsContext(), false) ) { left.includeAsAssignmentTarget(context, includeChildrenRecursively, operator !== '='); } - right.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + right.include(context, includeChildrenRecursively); } initialise(): void { @@ -172,7 +164,8 @@ export default class AssignmentExpression extends NodeBase { protected applyDeoptimizations(): void { this.deoptimized = true; - this.left.deoptimizeAssignment(EMPTY_PATH, this.right); + this.left.deoptimizePath(EMPTY_PATH); + this.right.deoptimizePath(UNKNOWN_PATH); this.scope.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/AssignmentPattern.ts b/src/ast/nodes/AssignmentPattern.ts index e071fe980..a67646b90 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, InclusionContext } from '../ExecutionContext'; +import type { HasEffectsContext } 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 { DeclarationPatternNode, PatternNode } from './shared/Pattern'; +import type { PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class AssignmentPattern extends NodeBase implements DeclarationPatternNode { +export default class AssignmentPattern extends NodeBase implements PatternNode { declare left: PatternNode; declare right: ExpressionNode; declare type: NodeType.tAssignmentPattern; @@ -24,16 +24,8 @@ export default class AssignmentPattern extends NodeBase implements DeclarationPa this.left.addExportedVariables(variables, exportNamesByVariable); } - 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); + declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { + return this.left.declare(kind, init); } deoptimizePath(path: ObjectPath): void { @@ -52,36 +44,8 @@ export default class AssignmentPattern extends NodeBase implements DeclarationPa ); } - 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 as DeclarationPatternNode).markDeclarationReached(); + this.left.markDeclarationReached(); } render( diff --git a/src/ast/nodes/AwaitExpression.ts b/src/ast/nodes/AwaitExpression.ts index 55585f265..25f262c0e 100644 --- a/src/ast/nodes/AwaitExpression.ts +++ b/src/ast/nodes/AwaitExpression.ts @@ -1,5 +1,4 @@ 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'; @@ -14,11 +13,7 @@ export default class AwaitExpression extends NodeBase { return true; } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { if (!this.deoptimized) this.applyDeoptimizations(); if (!this.included) { this.included = true; @@ -31,6 +26,6 @@ export default class AwaitExpression extends NodeBase { this.scope.context.usesTopLevelAwait = true; } } - this.argument.includePath(path, context, includeChildrenRecursively); + this.argument.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/BinaryExpression.ts b/src/ast/nodes/BinaryExpression.ts index 8021d923b..7c969f3b6 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: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { if (path.length > 0) return UnknownValue; diff --git a/src/ast/nodes/BlockStatement.ts b/src/ast/nodes/BlockStatement.ts index ff6baa331..22c332f9b 100644 --- a/src/ast/nodes/BlockStatement.ts +++ b/src/ast/nodes/BlockStatement.ts @@ -3,7 +3,6 @@ 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'; @@ -50,18 +49,14 @@ export default class BlockStatement extends StatementBase { return false; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(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.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + node.include(context, includeChildrenRecursively); } } } diff --git a/src/ast/nodes/BreakStatement.ts b/src/ast/nodes/BreakStatement.ts index b3e783e84..836fb356c 100644 --- a/src/ast/nodes/BreakStatement.ts +++ b/src/ast/nodes/BreakStatement.ts @@ -1,9 +1,4 @@ -import { - createInclusionContext, - type HasEffectsContext, - type InclusionContext -} from '../ExecutionContext'; -import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; +import { type HasEffectsContext, type InclusionContext } from '../ExecutionContext'; import type Identifier from './Identifier'; import type * as NodeType from './NodeType'; import { StatementBase } from './shared/Node'; @@ -24,10 +19,10 @@ export default class BreakStatement extends StatementBase { return false; } - includePath(_: ObjectPath, context: InclusionContext): void { + include(context: InclusionContext): void { this.included = true; if (this.label) { - this.label.includePath(UNKNOWN_PATH, createInclusionContext()); + this.label.include(); 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 531a9c5d2..ae18f8758 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 { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; -import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker'; +import type { ObjectPath, PathTracker } from '../utils/PathTracker'; +import { EMPTY_PATH, SHARED_RECURSION_TRACKER } 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 type SpreadElement from './SpreadElement'; -import type Super from './Super'; +import { getChainElementLiteralValueAtPath } from './shared/chainElements'; export default class CallExpression extends CallExpressionBase @@ -67,7 +67,7 @@ export default class CallExpression getLiteralValueAtPathAsChainElement( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown | SkippedChain { return getChainElementLiteralValueAtPath(this, this.callee, path, recursionTracker, origin); @@ -111,14 +111,10 @@ export default class CallExpression ); } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { if (!this.deoptimized) this.applyDeoptimizations(); if (includeChildrenRecursively) { - super.includePath(path, context, includeChildrenRecursively); + super.include(context, includeChildrenRecursively); if ( includeChildrenRecursively === INCLUDE_PARAMETERS && this.callee instanceof Identifier && @@ -128,17 +124,9 @@ export default class CallExpression } } else { this.included = true; - // 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.include(context, false); } + this.callee.includeCallArguments(context, this.arguments); } initialise() { @@ -174,7 +162,7 @@ export default class CallExpression } protected getReturnExpression( - recursionTracker: EntityPathTracker = SHARED_RECURSION_TRACKER + recursionTracker: PathTracker = 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 be2a9d6c2..ae9743c2f 100644 --- a/src/ast/nodes/CatchClause.ts +++ b/src/ast/nodes/CatchClause.ts @@ -1,15 +1,14 @@ 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 { DeclarationPatternNode } from './shared/Pattern'; +import type { PatternNode } from './shared/Pattern'; export default class CatchClause extends NodeBase { declare body: BlockStatement; - declare param: DeclarationPatternNode | null; + declare param: PatternNode | null; declare preventChildBlockScope: true; declare scope: ParameterScope; declare type: NodeType.tCatchClause; @@ -25,8 +24,8 @@ export default class CatchClause extends NodeBase { this.param = new (this.scope.context.getNodeConstructor(param.type))( this, this.scope - ).parseNode(param) as unknown as DeclarationPatternNode; - this.param!.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION); + ).parseNode(param) as unknown as PatternNode; + this.param!.declare('parameter', 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 5bd7868bc..28b583332 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 { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { const literalValue = this.expression.getLiteralValueAtPathAsChainElement( diff --git a/src/ast/nodes/ClassBody.ts b/src/ast/nodes/ClassBody.ts index 53590a4d4..845f21b15 100644 --- a/src/ast/nodes/ClassBody.ts +++ b/src/ast/nodes/ClassBody.ts @@ -1,14 +1,13 @@ 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)[]; @@ -19,15 +18,11 @@ export default class ClassBody extends NodeBase { this.scope = new ClassBodyScope(parentScope, this.parent as ClassNode); } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; - this.scope.context.includeVariableInModule(this.scope.thisVariable, UNKNOWN_PATH); + this.scope.context.includeVariableInModule(this.scope.thisVariable); for (const definition of this.body) { - definition.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + definition.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/ConditionalExpression.ts b/src/ast/nodes/ConditionalExpression.ts index 51669e601..5645a23f9 100644 --- a/src/ast/nodes/ConditionalExpression.ts +++ b/src/ast/nodes/ConditionalExpression.ts @@ -9,9 +9,10 @@ import { import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions'; -import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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'; @@ -38,7 +39,7 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: EntityPathTracker + recursionTracker: PathTracker ): void { this.consequent.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); this.alternate.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); @@ -69,7 +70,7 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { const usedBranch = this.getUsedBranch(); @@ -81,7 +82,7 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { const usedBranch = this.getUsedBranch(); @@ -136,29 +137,28 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz return usedBranch.hasEffectsOnInteractionAtPath(path, interaction, context); } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; const usedBranch = this.getUsedBranch(); if (includeChildrenRecursively || this.test.shouldBeIncluded(context) || usedBranch === null) { - this.test.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); - this.consequent.includePath(path, context, includeChildrenRecursively); - this.alternate.includePath(path, context, includeChildrenRecursively); + this.test.include(context, includeChildrenRecursively); + this.consequent.include(context, includeChildrenRecursively); + this.alternate.include(context, includeChildrenRecursively); } else { - usedBranch.includePath(path, context, includeChildrenRecursively); + usedBranch.include(context, includeChildrenRecursively); } } - includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { + includeCallArguments( + context: InclusionContext, + parameters: readonly (ExpressionEntity | SpreadElement)[] + ): void { const usedBranch = this.getUsedBranch(); if (usedBranch) { - usedBranch.includeCallArguments(context, interaction); + usedBranch.includeCallArguments(context, parameters); } else { - this.consequent.includeCallArguments(context, interaction); - this.alternate.includeCallArguments(context, interaction); + this.consequent.includeCallArguments(context, parameters); + this.alternate.includeCallArguments(context, parameters); } } diff --git a/src/ast/nodes/ContinueStatement.ts b/src/ast/nodes/ContinueStatement.ts index 16c4f8c7a..9e137e1af 100644 --- a/src/ast/nodes/ContinueStatement.ts +++ b/src/ast/nodes/ContinueStatement.ts @@ -1,9 +1,4 @@ -import { - createInclusionContext, - type HasEffectsContext, - type InclusionContext -} from '../ExecutionContext'; -import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; +import { type HasEffectsContext, type InclusionContext } from '../ExecutionContext'; import type Identifier from './Identifier'; import type * as NodeType from './NodeType'; import { StatementBase } from './shared/Node'; @@ -24,10 +19,10 @@ export default class ContinueStatement extends StatementBase { return false; } - includePath(_: ObjectPath, context: InclusionContext): void { + include(context: InclusionContext): void { this.included = true; if (this.label) { - this.label.includePath(UNKNOWN_PATH, createInclusionContext()); + this.label.include(); 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 829aaae34..7b74b9ad7 100644 --- a/src/ast/nodes/DoWhileStatement.ts +++ b/src/ast/nodes/DoWhileStatement.ts @@ -1,5 +1,4 @@ import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; -import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { type ExpressionNode, @@ -19,13 +18,9 @@ export default class DoWhileStatement extends StatementBase { return hasLoopBodyEffects(context, this.body); } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; - this.test.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.test.include(context, includeChildrenRecursively); includeLoopBody(context, this.body, includeChildrenRecursively); } } diff --git a/src/ast/nodes/ExportDefaultDeclaration.ts b/src/ast/nodes/ExportDefaultDeclaration.ts index 98898083c..5cde06600 100644 --- a/src/ast/nodes/ExportDefaultDeclaration.ts +++ b/src/ast/nodes/ExportDefaultDeclaration.ts @@ -9,7 +9,6 @@ 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'; @@ -42,15 +41,10 @@ export default class ExportDefaultDeclaration extends NodeBase { private declare declarationName: string | undefined; - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { - this.included = true; - this.declaration.includePath(path, context, includeChildrenRecursively); + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + super.include(context, includeChildrenRecursively); if (includeChildrenRecursively) { - this.scope.context.includeVariableInModule(this.variable, path); + this.scope.context.includeVariableInModule(this.variable); } } diff --git a/src/ast/nodes/ForInStatement.ts b/src/ast/nodes/ForInStatement.ts index 0cdb33b66..c078d09e5 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 type { ObjectPath } from '../utils/PathTracker'; -import { EMPTY_PATH, UNKNOWN_PATH } from '../utils/PathTracker'; +import { EMPTY_PATH } from '../utils/PathTracker'; +import type MemberExpression from './MemberExpression'; 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 type VariableDeclaration from './VariableDeclaration'; +import { hasLoopBodyEffects, includeLoopBody } from './shared/loops'; export default class ForInStatement extends StatementBase { declare body: StatementNode; - declare left: VariableDeclaration | PatternNode; + declare left: VariableDeclaration | PatternNode | MemberExpression; declare right: ExpressionNode; declare type: NodeType.tForInStatement; @@ -34,16 +34,12 @@ export default class ForInStatement extends StatementBase { return hasLoopBodyEffects(context, body); } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(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.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + right.include(context, includeChildrenRecursively); includeLoopBody(context, body, includeChildrenRecursively); } diff --git a/src/ast/nodes/ForOfStatement.ts b/src/ast/nodes/ForOfStatement.ts index 17fc07b02..227075a69 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 type VariableDeclaration from './VariableDeclaration'; +import { includeLoopBody } from './shared/loops'; export default class ForOfStatement extends StatementBase { declare body: StatementNode; - declare left: VariableDeclaration | PatternNode; + declare left: VariableDeclaration | PatternNode | MemberExpression; declare right: ExpressionNode; declare type: NodeType.tForOfStatement; @@ -41,16 +41,12 @@ export default class ForOfStatement extends StatementBase { return true; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(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.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + right.include(context, includeChildrenRecursively); includeLoopBody(context, body, includeChildrenRecursively); } diff --git a/src/ast/nodes/ForStatement.ts b/src/ast/nodes/ForStatement.ts index 6f94edcc9..1fe86e73e 100644 --- a/src/ast/nodes/ForStatement.ts +++ b/src/ast/nodes/ForStatement.ts @@ -3,7 +3,6 @@ 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 { @@ -36,17 +35,11 @@ export default class ForStatement extends StatementBase { return hasLoopBodyEffects(context, this.body); } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; - this.init?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively, { - asSingleStatement: true - }); - this.test?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); - this.update?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.init?.include(context, includeChildrenRecursively, { asSingleStatement: true }); + this.test?.include(context, includeChildrenRecursively); + this.update?.include(context, includeChildrenRecursively); includeLoopBody(context, this.body, includeChildrenRecursively); } diff --git a/src/ast/nodes/Identifier.ts b/src/ast/nodes/Identifier.ts index 75a27641a..c513e800b 100644 --- a/src/ast/nodes/Identifier.ts +++ b/src/ast/nodes/Identifier.ts @@ -1,39 +1,23 @@ 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 { ObjectMember } from './shared/ObjectMember'; -import type { DeclarationPatternNode } from './shared/Pattern'; +import type { PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; export type IdentifierWithVariable = Identifier & { variable: Variable }; -export default class Identifier extends IdentifierBase implements DeclarationPatternNode { +export default class Identifier extends IdentifierBase implements PatternNode { 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 @@ -51,88 +35,42 @@ export default class Identifier extends IdentifierBase implements DeclarationPat } } - declare( - kind: VariableKind, - destructuredInitPath: ObjectPath, - init: ExpressionEntity - ): LocalVariable[] { + declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { let variable: LocalVariable; const { treeshake } = this.scope.context.options; - 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(); + 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; } - } - 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); + 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}.`); } - init.includePath(destructuredInitPath, context, false); - return true; } - return false; + return [(this.variable = variable)]; } markDeclarationReached(): void { diff --git a/src/ast/nodes/IfStatement.ts b/src/ast/nodes/IfStatement.ts index d3bd5d83c..07651843b 100644 --- a/src/ast/nodes/IfStatement.ts +++ b/src/ast/nodes/IfStatement.ts @@ -3,8 +3,7 @@ import type { RenderOptions } from '../../utils/renderHelpers'; import type { DeoptimizableEntity } from '../DeoptimizableEntity'; import { type HasEffectsContext, type InclusionContext } from '../ExecutionContext'; import TrackingScope from '../scopes/TrackingScope'; -import type { ObjectPath } from '../utils/PathTracker'; -import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker'; +import { EMPTY_PATH, SHARED_RECURSION_TRACKER } from '../utils/PathTracker'; import BlockStatement from './BlockStatement'; import type Identifier from './Identifier'; import * as NodeType from './NodeType'; @@ -51,11 +50,7 @@ export default class IfStatement extends StatementBase implements DeoptimizableE return testValue ? this.consequent.hasEffects(context) : !!this.alternate?.hasEffects(context); } - includePath( - _: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; if (includeChildrenRecursively) { this.includeRecursively(includeChildrenRecursively, context); @@ -140,13 +135,13 @@ export default class IfStatement extends StatementBase implements DeoptimizableE private includeKnownTest(context: InclusionContext, testValue: LiteralValueOrUnknown) { if (this.test.shouldBeIncluded(context)) { - this.test.includePath(UNKNOWN_PATH, context, false); + this.test.include(context, false); } if (testValue && this.consequent.shouldBeIncluded(context)) { - this.consequent.includePath(UNKNOWN_PATH, context, false, { asSingleStatement: true }); + this.consequent.include(context, false, { asSingleStatement: true }); } if (!testValue && this.alternate?.shouldBeIncluded(context)) { - this.alternate.includePath(UNKNOWN_PATH, context, false, { asSingleStatement: true }); + this.alternate.include(context, false, { asSingleStatement: true }); } } @@ -154,22 +149,22 @@ export default class IfStatement extends StatementBase implements DeoptimizableE includeChildrenRecursively: true | 'variables', context: InclusionContext ) { - this.test.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); - this.consequent.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); - this.alternate?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.test.include(context, includeChildrenRecursively); + this.consequent.include(context, includeChildrenRecursively); + this.alternate?.include(context, includeChildrenRecursively); } private includeUnknownTest(context: InclusionContext) { - this.test.includePath(UNKNOWN_PATH, context, false); + this.test.include(context, false); const { brokenFlow } = context; let consequentBrokenFlow = false; if (this.consequent.shouldBeIncluded(context)) { - this.consequent.includePath(UNKNOWN_PATH, context, false, { asSingleStatement: true }); + this.consequent.include(context, false, { asSingleStatement: true }); consequentBrokenFlow = context.brokenFlow; context.brokenFlow = brokenFlow; } if (this.alternate?.shouldBeIncluded(context)) { - this.alternate.includePath(UNKNOWN_PATH, context, false, { asSingleStatement: true }); + this.alternate.include(context, false, { asSingleStatement: true }); context.brokenFlow = context.brokenFlow && consequentBrokenFlow; } } diff --git a/src/ast/nodes/ImportExpression.ts b/src/ast/nodes/ImportExpression.ts index 480dbcac5..785d6b679 100644 --- a/src/ast/nodes/ImportExpression.ts +++ b/src/ast/nodes/ImportExpression.ts @@ -2,17 +2,16 @@ 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'; @@ -23,13 +22,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; @@ -43,8 +42,6 @@ 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; @@ -82,15 +79,12 @@ export default class ImportExpression extends NodeBase { return EMPTY_ARRAY; } - // Case 1: const { foo } / module = await import('bar') + // Case 1: const { foo } = await import('bar') if (parent2 instanceof VariableDeclarator) { const declaration = parent2.id; - if (declaration instanceof Identifier) { - return this.hasUnknownAccessedKey ? undefined : [...this.accessedPropKey]; - } - if (declaration instanceof ObjectPattern) { - return getDeterministicObjectDestructure(declaration); - } + return declaration instanceof ObjectPattern + ? getDeterministicObjectDestructure(declaration) + : undefined; } // Case 2: (await import('bar')).foo @@ -157,25 +151,13 @@ export default class ImportExpression extends NodeBase { return true; } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(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 ee127b388..6d50740c1 100644 --- a/src/ast/nodes/JSXOpeningFragment.ts +++ b/src/ast/nodes/JSXOpeningFragment.ts @@ -2,7 +2,6 @@ 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'; @@ -16,11 +15,7 @@ export default class JSXOpeningFragment extends NodeBase { private fragment: string | null = null; private fragmentVariable: Variable | null = null; - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ) { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren) { if (!this.included) { const jsx = this.scope.context.options.jsx as NormalizedJsxOptions; if (jsx.mode === 'automatic') { @@ -44,7 +39,7 @@ export default class JSXOpeningFragment extends NodeBase { } } } - super.includePath(path, context, includeChildrenRecursively); + super.include(context, includeChildrenRecursively); } render(code: MagicString, options: RenderOptions): void { diff --git a/src/ast/nodes/LabeledStatement.ts b/src/ast/nodes/LabeledStatement.ts index bbdb401d1..57162a566 100644 --- a/src/ast/nodes/LabeledStatement.ts +++ b/src/ast/nodes/LabeledStatement.ts @@ -4,12 +4,7 @@ import { findNonWhiteSpace, type RenderOptions } from '../../utils/renderHelpers'; -import { - createInclusionContext, - type HasEffectsContext, - type InclusionContext -} from '../ExecutionContext'; -import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; +import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type Identifier from './Identifier'; import type * as NodeType from './NodeType'; import { type IncludeChildren, StatementBase, type StatementNode } from './shared/Node'; @@ -37,17 +32,13 @@ export default class LabeledStatement extends StatementBase { return bodyHasEffects; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; const { brokenFlow, includedLabels } = context; context.includedLabels = new Set(); - this.body.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.body.include(context, includeChildrenRecursively); if (includeChildrenRecursively || context.includedLabels.has(this.label.name)) { - this.label.includePath(UNKNOWN_PATH, createInclusionContext()); + this.label.include(); context.includedLabels.delete(this.label.name); context.brokenFlow = brokenFlow; } diff --git a/src/ast/nodes/LogicalExpression.ts b/src/ast/nodes/LogicalExpression.ts index f958edb79..e5fa9d6f5 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: EntityPathTracker + recursionTracker: PathTracker ): 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: EntityPathTracker, + recursionTracker: PathTracker, 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: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { const usedBranch = this.getUsedBranch(); @@ -156,11 +156,7 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable return usedBranch.hasEffectsOnInteractionAtPath(path, interaction, context); } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; const usedBranch = this.getUsedBranch(); if ( @@ -168,10 +164,10 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable (usedBranch === this.right && this.left.shouldBeIncluded(context)) || !usedBranch ) { - this.left.includePath(path, context, includeChildrenRecursively); - this.right.includePath(path, context, includeChildrenRecursively); + this.left.include(context, includeChildrenRecursively); + this.right.include(context, includeChildrenRecursively); } else { - usedBranch.includePath(path, context, includeChildrenRecursively); + usedBranch.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/MemberExpression.ts b/src/ast/nodes/MemberExpression.ts index 740c7ceae..0fe75f3d1 100644 --- a/src/ast/nodes/MemberExpression.ts +++ b/src/ast/nodes/MemberExpression.ts @@ -7,24 +7,18 @@ 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, - NODE_INTERACTION_UNKNOWN_ACCESS -} from '../NodeInteractions'; -import { MAX_PATH_DEPTH } from '../utils/limitPathLength'; +import { INTERACTION_ACCESSED, INTERACTION_ASSIGNED } from '../NodeInteractions'; import { EMPTY_PATH, - type EntityPathTracker, type ObjectPath, type ObjectPathKey, + type PathTracker, SHARED_RECURSION_TRACKER, SymbolToStringTag, UNKNOWN_PATH, @@ -39,8 +33,9 @@ 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, @@ -50,8 +45,10 @@ import { } from './shared/Expression'; import type { ChainElement, ExpressionNode, IncludeChildren, SkippedChain } from './shared/Node'; import { IS_SKIPPED_CHAIN, NodeBase } from './shared/Node'; -import type { PatternNode } from './shared/Pattern'; -import type Super from './Super'; +import { getChainElementLiteralValueAtPath } from './shared/chainElements'; + +// To avoid infinite recursions +const MAX_PATH_DEPTH = 7; function getResolvablePropertyKey(memberExpression: MemberExpression): string | null { return memberExpression.computed @@ -98,7 +95,7 @@ function getStringFromPath(path: PathWithPositions): string { export default class MemberExpression extends NodeBase - implements DeoptimizableEntity, ChainElement, PatternNode + implements DeoptimizableEntity, ChainElement { declare object: ExpressionNode | Super; declare property: ExpressionNode | PrivateIdentifier; @@ -170,7 +167,7 @@ export default class MemberExpression deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: EntityPathTracker + recursionTracker: PathTracker ): void { if (this.variable) { this.variable.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); @@ -187,11 +184,6 @@ 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[]; @@ -206,20 +198,18 @@ export default class MemberExpression if (path.length === 0) this.disallowNamespaceReassignment(); if (this.variable) { this.variable.deoptimizePath(path); - } else if (!this.isUndefined) { + } else if (!this.isUndefined && path.length < MAX_PATH_DEPTH) { const propertyKey = this.getPropertyKey(); this.object.deoptimizePath([ propertyKey === UnknownKey ? UnknownNonAccessorKey : propertyKey, - ...(path.length < MAX_PATH_DEPTH - ? path - : [...path.slice(0, MAX_PATH_DEPTH), UnknownKey as ObjectPathKey]) + ...path ]); } } getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { if (this.variable) { @@ -241,7 +231,7 @@ export default class MemberExpression getLiteralValueAtPathAsChainElement( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown | SkippedChain { if (this.variable) { @@ -256,7 +246,7 @@ export default class MemberExpression getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { if (this.variable) { @@ -341,38 +331,9 @@ export default class MemberExpression return true; } - 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 { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { if (!this.deoptimized) this.applyDeoptimizations(); - this.includeProperties( - path, - [ - this.getPropertyKey(), - ...(path.length < MAX_PATH_DEPTH - ? path - : [...path.slice(0, MAX_PATH_DEPTH), UnknownKey as ObjectPathKey]) - ], - context, - includeChildrenRecursively - ); + this.includeProperties(context, includeChildrenRecursively); } includeAsAssignmentTarget( @@ -382,44 +343,21 @@ export default class MemberExpression ): void { if (!this.assignmentDeoptimized) this.applyAssignmentDeoptimization(); if (deoptimizeAccess) { - this.includePath([this.getPropertyKey()], context, includeChildrenRecursively); + this.include(context, includeChildrenRecursively); } else { - this.includeProperties( - EMPTY_PATH, - [this.getPropertyKey()], - context, - includeChildrenRecursively - ); + this.includeProperties(context, includeChildrenRecursively); } } - includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { + includeCallArguments( + context: InclusionContext, + parameters: readonly (ExpressionEntity | SpreadElement)[] + ): void { if (this.variable) { - this.variable.includeCallArguments(context, interaction); + this.variable.includeCallArguments(context, parameters); } else { - 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; + super.includeCallArguments(context, parameters); } - return false; } initialise(): void { @@ -511,7 +449,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, UNKNOWN_PATH); + this.scope.context.includeVariableInModule(this.variable); } this.scope.context.log( LOGLEVEL_WARN, @@ -552,21 +490,17 @@ 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, includedPath); + this.scope.context.includeVariableInModule(this.variable); } - } else if (includedPath.length > 0) { - this.variable?.includePath(includedPath, context); } - this.object.includePath(objectPath, context, includeChildrenRecursively); - this.property.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.object.include(context, includeChildrenRecursively); + this.property.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/MetaProperty.ts b/src/ast/nodes/MetaProperty.ts index fa6bf8cef..a16a85c07 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; } - includePath(): void { + include(): 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 69ae15445..cc36256d1 100644 --- a/src/ast/nodes/NewExpression.ts +++ b/src/ast/nodes/NewExpression.ts @@ -5,12 +5,7 @@ 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, - UNKNOWN_PATH -} from '../utils/PathTracker'; +import { EMPTY_PATH, type ObjectPath, SHARED_RECURSION_TRACKER } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import type { ExpressionNode, IncludeChildren } from './shared/Node'; import { NodeBase } from './shared/Node'; @@ -41,19 +36,15 @@ export default class NewExpression extends NodeBase { return path.length > 0 || type !== INTERACTION_ACCESSED; } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { if (!this.deoptimized) this.applyDeoptimizations(); if (includeChildrenRecursively) { - super.includePath(path, context, includeChildrenRecursively); + super.include(context, includeChildrenRecursively); } else { this.included = true; - this.callee.includePath(UNKNOWN_PATH, context, false); + this.callee.include(context, false); } - this.callee.includeCallArguments(context, this.interaction); + this.callee.includeCallArguments(context, this.arguments); } initialise(): void { diff --git a/src/ast/nodes/ObjectExpression.ts b/src/ast/nodes/ObjectExpression.ts index 4c9950b1a..f9c0bb4d4 100644 --- a/src/ast/nodes/ObjectExpression.ts +++ b/src/ast/nodes/ObjectExpression.ts @@ -1,40 +1,35 @@ 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, InclusionContext } from '../ExecutionContext'; +import type { HasEffectsContext } 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 type { ExpressionEntity, LiteralValueOrUnknown } from './shared/Expression'; -import type { IncludeChildren } from './shared/Node'; +import SpreadElement from './SpreadElement'; +import { type ExpressionEntity, type LiteralValueOrUnknown } from './shared/Expression'; 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: EntityPathTracker + recursionTracker: PathTracker ): void { this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -53,7 +48,7 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getObjectEntity().getLiteralValueAtPath(path, recursionTracker, origin); @@ -62,7 +57,7 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.getObjectEntity().getReturnExpressionWhenCalledAtPath( @@ -81,21 +76,12 @@ 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 @@ -103,26 +89,6 @@ 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() {} @@ -157,7 +123,6 @@ 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 dba5598e0..df8b9ef37 100644 --- a/src/ast/nodes/ObjectPattern.ts +++ b/src/ast/nodes/ObjectPattern.ts @@ -1,8 +1,4 @@ -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 { HasEffectsContext } from '../ExecutionContext'; import type { NodeInteractionAssigned } from '../NodeInteractions'; import { EMPTY_PATH, type ObjectPath } from '../utils/PathTracker'; import type LocalVariable from '../variables/LocalVariable'; @@ -12,10 +8,10 @@ import type Property from './Property'; import type RestElement from './RestElement'; import type { ExpressionEntity } from './shared/Expression'; import { NodeBase } from './shared/Node'; -import type { DeclarationPatternNode } from './shared/Pattern'; +import type { PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class ObjectPattern extends NodeBase implements DeclarationPatternNode { +export default class ObjectPattern extends NodeBase implements PatternNode { declare properties: readonly (Property | RestElement)[]; declare type: NodeType.tObjectPattern; @@ -25,31 +21,24 @@ export default class ObjectPattern extends NodeBase implements DeclarationPatter ): void { for (const property of this.properties) { if (property.type === NodeType.Property) { - property.value.addExportedVariables(variables, exportNamesByVariable); + (property.value as unknown as PatternNode).addExportedVariables( + variables, + exportNamesByVariable + ); } else { property.argument.addExportedVariables(variables, exportNamesByVariable); } } } - declare( - kind: VariableKind, - destructuredInitPath: ObjectPath, - init: ExpressionEntity - ): LocalVariable[] { + declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { const variables: LocalVariable[] = []; for (const property of this.properties) { - variables.push(...property.declare(kind, destructuredInitPath, init)); + variables.push(...property.declare(kind, 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) { @@ -71,58 +60,9 @@ export default class ObjectPattern extends NodeBase implements DeclarationPatter 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 0079db695..0441eb8b9 100644 --- a/src/ast/nodes/Program.ts +++ b/src/ast/nodes/Program.ts @@ -10,7 +10,6 @@ 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'; @@ -50,15 +49,11 @@ export default class Program extends NodeBase { return false; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; for (const node of this.body) { if (includeChildrenRecursively || node.shouldBeIncluded(context)) { - node.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + node.include(context, includeChildrenRecursively); } } } diff --git a/src/ast/nodes/Property.ts b/src/ast/nodes/Property.ts index b71f96a9b..10b5a32c0 100644 --- a/src/ast/nodes/Property.ts +++ b/src/ast/nodes/Property.ts @@ -1,24 +1,22 @@ import type MagicString from 'magic-string'; +import type { NormalizedTreeshakingOptions } from '../../rollup/types'; import type { RenderOptions } from '../../utils/renderHelpers'; -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 { HasEffectsContext } from '../ExecutionContext'; +import { 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 } from './shared/Expression'; +import { type ExpressionEntity, UNKNOWN_EXPRESSION } from './shared/Expression'; import MethodBase from './shared/MethodBase'; -import type { ExpressionNode, IncludeChildren } from './shared/Node'; -import type { DeclarationPatternNode, PatternNode } from './shared/Pattern'; +import type { ExpressionNode } from './shared/Node'; +import type { PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class Property extends MethodBase implements DeclarationPatternNode { +export default class Property extends MethodBase implements PatternNode { declare key: ExpressionNode; declare kind: 'init' | 'get' | 'set'; declare type: NodeType.tProperty; + private declarationInit: ExpressionEntity | null = null; //declare method: boolean; get method(): boolean { @@ -36,75 +34,25 @@ export default class Property extends MethodBase implements DeclarationPatternNo this.flags = setFlag(this.flags, Flag.shorthand, value); } - 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 - ); + declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { + this.declarationInit = init; + return (this.value as PatternNode).declare(kind, UNKNOWN_EXPRESSION); } hasEffects(context: HasEffectsContext): boolean { if (!this.deoptimized) this.applyDeoptimizations(); - 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 + 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) ); } - 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 DeclarationPatternNode).markDeclarationReached(); + (this.value as PatternNode).markDeclarationReached(); } render(code: MagicString, options: RenderOptions): void { @@ -114,17 +62,11 @@ export default class Property extends MethodBase implements DeclarationPatternNo this.value.render(code, options, { isShorthandProperty: this.shorthand }); } - 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)]; + protected applyDeoptimizations(): void { + this.deoptimized = true; + if (this.declarationInit !== null) { + this.declarationInit.deoptimizePath([UnknownKey, UnknownKey]); + this.scope.context.requestTreeshakingPass(); + } } } diff --git a/src/ast/nodes/PropertyDefinition.ts b/src/ast/nodes/PropertyDefinition.ts index a5a2d5e53..bb1e9903d 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 { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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: EntityPathTracker + recursionTracker: PathTracker ): void { this.value?.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); } @@ -43,7 +43,7 @@ export default class PropertyDefinition extends NodeBase { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.value @@ -54,7 +54,7 @@ export default class PropertyDefinition extends NodeBase { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.value diff --git a/src/ast/nodes/RestElement.ts b/src/ast/nodes/RestElement.ts index 02148edbe..85cbb2e9f 100644 --- a/src/ast/nodes/RestElement.ts +++ b/src/ast/nodes/RestElement.ts @@ -1,16 +1,15 @@ -import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; +import type { HasEffectsContext } 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 } from './shared/Expression'; -import type { IncludeChildren } from './shared/Node'; +import { type ExpressionEntity, UNKNOWN_EXPRESSION } from './shared/Expression'; import { NodeBase } from './shared/Node'; -import type { DeclarationPatternNode, PatternNode } from './shared/Pattern'; +import type { PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; -export default class RestElement extends NodeBase implements DeclarationPatternNode { +export default class RestElement extends NodeBase implements PatternNode { declare argument: PatternNode; declare type: NodeType.tRestElement; private declarationInit: ExpressionEntity | null = null; @@ -22,21 +21,9 @@ export default class RestElement extends NodeBase implements DeclarationPatternN this.argument.addExportedVariables(variables, exportNamesByVariable); } - declare( - kind: VariableKind, - destructuredInitPath: ObjectPath, - init: ExpressionEntity - ): LocalVariable[] { + declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[] { this.declarationInit = init; - return (this.argument as DeclarationPatternNode).declare( - kind, - getIncludedPatternPath(destructuredInitPath), - init - ); - } - - deoptimizeAssignment(destructuredInitPath: ObjectPath, init: ExpressionEntity): void { - this.argument.deoptimizeAssignment(getIncludedPatternPath(destructuredInitPath), init); + return this.argument.declare(kind, UNKNOWN_EXPRESSION); } deoptimizePath(path: ObjectPath): void { @@ -56,44 +43,8 @@ export default class RestElement extends NodeBase implements DeclarationPatternN ); } - 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 as DeclarationPatternNode).markDeclarationReached(); + this.argument.markDeclarationReached(); } protected applyDeoptimizations(): void { @@ -104,8 +55,3 @@ export default class RestElement extends NodeBase implements DeclarationPatternN } } } - -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 6f3d08a41..624a52165 100644 --- a/src/ast/nodes/ReturnStatement.ts +++ b/src/ast/nodes/ReturnStatement.ts @@ -1,7 +1,6 @@ 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'; @@ -16,13 +15,9 @@ export default class ReturnStatement extends StatementBase { return false; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; - this.argument?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.argument?.include(context, includeChildrenRecursively); context.brokenFlow = true; } diff --git a/src/ast/nodes/SequenceExpression.ts b/src/ast/nodes/SequenceExpression.ts index a753b227c..8ca3f731d 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 EntityPathTracker, type ObjectPath } from '../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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: EntityPathTracker + recursionTracker: PathTracker ): void { this.expressions[this.expressions.length - 1].deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -38,7 +38,7 @@ export default class SequenceExpression extends NodeBase { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.expressions[this.expressions.length - 1].getLiteralValueAtPath( @@ -67,11 +67,7 @@ export default class SequenceExpression extends NodeBase { ); } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; const lastExpression = this.expressions[this.expressions.length - 1]; for (const expression of this.expressions) { @@ -80,7 +76,7 @@ export default class SequenceExpression extends NodeBase { (expression === lastExpression && !(this.parent instanceof ExpressionStatement)) || expression.shouldBeIncluded(context) ) - expression.includePath(path, context, includeChildrenRecursively); + expression.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/SpreadElement.ts b/src/ast/nodes/SpreadElement.ts index be6e8c72f..e7605a0ce 100644 --- a/src/ast/nodes/SpreadElement.ts +++ b/src/ast/nodes/SpreadElement.ts @@ -2,12 +2,7 @@ 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 EntityPathTracker, - type ObjectPath, - UNKNOWN_PATH, - UnknownKey -} from '../utils/PathTracker'; +import { type ObjectPath, type PathTracker, UNKNOWN_PATH, UnknownKey } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { type ExpressionNode, NodeBase } from './shared/Node'; @@ -18,7 +13,7 @@ export default class SpreadElement extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: EntityPathTracker + recursionTracker: PathTracker ): void { if (path.length > 0) { this.argument.deoptimizeArgumentsOnInteractionAtPath( diff --git a/src/ast/nodes/StaticBlock.ts b/src/ast/nodes/StaticBlock.ts index ff0878977..d97dad164 100644 --- a/src/ast/nodes/StaticBlock.ts +++ b/src/ast/nodes/StaticBlock.ts @@ -7,7 +7,6 @@ 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'; @@ -26,15 +25,11 @@ export default class StaticBlock extends StatementBase { return false; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; for (const node of this.body) { if (includeChildrenRecursively || node.shouldBeIncluded(context)) - node.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + node.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/Super.ts b/src/ast/nodes/Super.ts index 470d4cac3..5db71aed4 100644 --- a/src/ast/nodes/Super.ts +++ b/src/ast/nodes/Super.ts @@ -1,6 +1,5 @@ -import type { InclusionContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; -import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; +import type { ObjectPath, PathTracker } from '../utils/PathTracker'; import type Variable from '../variables/Variable'; import type * as NodeType from './NodeType'; import { NodeBase } from './shared/Node'; @@ -16,7 +15,7 @@ export default class Super extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: EntityPathTracker + recursionTracker: PathTracker ) { this.variable.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); } @@ -25,12 +24,10 @@ export default class Super extends NodeBase { this.variable.deoptimizePath(path); } - includePath(path: ObjectPath, context: InclusionContext): void { + include(): void { if (!this.included) { this.included = true; - this.scope.context.includeVariableInModule(this.variable, path); - } else if (path.length > 0) { - this.variable.includePath(path, context); + this.scope.context.includeVariableInModule(this.variable); } } } diff --git a/src/ast/nodes/SwitchCase.ts b/src/ast/nodes/SwitchCase.ts index 3437460b7..fd78688e0 100644 --- a/src/ast/nodes/SwitchCase.ts +++ b/src/ast/nodes/SwitchCase.ts @@ -6,7 +6,6 @@ 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, @@ -30,16 +29,12 @@ export default class SwitchCase extends NodeBase { return false; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; - this.test?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.test?.include(context, includeChildrenRecursively); for (const node of this.consequent) { if (includeChildrenRecursively || node.shouldBeIncluded(context)) - node.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + node.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/SwitchStatement.ts b/src/ast/nodes/SwitchStatement.ts index 748d35115..f9ae63007 100644 --- a/src/ast/nodes/SwitchStatement.ts +++ b/src/ast/nodes/SwitchStatement.ts @@ -7,7 +7,6 @@ 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'; @@ -47,13 +46,9 @@ export default class SwitchStatement extends StatementBase { return false; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; - this.discriminant.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.discriminant.include(context, includeChildrenRecursively); const { brokenFlow, hasBreak } = context; context.hasBreak = false; let onlyHasBrokenFlow = true; @@ -71,7 +66,7 @@ export default class SwitchStatement extends StatementBase { isCaseIncluded = switchCase.hasEffects(hasEffectsContext); } if (isCaseIncluded) { - switchCase.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + switchCase.include(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 639284732..e8f01e6a6 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 { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; -import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker'; +import type { PathTracker } from '../utils/PathTracker'; +import { EMPTY_PATH, SHARED_RECURSION_TRACKER } 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,23 +44,19 @@ export default class TaggedTemplateExpression extends CallExpressionBase { ); } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { if (!this.deoptimized) this.applyDeoptimizations(); if (includeChildrenRecursively) { - super.includePath(path, context, includeChildrenRecursively); + super.include(context, includeChildrenRecursively); } else { this.included = true; - this.tag.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); - this.quasi.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.tag.include(context, includeChildrenRecursively); + this.quasi.include(context, includeChildrenRecursively); } - this.tag.includeCallArguments(context, this.interaction); + this.tag.includeCallArguments(context, this.args); const [returnExpression] = this.getReturnExpression(); if (!returnExpression.included) { - returnExpression.includePath(UNKNOWN_PATH, context, false); + returnExpression.include(context, false); } } @@ -93,7 +89,7 @@ export default class TaggedTemplateExpression extends CallExpressionBase { } protected getReturnExpression( - recursionTracker: EntityPathTracker = SHARED_RECURSION_TRACKER + recursionTracker: PathTracker = 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 1489655d4..69f54ff33 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; } - includePath(): void { + include(): void { this.included = true; } diff --git a/src/ast/nodes/ThisExpression.ts b/src/ast/nodes/ThisExpression.ts index 9f242fc08..eae0151c4 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, InclusionContext } from '../ExecutionContext'; +import type { HasEffectsContext } from '../ExecutionContext'; import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ACCESSED } from '../NodeInteractions'; import ModuleScope from '../scopes/ModuleScope'; -import type { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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: EntityPathTracker + recursionTracker: PathTracker ): void { this.variable.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); } @@ -42,12 +42,10 @@ export default class ThisExpression extends NodeBase { return this.variable.hasEffectsOnInteractionAtPath(path, interaction, context); } - includePath(path: ObjectPath, context: InclusionContext): void { + include(): void { if (!this.included) { this.included = true; - this.scope.context.includeVariableInModule(this.variable, path); - } else if (path.length > 0) { - this.variable.includePath(path, context); + this.scope.context.includeVariableInModule(this.variable); } } diff --git a/src/ast/nodes/ThrowStatement.ts b/src/ast/nodes/ThrowStatement.ts index 7b64de077..2d9956e82 100644 --- a/src/ast/nodes/ThrowStatement.ts +++ b/src/ast/nodes/ThrowStatement.ts @@ -1,7 +1,6 @@ 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'; @@ -13,13 +12,9 @@ export default class ThrowStatement extends StatementBase { return true; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; - this.argument.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.argument.include(context, includeChildrenRecursively); context.brokenFlow = true; } diff --git a/src/ast/nodes/TryStatement.ts b/src/ast/nodes/TryStatement.ts index 76fdfa4b6..851d2cdd0 100644 --- a/src/ast/nodes/TryStatement.ts +++ b/src/ast/nodes/TryStatement.ts @@ -1,6 +1,5 @@ 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'; @@ -23,11 +22,7 @@ export default class TryStatement extends StatementBase { ); } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { const tryCatchDeoptimization = ( this.scope.context.options.treeshake as NormalizedTreeshakingOptions )?.tryCatchDeoptimization; @@ -35,8 +30,7 @@ export default class TryStatement extends StatementBase { if (!this.directlyIncluded || !tryCatchDeoptimization) { this.included = true; this.directlyIncluded = true; - this.block.includePath( - UNKNOWN_PATH, + this.block.include( context, tryCatchDeoptimization ? INCLUDE_PARAMETERS : includeChildrenRecursively ); @@ -50,9 +44,9 @@ export default class TryStatement extends StatementBase { } } if (this.handler !== null) { - this.handler.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.handler.include(context, includeChildrenRecursively); context.brokenFlow = brokenFlow; } - this.finalizer?.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.finalizer?.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/UnaryExpression.ts b/src/ast/nodes/UnaryExpression.ts index b8f72c42d..aa8f325c3 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 EntityPathTracker, type ObjectPath } from '../utils/PathTracker'; +import { EMPTY_PATH, type ObjectPath, type PathTracker } 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: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { if (path.length > 0) return UnknownValue; diff --git a/src/ast/nodes/UnknownNode.ts b/src/ast/nodes/UnknownNode.ts index 112eb1269..60ebf1360 100644 --- a/src/ast/nodes/UnknownNode.ts +++ b/src/ast/nodes/UnknownNode.ts @@ -1,5 +1,4 @@ import type { InclusionContext } from '../ExecutionContext'; -import type { ObjectPath } from '../utils/PathTracker'; import { NodeBase } from './shared/Node'; export default class UnknownNode extends NodeBase { @@ -7,7 +6,7 @@ export default class UnknownNode extends NodeBase { return true; } - includePath(path: ObjectPath, context: InclusionContext): void { - super.includePath(path, context, true); + include(context: InclusionContext): void { + super.include(context, true); } } diff --git a/src/ast/nodes/UpdateExpression.ts b/src/ast/nodes/UpdateExpression.ts index 311efff60..860bdd927 100644 --- a/src/ast/nodes/UpdateExpression.ts +++ b/src/ast/nodes/UpdateExpression.ts @@ -31,11 +31,7 @@ export default class UpdateExpression extends NodeBase { return path.length > 1 || type !== INTERACTION_ACCESSED; } - includePath( - _: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ) { + include(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 89be6e470..dd787ab79 100644 --- a/src/ast/nodes/VariableDeclaration.ts +++ b/src/ast/nodes/VariableDeclaration.ts @@ -12,10 +12,8 @@ import { getSystemExportStatement, renderSystemExportExpression } from '../../utils/systemJsRendering'; -import { treeshakeNode } from '../../utils/treeshakeNode'; import type { InclusionContext } from '../ExecutionContext'; -import type { ObjectPath } from '../utils/PathTracker'; -import { EMPTY_PATH, UNKNOWN_PATH } from '../utils/PathTracker'; +import { EMPTY_PATH } from '../utils/PathTracker'; import type Variable from '../variables/Variable'; import ArrayPattern from './ArrayPattern'; import Identifier, { type IdentifierWithVariable } from './Identifier'; @@ -59,8 +57,7 @@ export default class VariableDeclaration extends NodeBase { return false; } - includePath( - _path: ObjectPath, + include( context: InclusionContext, includeChildrenRecursively: IncludeChildren, { asSingleStatement }: InclusionOptions = BLANK @@ -68,10 +65,10 @@ export default class VariableDeclaration extends NodeBase { this.included = true; for (const declarator of this.declarations) { if (includeChildrenRecursively || declarator.shouldBeIncluded(context)) - declarator.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + declarator.include(context, includeChildrenRecursively); const { id, init } = declarator; if (asSingleStatement) { - id.includePath(EMPTY_PATH, context, includeChildrenRecursively); + id.include(context, includeChildrenRecursively); } if ( init && @@ -79,7 +76,7 @@ export default class VariableDeclaration extends NodeBase { !init.included && (id instanceof ObjectPattern || id instanceof ArrayPattern) ) { - init.includePath(EMPTY_PATH, context, includeChildrenRecursively); + init.include(context, includeChildrenRecursively); } } } @@ -186,7 +183,8 @@ export default class VariableDeclaration extends NodeBase { ); for (const { node, start, separator, contentEnd, end } of separatedNodes) { if (!node.included) { - treeshakeNode(node, code, start, end); + code.remove(start, end); + node.removeAnnotations(code); continue; } node.render(code, options); diff --git a/src/ast/nodes/VariableDeclarator.ts b/src/ast/nodes/VariableDeclarator.ts index f48a97737..2a96aba44 100644 --- a/src/ast/nodes/VariableDeclarator.ts +++ b/src/ast/nodes/VariableDeclarator.ts @@ -1,5 +1,4 @@ import type MagicString from 'magic-string'; -import type { NormalizedTreeshakingOptions } from '../../rollup/types'; import { BLANK } from '../../utils/blank'; import { isReassignedExportsMember } from '../../utils/reassignedExportsMember'; import { @@ -8,24 +7,24 @@ import { type RenderOptions } from '../../utils/renderHelpers'; import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; -import { EMPTY_PATH, type ObjectPath } from '../utils/PathTracker'; +import 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 { DeclarationPatternNode } from './shared/Pattern'; +import type { PatternNode } from './shared/Pattern'; import type { VariableKind } from './shared/VariableKinds'; export default class VariableDeclarator extends NodeBase { - declare id: DeclarationPatternNode; + declare id: PatternNode; declare init: ExpressionNode | null; declare type: NodeType.tVariableDeclarator; declare isUsingDeclaration: boolean; declareDeclarator(kind: VariableKind, isUsingDeclaration: boolean): void { this.isUsingDeclaration = isUsingDeclaration; - this.id.declare(kind, EMPTY_PATH, this.init || UNDEFINED_EXPRESSION); + this.id.declare(kind, this.init || UNDEFINED_EXPRESSION); } deoptimizePath(path: ObjectPath): void { @@ -36,30 +35,17 @@ export default class VariableDeclarator extends NodeBase { if (!this.deoptimized) this.applyDeoptimizations(); const initEffect = this.init?.hasEffects(context); this.id.markDeclarationReached(); - 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)) - ); + return initEffect || this.id.hasEffects(context) || this.isUsingDeclaration; } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { const { deoptimized, id, init } = this; if (!deoptimized) this.applyDeoptimizations(); this.included = true; - init?.includePath(EMPTY_PATH, context, includeChildrenRecursively); + init?.include(context, includeChildrenRecursively); id.markDeclarationReached(); - if (includeChildrenRecursively) { - id.includePath(EMPTY_PATH, context, includeChildrenRecursively); - } else { - id.includeDestructuredIfNecessary(context, EMPTY_PATH, init || UNDEFINED_EXPRESSION); + if (includeChildrenRecursively || id.shouldBeIncluded(context)) { + id.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/WhileStatement.ts b/src/ast/nodes/WhileStatement.ts index 65f1a8506..a710564cd 100644 --- a/src/ast/nodes/WhileStatement.ts +++ b/src/ast/nodes/WhileStatement.ts @@ -1,5 +1,4 @@ import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; -import { type ObjectPath, UNKNOWN_PATH } from '../utils/PathTracker'; import type * as NodeType from './NodeType'; import { type ExpressionNode, @@ -19,13 +18,9 @@ export default class WhileStatement extends StatementBase { return hasLoopBodyEffects(context, this.body); } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { this.included = true; - this.test.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.test.include(context, includeChildrenRecursively); includeLoopBody(context, this.body, includeChildrenRecursively); } } diff --git a/src/ast/nodes/shared/BitFlags.ts b/src/ast/nodes/shared/BitFlags.ts index 04b51af39..0d668f841 100644 --- a/src/ast/nodes/shared/BitFlags.ts +++ b/src/ast/nodes/shared/BitFlags.ts @@ -22,8 +22,7 @@ export const enum Flag { tail = 1 << 20, prefix = 1 << 21, generator = 1 << 22, - expression = 1 << 23, - destructuringDeoptimized = 1 << 23 + expression = 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 8efca6957..04c045b5d 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 EntityPathTracker, type ObjectPath, UNKNOWN_PATH } from '../../utils/PathTracker'; +import { type ObjectPath, type PathTracker, 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: EntityPathTracker + recursionTracker: PathTracker ): 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: EntityPathTracker, + recursionTracker: PathTracker, 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: EntityPathTracker, + recursionTracker: PathTracker, 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?: EntityPathTracker + recursionTracker?: PathTracker ): [expression: ExpressionEntity, isPure: boolean]; } diff --git a/src/ast/nodes/shared/ClassNode.ts b/src/ast/nodes/shared/ClassNode.ts index 8439cc659..43ca97814 100644 --- a/src/ast/nodes/shared/ClassNode.ts +++ b/src/ast/nodes/shared/ClassNode.ts @@ -1,21 +1,17 @@ import type { DeoptimizableEntity } from '../../DeoptimizableEntity'; -import { - createInclusionContext, - type HasEffectsContext, - type InclusionContext -} from '../../ExecutionContext'; +import type { HasEffectsContext, 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'; @@ -43,7 +39,7 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: EntityPathTracker + recursionTracker: PathTracker ): void { this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -62,7 +58,7 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getObjectEntity().getLiteralValueAtPath(path, recursionTracker, origin); @@ -71,7 +67,7 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.getObjectEntity().getReturnExpressionWhenCalledAtPath( @@ -103,26 +99,21 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity { : this.getObjectEntity().hasEffectsOnInteractionAtPath(path, interaction, context); } - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { if (!this.deoptimized) this.applyDeoptimizations(); this.included = true; - 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); + this.superClass?.include(context, includeChildrenRecursively); + this.body.include(context, includeChildrenRecursively); + for (const decorator of this.decorators) decorator.include(context, includeChildrenRecursively); if (this.id) { this.id.markDeclarationReached(); - this.id.includePath(UNKNOWN_PATH, createInclusionContext()); + this.id.include(); } } initialise(): void { super.initialise(); - this.id?.declare('class', EMPTY_PATH, this); + this.id?.declare('class', this); for (const method of this.body.body) { if (method instanceof MethodDefinition && method.kind === 'constructor') { this.classConstructor = method; @@ -188,7 +179,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 af77ce213..146ae33a8 100644 --- a/src/ast/nodes/shared/Expression.ts +++ b/src/ast/nodes/shared/Expression.ts @@ -2,9 +2,10 @@ import type { DeoptimizableEntity } from '../../DeoptimizableEntity'; import type { WritableEntity } from '../../Entity'; import type { HasEffectsContext, InclusionContext } from '../../ExecutionContext'; import type { NodeInteraction, NodeInteractionCalled } from '../../NodeInteractions'; -import type { EntityPathTracker, ObjectPath, SymbolToStringTag } from '../../utils/PathTracker'; +import type { ObjectPath, PathTracker, 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'; @@ -38,7 +39,7 @@ export class ExpressionEntity implements WritableEntity { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, _path: ObjectPath, - _recursionTracker: EntityPathTracker + _recursionTracker: PathTracker ): void { deoptimizeInteraction(interaction); } @@ -52,7 +53,7 @@ export class ExpressionEntity implements WritableEntity { */ getLiteralValueAtPath( _path: ObjectPath, - _recursionTracker: EntityPathTracker, + _recursionTracker: PathTracker, _origin: DeoptimizableEntity ): LiteralValueOrUnknown { return UnknownValue; @@ -61,7 +62,7 @@ export class ExpressionEntity implements WritableEntity { getReturnExpressionWhenCalledAtPath( _path: ObjectPath, _interaction: NodeInteractionCalled, - _recursionTracker: EntityPathTracker, + _recursionTracker: PathTracker, _origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return UNKNOWN_RETURN_EXPRESSION; @@ -75,8 +76,7 @@ export class ExpressionEntity implements WritableEntity { return true; } - includePath( - _path: ObjectPath, + include( _context: InclusionContext, _includeChildrenRecursively: IncludeChildren, _options?: InclusionOptions @@ -84,9 +84,12 @@ export class ExpressionEntity implements WritableEntity { this.included = true; } - includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { - for (const argument of interaction.args) { - argument?.includePath(UNKNOWN_PATH, context, false); + includeCallArguments( + context: InclusionContext, + parameters: readonly (ExpressionEntity | SpreadElement)[] + ): void { + for (const argument of parameters) { + argument.include(context, false); } } diff --git a/src/ast/nodes/shared/FunctionBase.ts b/src/ast/nodes/shared/FunctionBase.ts index 5f363b6e6..e57af88d0 100644 --- a/src/ast/nodes/shared/FunctionBase.ts +++ b/src/ast/nodes/shared/FunctionBase.ts @@ -8,15 +8,17 @@ import { NODE_INTERACTION_UNKNOWN_CALL } from '../../NodeInteractions'; import type ReturnValueScope from '../../scopes/ReturnValueScope'; -import type { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; -import { EMPTY_PATH, UNKNOWN_PATH, UnknownKey } from '../../utils/PathTracker'; +import type { ObjectPath, PathTracker } from '../../utils/PathTracker'; +import { 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'; @@ -28,11 +30,13 @@ import { NodeBase } from './Node'; import type { ObjectEntity } from './ObjectEntity'; -import type { DeclarationPatternNode } from './Pattern'; +import type { PatternNode } from './Pattern'; + +type InteractionCalledArguments = NodeInteractionCalled['args']; export default abstract class FunctionBase extends NodeBase { declare body: BlockStatement | ExpressionNode; - declare params: DeclarationPatternNode[]; + declare params: PatternNode[]; declare preventChildBlockScope: true; declare scope: ReturnValueScope; @@ -60,13 +64,58 @@ 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: EntityPathTracker + recursionTracker: PathTracker ): void { - if (interaction.type === INTERACTION_CALLED && path.length === 0) { - this.scope.deoptimizeArgumentsOnCall(interaction); + 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); } else { this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath( interaction, @@ -82,13 +131,18 @@ 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); - this.scope.deoptimizeAllParameters(); + for (const parameterList of this.scope.parameters) { + for (const parameter of parameterList) { + parameter.deoptimizePath(UNKNOWN_PATH); + parameter.markReassigned(); + } + } } } getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getObjectEntity().getLiteralValueAtPath(path, recursionTracker, origin); @@ -97,7 +151,7 @@ export default abstract class FunctionBase extends NodeBase { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { if (path.length > 0) { @@ -153,20 +207,8 @@ export default abstract class FunctionBase extends NodeBase { 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; + for (const parameter of this.params) { + if (parameter.hasEffects(context)) return true; } return false; } @@ -186,25 +228,25 @@ export default abstract class FunctionBase extends NodeBase { } private parameterVariableValuesDeoptimized = false; - - includePath( - _path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { - if (!(this.parameterVariableValuesDeoptimized || this.onlyFunctionCallUsed())) { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + if (!this.parameterVariableValuesDeoptimized && !this.onlyFunctionCallUsed()) { this.parameterVariableValuesDeoptimized = true; - this.scope.reassignAllParameters(); + this.deoptimizeParameterVariableValues(); } if (!this.deoptimized) this.applyDeoptimizations(); this.included = true; const { brokenFlow } = context; context.brokenFlow = false; - this.body.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + this.body.include(context, includeChildrenRecursively); context.brokenFlow = brokenFlow; } - includeCallArguments = this.scope.includeCallArguments.bind(this.scope); + includeCallArguments( + context: InclusionContext, + parameters: readonly (ExpressionEntity | SpreadElement)[] + ): void { + this.scope.includeCallArguments(context, parameters); + } initialise(): void { super.initialise(); @@ -234,12 +276,11 @@ export default abstract class FunctionBase extends NodeBase { (parameter: GenericEsTreeNode) => new (context.getNodeConstructor(parameter.type))(this, scope).parseNode( parameter - ) as unknown as DeclarationPatternNode + ) as unknown as PatternNode )); scope.addParameterVariables( parameters.map( - parameter => - parameter.declare('parameter', EMPTY_PATH, UNKNOWN_EXPRESSION) as ParameterVariable[] + parameter => parameter.declare('parameter', UNKNOWN_EXPRESSION) as ParameterVariable[] ), parameters[parameters.length - 1] instanceof RestElement ); @@ -247,6 +288,8 @@ 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 74e77441b..7b3399615 100644 --- a/src/ast/nodes/shared/FunctionNode.ts +++ b/src/ast/nodes/shared/FunctionNode.ts @@ -1,31 +1,23 @@ -import { - createInclusionContext, - type HasEffectsContext, - type InclusionContext -} from '../../ExecutionContext'; +import { 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 { - EMPTY_PATH, - type EntityPathTracker, - type ObjectPath, - UNKNOWN_PATH -} from '../../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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 { DeclarationPatternNode } from './Pattern'; +import type { PatternNode } from './Pattern'; export default class FunctionNode extends FunctionBase { declare body: BlockStatement; declare id: IdentifierWithVariable | null; - declare params: DeclarationPatternNode[]; + declare params: PatternNode[]; declare preventChildBlockScope: true; declare scope: FunctionScope; protected objectEntity: ObjectEntity | null = null; @@ -36,18 +28,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.addArgumentValue(this.constructedEntity); + this.scope.thisVariable.addEntityToBeDeoptimized(this.constructedEntity); } deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: EntityPathTracker + recursionTracker: PathTracker ): 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.addArgumentValue(interaction.args[0]); + this.scope.thisVariable.addEntityToBeDeoptimized(interaction.args[0]); } } @@ -98,24 +90,24 @@ export default class FunctionNode extends FunctionBase { return false; } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ): void { - super.includePath(path, context, includeChildrenRecursively); - this.id?.includePath(UNKNOWN_PATH, createInclusionContext()); + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void { + super.include(context, includeChildrenRecursively); + this.id?.include(); const hasArguments = this.scope.argumentsVariable.included; for (const parameter of this.params) { if (!(parameter instanceof Identifier) || hasArguments) { - parameter.includePath(UNKNOWN_PATH, context, includeChildrenRecursively); + parameter.include(context, includeChildrenRecursively); } } } initialise(): void { super.initialise(); - this.id?.declare('function', EMPTY_PATH, this); + this.id?.declare('function', this); + } + + protected addArgumentToBeDeoptimized(argument: ExpressionEntity) { + this.scope.argumentsVariable.addArgumentToBeDeoptimized(argument); } protected getObjectEntity(): ObjectEntity { diff --git a/src/ast/nodes/shared/IdentifierBase.ts b/src/ast/nodes/shared/IdentifierBase.ts index 72241cc67..684d7f2de 100644 --- a/src/ast/nodes/shared/IdentifierBase.ts +++ b/src/ast/nodes/shared/IdentifierBase.ts @@ -11,11 +11,12 @@ import { INTERACTION_CALLED, NODE_INTERACTION_UNKNOWN_ACCESS } from '../../NodeInteractions'; -import type { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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'; @@ -44,7 +45,7 @@ export default class IdentifierBase extends NodeBase { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: EntityPathTracker + recursionTracker: PathTracker ): void { this.variable!.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker); } @@ -60,7 +61,7 @@ export default class IdentifierBase extends NodeBase { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { return this.getVariableRespectingTDZ()!.getLiteralValueAtPath(path, recursionTracker, origin); @@ -69,7 +70,7 @@ export default class IdentifierBase extends NodeBase { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { const [expression, isPure] = @@ -127,20 +128,21 @@ export default class IdentifierBase extends NodeBase { } } - includePath(path: ObjectPath, context: InclusionContext): void { + include(): void { if (!this.deoptimized) this.applyDeoptimizations(); if (!this.included) { this.included = true; if (this.variable !== null) { - this.scope.context.includeVariableInModule(this.variable, path); + this.scope.context.includeVariableInModule(this.variable); } - } else if (path.length > 0) { - this.variable?.includePath(path, context); } } - includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { - this.variable!.includeCallArguments(context, interaction); + includeCallArguments( + context: InclusionContext, + parameters: readonly (ExpressionEntity | SpreadElement)[] + ): void { + this.variable!.includeCallArguments(context, parameters); } isPossibleTDZ(): boolean { diff --git a/src/ast/nodes/shared/JSXElementBase.ts b/src/ast/nodes/shared/JSXElementBase.ts index 62474cc65..6f8b08e79 100644 --- a/src/ast/nodes/shared/JSXElementBase.ts +++ b/src/ast/nodes/shared/JSXElementBase.ts @@ -3,7 +3,6 @@ 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'; @@ -27,11 +26,7 @@ export default class JSXElementBase extends NodeBase { } } - includePath( - path: ObjectPath, - context: InclusionContext, - includeChildrenRecursively: IncludeChildren - ) { + include(context: InclusionContext, includeChildrenRecursively: IncludeChildren) { if (!this.included) { const { factory, importSource, mode } = this.jsxMode; if (factory) { @@ -44,7 +39,7 @@ export default class JSXElementBase extends NodeBase { ); } } - super.includePath(path, context, includeChildrenRecursively); + super.include(context, includeChildrenRecursively); } protected applyDeoptimizations() {} diff --git a/src/ast/nodes/shared/MethodBase.ts b/src/ast/nodes/shared/MethodBase.ts index 5ca69cfa0..b893dbc61 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 { DeclarationPatternNode } from './Pattern'; +import type { PatternNode } 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 & DeclarationPatternNode); + declare value: ExpressionNode | (ExpressionNode & PatternNode); 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: EntityPathTracker + recursionTracker: PathTracker ): 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: EntityPathTracker, + recursionTracker: PathTracker, 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: EntityPathTracker, + recursionTracker: PathTracker, 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 5b1279355..5981af20b 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 { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return [ diff --git a/src/ast/nodes/shared/Node.ts b/src/ast/nodes/shared/Node.ts index 6be0f33bf..7e6aee31b 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,19 +78,11 @@ export interface Node extends Entity { hasEffectsAsAssignmentTarget(context: HasEffectsContext, checkAccess: boolean): boolean; /** - * 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". + * 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. */ - includePath( - path: ObjectPath, + include( context: InclusionContext, includeChildrenRecursively: IncludeChildren, options?: InclusionOptions @@ -139,7 +131,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 8bda71fbc..676b7548c 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 { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; +import type { ObjectPath, PathTracker } from '../../utils/PathTracker'; import { ExpressionEntity, type LiteralValueOrUnknown } from './Expression'; export class ObjectMember extends ExpressionEntity { constructor( private readonly object: ExpressionEntity, - private readonly path: ObjectPath + private readonly key: string ) { super(); } @@ -15,35 +15,35 @@ export class ObjectMember extends ExpressionEntity { deoptimizeArgumentsOnInteractionAtPath( interaction: NodeInteraction, path: ObjectPath, - recursionTracker: EntityPathTracker + recursionTracker: PathTracker ): void { this.object.deoptimizeArgumentsOnInteractionAtPath( interaction, - [...this.path, ...path], + [this.key, ...path], recursionTracker ); } deoptimizePath(path: ObjectPath): void { - this.object.deoptimizePath([...this.path, ...path]); + this.object.deoptimizePath([this.key, ...path]); } getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { - return this.object.getLiteralValueAtPath([...this.path, ...path], recursionTracker, origin); + return this.object.getLiteralValueAtPath([this.key, ...path], recursionTracker, origin); } getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { return this.object.getReturnExpressionWhenCalledAtPath( - [...this.path, ...path], + [this.key, ...path], interaction, recursionTracker, origin @@ -55,6 +55,6 @@ export class ObjectMember extends ExpressionEntity { interaction: NodeInteraction, context: HasEffectsContext ): boolean { - return this.object.hasEffectsOnInteractionAtPath([...this.path, ...path], interaction, context); + return this.object.hasEffectsOnInteractionAtPath([this.key, ...path], interaction, context); } } diff --git a/src/ast/nodes/shared/Pattern.ts b/src/ast/nodes/shared/Pattern.ts index 4591bf46e..e53385d28 100644 --- a/src/ast/nodes/shared/Pattern.ts +++ b/src/ast/nodes/shared/Pattern.ts @@ -1,34 +1,10 @@ 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 { - // 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[]; - + declare(kind: VariableKind, init: ExpressionEntity): LocalVariable[]; markDeclarationReached(): void; } diff --git a/src/ast/nodes/shared/chainElements.ts b/src/ast/nodes/shared/chainElements.ts index 390e4029a..d7baa0a25 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 { EntityPathTracker, ObjectPath } from '../../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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: EntityPathTracker, + recursionTracker: PathTracker, 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 f90376e82..bdbdfe8ef 100644 --- a/src/ast/nodes/shared/jsxHelpers.ts +++ b/src/ast/nodes/shared/jsxHelpers.ts @@ -1,5 +1,3 @@ -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'; @@ -37,14 +35,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.includePath(UNKNOWN_PATH, createInclusionContext()); + globalVariable.include(); // This excludes this variable from renaming factoryVariable.globalName = baseName; } } else { factoryVariable = node.scope.findGlobal(baseName); } - node.scope.context.includeVariableInModule(factoryVariable, UNKNOWN_PATH); + node.scope.context.includeVariableInModule(factoryVariable); 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 0782260de..dcc2dc6b4 100644 --- a/src/ast/nodes/shared/loops.ts +++ b/src/ast/nodes/shared/loops.ts @@ -1,5 +1,4 @@ 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 { @@ -26,7 +25,7 @@ export function includeLoopBody( const { brokenFlow, hasBreak, hasContinue } = context; context.hasBreak = false; context.hasContinue = false; - body.includePath(UNKNOWN_PATH, context, includeChildrenRecursively, { asSingleStatement: true }); + body.include(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 278e4096c..77e9f5307 100644 --- a/src/ast/scopes/BlockScope.ts +++ b/src/ast/scopes/BlockScope.ts @@ -3,7 +3,6 @@ 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'; @@ -16,7 +15,6 @@ export default class BlockScope extends ChildScope { identifier: Identifier, context: AstContext, init: ExpressionEntity, - destructuredInitPath: ObjectPath, kind: VariableKind ): LocalVariable { if (kind === 'var') { @@ -33,13 +31,7 @@ export default class BlockScope extends ChildScope { } return context.error(logRedeclarationError(name), identifier.start); } - const declaredVariable = this.parent.addDeclaration( - identifier, - context, - init, - destructuredInitPath, - kind - ); + const declaredVariable = this.parent.addDeclaration(identifier, context, init, kind); // Necessary to make sure the init is deoptimized for conditional declarations. // We cannot call deoptimizePath here. declaredVariable.markInitializersForDeoptimization(); @@ -47,6 +39,6 @@ export default class BlockScope extends ChildScope { this.addHoistedVariable(name, declaredVariable); return declaredVariable; } - return super.addDeclaration(identifier, context, init, destructuredInitPath, kind); + return super.addDeclaration(identifier, context, init, kind); } } diff --git a/src/ast/scopes/CatchBodyScope.ts b/src/ast/scopes/CatchBodyScope.ts index 46cc32b68..40dad8cae 100644 --- a/src/ast/scopes/CatchBodyScope.ts +++ b/src/ast/scopes/CatchBodyScope.ts @@ -4,7 +4,6 @@ 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'; @@ -19,7 +18,6 @@ export default class CatchBodyScope extends ChildScope { identifier: Identifier, context: AstContext, init: ExpressionEntity, - destructuredInitPath: ObjectPath, kind: VariableKind ): LocalVariable { if (kind === 'var') { @@ -41,7 +39,6 @@ export default class CatchBodyScope extends ChildScope { identifier, context, UNDEFINED_EXPRESSION, - destructuredInitPath, kind ); // To avoid the need to rewrite the declaration, we link the variable @@ -62,13 +59,7 @@ 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, - destructuredInitPath, - kind - ); + const declaredVariable = this.parent.parent.addDeclaration(identifier, context, init, kind); // Necessary to make sure the init is deoptimized for conditional declarations. // We cannot call deoptimizePath here. declaredVariable.markInitializersForDeoptimization(); @@ -76,6 +67,6 @@ export default class CatchBodyScope extends ChildScope { this.addHoistedVariable(name, declaredVariable); return declaredVariable; } - return super.addDeclaration(identifier, context, init, destructuredInitPath, kind); + return super.addDeclaration(identifier, context, init, kind); } } diff --git a/src/ast/scopes/ClassBodyScope.ts b/src/ast/scopes/ClassBodyScope.ts index a9c086444..c8f7f1f62 100644 --- a/src/ast/scopes/ClassBodyScope.ts +++ b/src/ast/scopes/ClassBodyScope.ts @@ -1,5 +1,4 @@ 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'; @@ -13,7 +12,7 @@ export default class ClassBodyScope extends ChildScope { super(parent, context); this.variables.set( 'this', - (this.thisVariable = new LocalVariable('this', null, classNode, EMPTY_PATH, context, 'other')) + (this.thisVariable = new LocalVariable('this', null, classNode, 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 6edab0c0b..7343ac033 100644 --- a/src/ast/scopes/FunctionBodyScope.ts +++ b/src/ast/scopes/FunctionBodyScope.ts @@ -3,7 +3,6 @@ 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'; @@ -19,7 +18,6 @@ export default class FunctionBodyScope extends ChildScope { identifier: Identifier, context: AstContext, init: ExpressionEntity, - destructuredInitPath: ObjectPath, kind: VariableKind ): LocalVariable { const name = identifier.name; @@ -36,14 +34,7 @@ export default class FunctionBodyScope extends ChildScope { } context.error(logRedeclarationError(name), identifier.start); } - const newVariable = new LocalVariable( - identifier.name, - identifier, - init, - destructuredInitPath, - context, - kind - ); + const newVariable = new LocalVariable(identifier.name, identifier, init, context, kind); this.variables.set(name, newVariable); return newVariable; } diff --git a/src/ast/scopes/FunctionScope.ts b/src/ast/scopes/FunctionScope.ts index b6bbd6cef..ed1d91863 100644 --- a/src/ast/scopes/FunctionScope.ts +++ b/src/ast/scopes/FunctionScope.ts @@ -1,7 +1,6 @@ import type { InclusionContext } from '../ExecutionContext'; -import type { NodeInteractionCalled } from '../NodeInteractions'; +import type SpreadElement from '../nodes/SpreadElement'; 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'; @@ -12,8 +11,8 @@ export default class FunctionScope extends ReturnValueScope { readonly thisVariable: ThisVariable; constructor(parent: ChildScope) { - super(parent, false); const { context } = parent; + super(parent, false); this.variables.set('arguments', (this.argumentsVariable = new ArgumentsVariable(context))); this.variables.set('this', (this.thisVariable = new ThisVariable(context))); } @@ -22,17 +21,17 @@ export default class FunctionScope extends ReturnValueScope { return this; } - includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { - super.includeCallArguments(context, interaction); + includeCallArguments( + context: InclusionContext, + parameters: readonly (ExpressionEntity | SpreadElement)[] + ): void { + super.includeCallArguments(context, parameters); if (this.argumentsVariable.included) { - const { args } = interaction; - for (let argumentIndex = 1; argumentIndex < args.length; argumentIndex++) { - args[argumentIndex]?.includePath(UNKNOWN_PATH, context, false); + for (const argument of parameters) { + if (!argument.included) { + argument.include(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 af7ba7235..2902fe666 100644 --- a/src/ast/scopes/ModuleScope.ts +++ b/src/ast/scopes/ModuleScope.ts @@ -5,8 +5,6 @@ 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'; @@ -22,7 +20,7 @@ export default class ModuleScope extends ChildScope { super(parent, context); this.variables.set( 'this', - new LocalVariable('this', null, UNDEFINED_EXPRESSION, EMPTY_PATH, context, 'other') + new LocalVariable('this', null, UNDEFINED_EXPRESSION, context, 'other') ); } @@ -30,13 +28,12 @@ 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, destructuredInitPath, kind); + return super.addDeclaration(identifier, context, init, kind); } addExportDefaultDeclaration( diff --git a/src/ast/scopes/ParameterScope.ts b/src/ast/scopes/ParameterScope.ts index de5ccdd74..8cc03c02c 100644 --- a/src/ast/scopes/ParameterScope.ts +++ b/src/ast/scopes/ParameterScope.ts @@ -1,10 +1,8 @@ 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 { ObjectPath } from '../utils/PathTracker'; -import { EMPTY_PATH, UNKNOWN_PATH } from '../utils/PathTracker'; +import type { ExpressionEntity } from '../nodes/shared/Expression'; import ParameterVariable from '../variables/ParameterVariable'; import CatchBodyScope from './CatchBodyScope'; import ChildScope from './ChildScope'; @@ -12,9 +10,9 @@ import FunctionBodyScope from './FunctionBodyScope'; export default class ParameterScope extends ChildScope { readonly bodyScope: ChildScope; + parameters: readonly ParameterVariable[][] = []; - protected hasRest = false; - protected parameters: readonly ParameterVariable[][] = []; + private hasRest = false; constructor(parent: ChildScope, isCatchScope: boolean) { super(parent, parent.context); @@ -25,13 +23,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, argumentPath: ObjectPath): ParameterVariable { + addParameterDeclaration(identifier: Identifier): 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, argumentPath, this.context); + const variable = new ParameterVariable(name, identifier, 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 @@ -51,52 +49,45 @@ export default class ParameterScope extends ChildScope { this.hasRest = hasRest; } - includeCallArguments(context: InclusionContext, interaction: NodeInteractionCalled): void { + includeCallArguments( + context: InclusionContext, + parameters: readonly (ExpressionEntity | SpreadElement)[] + ): void { let calledFromTryStatement = false; let argumentIncluded = false; const restParameter = this.hasRest && this.parameters[this.parameters.length - 1]; - 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 (const checkedArgument of parameters) { + if (checkedArgument instanceof SpreadElement) { + for (const argument of parameters) { + argument.include(context, false); + } + break; } } - // 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]!; + for (let index = parameters.length - 1; index >= 0; index--) { + const parameterVariables = this.parameters[index] || restParameter; + const argument = parameters[index]; if (parameterVariables) { calledFromTryStatement = false; if (parameterVariables.length === 0) { - // handle empty destructuring to avoid destructuring undefined + // handle empty destructuring argumentIncluded = true; } else { for (const variable of parameterVariables) { - if (variable.calledFromTryStatement) { - calledFromTryStatement = true; - } if (variable.included) { argumentIncluded = true; - if (calledFromTryStatement) { - argument.includePath(UNKNOWN_PATH, context, true); - } else { - variable.includeArgumentPaths(argument, context); - } + } + if (variable.calledFromTryStatement) { + calledFromTryStatement = true; } } } } - if (!argument.included && (argumentIncluded || argument.shouldBeIncluded(context))) { + if (!argumentIncluded && argument.shouldBeIncluded(context)) { argumentIncluded = true; - argument.includePath(EMPTY_PATH, context, calledFromTryStatement); + } + if (argumentIncluded) { + argument.include(context, calledFromTryStatement); } } } diff --git a/src/ast/scopes/ReturnValueScope.ts b/src/ast/scopes/ReturnValueScope.ts index 0aedb66e8..be4a522f3 100644 --- a/src/ast/scopes/ReturnValueScope.ts +++ b/src/ast/scopes/ReturnValueScope.ts @@ -1,8 +1,5 @@ -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 { @@ -13,64 +10,11 @@ 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 3be6b6429..eaeea352d 100644 --- a/src/ast/scopes/Scope.ts +++ b/src/ast/scopes/Scope.ts @@ -3,7 +3,6 @@ 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'; @@ -29,27 +28,20 @@ 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) { - if (kind === 'var' && existingVariable.kind === 'var') { + const existingKind = existingVariable.kind; + if (kind === 'var' && existingKind === 'var') { existingVariable.addDeclaration(identifier, init); return existingVariable; } context.error(logRedeclarationError(name), identifier.start); } - const newVariable = new LocalVariable( - identifier.name, - identifier, - init, - destructuredInitPath, - context, - kind - ); + const newVariable = new LocalVariable(identifier.name, identifier, init, context, kind); this.variables.set(name, newVariable); return newVariable; } diff --git a/src/ast/scopes/TrackingScope.ts b/src/ast/scopes/TrackingScope.ts index 616fcafaf..7449c5e0f 100644 --- a/src/ast/scopes/TrackingScope.ts +++ b/src/ast/scopes/TrackingScope.ts @@ -2,7 +2,6 @@ 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'; @@ -13,10 +12,9 @@ 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, destructuredInitPath, kind); + return super.addDeclaration(identifier, context, init, kind); } } diff --git a/src/ast/utils/PathTracker.ts b/src/ast/utils/PathTracker.ts index 3b70c4add..406ffd661 100644 --- a/src/ast/utils/PathTracker.ts +++ b/src/ast/utils/PathTracker.ts @@ -1,8 +1,5 @@ -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'); @@ -37,7 +34,7 @@ interface EntityPaths { [UnknownNonAccessorKey]?: EntityPaths; } -export class EntityPathTracker { +export class PathTracker { private entityPaths: EntityPaths = Object.create(null, { [EntitiesKey]: { value: new Set() } }); @@ -66,15 +63,15 @@ export class EntityPathTracker { private getEntities(path: ObjectPath): Set { let currentPaths = this.entityPaths; for (const pathSegment of path) { - currentPaths = currentPaths[pathSegment] ||= Object.create(null, { - [EntitiesKey]: { value: new Set() } - }); + currentPaths = currentPaths[pathSegment] = + currentPaths[pathSegment] || + Object.create(null, { [EntitiesKey]: { value: new Set() } }); } return currentPaths[EntitiesKey]; } } -export const SHARED_RECURSION_TRACKER = new EntityPathTracker(); +export const SHARED_RECURSION_TRACKER = new PathTracker(); interface DiscriminatedEntityPaths { [pathSegment: string]: DiscriminatedEntityPaths; @@ -97,9 +94,9 @@ export class DiscriminatedPathTracker { ): boolean { let currentPaths = this.entityPaths; for (const pathSegment of path) { - currentPaths = currentPaths[pathSegment] ||= Object.create(null, { - [EntitiesKey]: { value: new Map>() } - }); + currentPaths = currentPaths[pathSegment] = + currentPaths[pathSegment] || + Object.create(null, { [EntitiesKey]: { value: new Map>() } }); } const trackedEntities = getOrCreate( currentPaths[EntitiesKey], @@ -111,65 +108,3 @@ 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/variables/ArgumentsVariable.ts b/src/ast/variables/ArgumentsVariable.ts index 531d5e7f9..a623b235b 100644 --- a/src/ast/variables/ArgumentsVariable.ts +++ b/src/ast/variables/ArgumentsVariable.ts @@ -1,18 +1,17 @@ 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 { EMPTY_PATH, UNKNOWN_PATH } from '../utils/PathTracker'; +import { 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, EMPTY_PATH, context, 'other'); + super('arguments', null, UNKNOWN_EXPRESSION, context, 'other'); } addArgumentToBeDeoptimized(argument: ExpressionEntity): void { @@ -27,8 +26,8 @@ export default class ArgumentsVariable extends LocalVariable { return type !== INTERACTION_ACCESSED || path.length > 1; } - includePath(path: ObjectPath, context: InclusionContext) { - super.includePath(path, context); + include() { + super.include(); 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 f619de19b..baed0873c 100644 --- a/src/ast/variables/ExportDefaultVariable.ts +++ b/src/ast/variables/ExportDefaultVariable.ts @@ -5,7 +5,6 @@ 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'; @@ -13,7 +12,7 @@ import type Variable from './Variable'; export default class ExportDefaultVariable extends LocalVariable { hasId = false; - private readonly originalId: IdentifierWithVariable | null = null; + private originalId: IdentifierWithVariable | null = null; private originalVariable: Variable | null = null; constructor( @@ -21,14 +20,7 @@ export default class ExportDefaultVariable extends LocalVariable { exportDefaultDeclaration: ExportDefaultDeclaration, context: AstContext ) { - super( - name, - exportDefaultDeclaration, - exportDefaultDeclaration.declaration, - EMPTY_PATH, - context, - 'other' - ); + super(name, exportDefaultDeclaration, exportDefaultDeclaration.declaration, 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 f936e32c3..f473c49c8 100644 --- a/src/ast/variables/ExportShimVariable.ts +++ b/src/ast/variables/ExportShimVariable.ts @@ -1,7 +1,5 @@ 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 { @@ -12,8 +10,8 @@ export default class ExportShimVariable extends Variable { this.module = module; } - includePath(path: ObjectPath, context: InclusionContext): void { - super.includePath(path, context); + include(): void { + super.include(); this.module.needsExportShim = true; } } diff --git a/src/ast/variables/ExternalVariable.ts b/src/ast/variables/ExternalVariable.ts index ceda7380b..0775fc036 100644 --- a/src/ast/variables/ExternalVariable.ts +++ b/src/ast/variables/ExternalVariable.ts @@ -1,9 +1,8 @@ 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 { @@ -28,8 +27,8 @@ export default class ExternalVariable extends Variable { return type !== INTERACTION_ACCESSED || path.length > (this.isNamespace ? 1 : 0); } - includePath(path: ObjectPath, context: InclusionContext): void { - super.includePath(path, context); + include(): void { + super.include(); this.module.used = true; } } diff --git a/src/ast/variables/GlobalVariable.ts b/src/ast/variables/GlobalVariable.ts index d6be0ebab..4f168ac8f 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 EntityPathTracker, type ObjectPath } from '../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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: EntityPathTracker + recursionTracker: PathTracker ) { 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: EntityPathTracker, + _recursionTracker: PathTracker, _origin: DeoptimizableEntity ): LiteralValueOrUnknown { const globalAtPath = getGlobalAtPath([this.name, ...path]); diff --git a/src/ast/variables/LocalVariable.ts b/src/ast/variables/LocalVariable.ts index 97d262a4c..83b32a81e 100644 --- a/src/ast/variables/LocalVariable.ts +++ b/src/ast/variables/LocalVariable.ts @@ -2,6 +2,7 @@ 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, @@ -11,6 +12,7 @@ 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, @@ -21,43 +23,33 @@ import { } from '../nodes/shared/Expression'; import type { Node } from '../nodes/shared/Node'; import type { VariableKind } from '../nodes/shared/VariableKinds'; -import { limitConcatenatedPathDepth, MAX_PATH_DEPTH } from '../utils/limitPathLength'; -import { - EMPTY_PATH, - type EntityPathTracker, - IncludedPathTracker, - type ObjectPath, - UNKNOWN_PATH, - UnknownKey -} from '../utils/PathTracker'; +import { type ObjectPath, type PathTracker, UNKNOWN_PATH } 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: EntityPathTracker; - protected includedPathTracker = new IncludedPathTracker(); + protected deoptimizationTracker: PathTracker; 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, - readonly kind: VariableKind + 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 { @@ -70,28 +62,23 @@ 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: EntityPathTracker + recursionTracker: PathTracker ): void { - if (this.isReassigned || path.length + this.initPath.length > MAX_PATH_DEPTH) { + if (this.isReassigned) { deoptimizeInteraction(interaction); return; } recursionTracker.withTrackedEntityAtPath( path, this.init, - () => { - this.init.deoptimizeArgumentsOnInteractionAtPath( - interaction, - [...this.initPath, ...path], - recursionTracker - ); - }, + () => this.init.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker), undefined ); } @@ -110,18 +97,18 @@ export default class LocalVariable extends Variable { for (const expression of expressionsToBeDeoptimized) { expression.deoptimizeCache(); } - this.init.deoptimizePath([...this.initPath, UnknownKey]); + this.init.deoptimizePath(UNKNOWN_PATH); } else { - this.init.deoptimizePath(limitConcatenatedPathDepth(this.initPath, path)); + this.init.deoptimizePath(path); } } getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { - if (this.isReassigned || path.length + this.initPath.length > MAX_PATH_DEPTH) { + if (this.isReassigned) { return UnknownValue; } return recursionTracker.withTrackedEntityAtPath( @@ -129,11 +116,7 @@ export default class LocalVariable extends Variable { this.init, () => { this.expressionsToBeDeoptimized.push(origin); - return this.init.getLiteralValueAtPath( - [...this.initPath, ...path], - recursionTracker, - origin - ); + return this.init.getLiteralValueAtPath(path, recursionTracker, origin); }, UnknownValue ); @@ -142,10 +125,10 @@ export default class LocalVariable extends Variable { getReturnExpressionWhenCalledAtPath( path: ObjectPath, interaction: NodeInteractionCalled, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): [expression: ExpressionEntity, isPure: boolean] { - if (this.isReassigned || path.length + this.initPath.length > MAX_PATH_DEPTH) { + if (this.isReassigned) { return UNKNOWN_RETURN_EXPRESSION; } return recursionTracker.withTrackedEntityAtPath( @@ -154,7 +137,7 @@ export default class LocalVariable extends Variable { () => { this.expressionsToBeDeoptimized.push(origin); return this.init.getReturnExpressionWhenCalledAtPath( - [...this.initPath, ...path], + path, interaction, recursionTracker, origin @@ -169,15 +152,12 @@ 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([...this.initPath, ...path], interaction, context) + this.init.hasEffectsOnInteractionAtPath(path, interaction, context) ); } case INTERACTION_ASSIGNED: { @@ -186,7 +166,7 @@ export default class LocalVariable extends Variable { if (this.isReassigned) return true; return ( !context.assigned.trackEntityAtPathAndGetIfTracked(path, this) && - this.init.hasEffectsOnInteractionAtPath([...this.initPath, ...path], interaction, context) + this.init.hasEffectsOnInteractionAtPath(path, interaction, context) ); } case INTERACTION_CALLED: { @@ -195,18 +175,18 @@ export default class LocalVariable extends Variable { !( interaction.withNew ? context.instantiated : context.called ).trackEntityAtPathAndGetIfTracked(path, interaction.args, this) && - this.init.hasEffectsOnInteractionAtPath([...this.initPath, ...path], interaction, context) + this.init.hasEffectsOnInteractionAtPath(path, interaction, context) ); } } } - includePath(path: ObjectPath, context: InclusionContext): void { - if (!this.includedPathTracker.includePathAndGetIfIncluded(path)) { - super.includePath(path, context); + include(): void { + if (!this.included) { + super.include(); 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.includePath(EMPTY_PATH, context, false); + if (!declaration.included) declaration.include(createInclusionContext(), 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 @@ -216,30 +196,20 @@ 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, 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); + 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); } } else { context.includedCallArguments.add(this.init); - this.init.includeCallArguments(context, interaction); + this.init.includeCallArguments(context, parameters); context.includedCallArguments.delete(this.init); } } diff --git a/src/ast/variables/NamespaceVariable.ts b/src/ast/variables/NamespaceVariable.ts index 91006e536..6085c435b 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, InclusionContext } from '../ExecutionContext'; +import type { HasEffectsContext } 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 { EntityPathTracker, ObjectPath } from '../utils/PathTracker'; +import type { ObjectPath, PathTracker } 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: EntityPathTracker + recursionTracker: PathTracker ) { 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 { ); } - includePath(path: ObjectPath, context: InclusionContext): void { - super.includePath(path, context); + include(): void { + super.include(); this.context.includeAllExports(); } diff --git a/src/ast/variables/ParameterVariable.ts b/src/ast/variables/ParameterVariable.ts index b9f354fcf..a17bf3b11 100644 --- a/src/ast/variables/ParameterVariable.ts +++ b/src/ast/variables/ParameterVariable.ts @@ -1,8 +1,7 @@ import type { AstContext } from '../../Module'; import { EMPTY_ARRAY } from '../../utils/blank'; import type { DeoptimizableEntity } from '../DeoptimizableEntity'; -import type { InclusionContext } from '../ExecutionContext'; -import { type HasEffectsContext } 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'; @@ -14,10 +13,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 { - EntityPathTracker, + EMPTY_PATH, + PathTracker, SHARED_RECURSION_TRACKER, UNKNOWN_PATH, UnknownKey @@ -32,33 +31,31 @@ 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 EntityPathTracker(); +const EMPTY_PATH_TRACKER = new PathTracker(); const UNKNOWN_DEOPTIMIZED_ENTITY = new Set([UNKNOWN_EXPRESSION]); export default class ParameterVariable extends LocalVariable { private deoptimizationInteractions: DeoptimizationInteraction[] = []; - private deoptimizations = new EntityPathTracker(); + private deoptimizations = new PathTracker(); private deoptimizedFields = new Set(); - private argumentsToBeDeoptimized = new Set(); - private expressionsDependingOnKnownValue: DeoptimizableEntity[] = []; + private entitiesToBeDeoptimized = new Set(); + private expressionsUseTheKnownValue: DeoptimizableEntity[] = []; constructor( name: string, declarator: Identifier | ExportDefaultDeclaration | null, - argumentPath: ObjectPath, context: AstContext ) { - super(name, declarator, UNKNOWN_EXPRESSION, argumentPath, context, 'parameter'); + super(name, declarator, UNKNOWN_EXPRESSION, context, 'parameter'); } - addArgumentValue(entity: ExpressionEntity): void { - this.updateKnownValue(entity); + addEntityToBeDeoptimized(entity: ExpressionEntity): void { 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.argumentsToBeDeoptimized.has(UNKNOWN_EXPRESSION)) { - this.argumentsToBeDeoptimized.add(UNKNOWN_EXPRESSION); + if (!this.entitiesToBeDeoptimized.has(UNKNOWN_EXPRESSION)) { + this.entitiesToBeDeoptimized.add(UNKNOWN_EXPRESSION); for (const { interaction } of this.deoptimizationInteractions) { deoptimizeInteraction(interaction); } @@ -67,38 +64,27 @@ 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([...this.initPath, UnknownKey]); - } else if (!this.argumentsToBeDeoptimized.has(entity)) { - this.argumentsToBeDeoptimized.add(entity); + entity.deoptimizePath(UNKNOWN_PATH); + } else if (!this.entitiesToBeDeoptimized.has(entity)) { + this.entitiesToBeDeoptimized.add(entity); for (const field of this.deoptimizedFields) { - entity.deoptimizePath([...this.initPath, field]); + entity.deoptimizePath([field]); } for (const { interaction, path } of this.deoptimizationInteractions) { - if (this.initPath.length + path.length > MAX_PATH_DEPTH) { - deoptimizeInteraction(interaction); - continue; - } - entity.deoptimizeArgumentsOnInteractionAtPath( - interaction, - [...this.initPath, ...path], - SHARED_RECURSION_TRACKER - ); + entity.deoptimizeArgumentsOnInteractionAtPath(interaction, 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.expressionsDependingOnKnownValue) { + for (const expression of this.expressionsUseTheKnownValue) { expression.deoptimizeCache(); } - this.expressionsDependingOnKnownValue = EMPTY_ARRAY as unknown as DeoptimizableEntity[]; + this.expressionsUseTheKnownValue = EMPTY_ARRAY as unknown as DeoptimizableEntity[]; } deoptimizeCache(): void { @@ -113,7 +99,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 */ - private updateKnownValue(argument: ExpressionEntity) { + updateKnownValue(argument: ExpressionEntity) { if (this.isReassigned) { return; } @@ -121,7 +107,7 @@ export default class ParameterVariable extends LocalVariable { if (this.knownValue === null) { this.knownValue = argument; this.knownValueLiteral = argument.getLiteralValueAtPath( - this.initPath, + EMPTY_PATH, SHARED_RECURSION_TRACKER, this ); @@ -144,7 +130,7 @@ export default class ParameterVariable extends LocalVariable { return; } // add tracking for the new argument - const newValue = argument.getLiteralValueAtPath(this.initPath, SHARED_RECURSION_TRACKER, this); + const newValue = argument.getLiteralValueAtPath(EMPTY_PATH, SHARED_RECURSION_TRACKER, this); if (newValue !== oldValue) { this.markReassigned(); } @@ -166,18 +152,18 @@ export default class ParameterVariable extends LocalVariable { getLiteralValueAtPath( path: ObjectPath, - recursionTracker: EntityPathTracker, + recursionTracker: PathTracker, origin: DeoptimizableEntity ): LiteralValueOrUnknown { - if (this.isReassigned || path.length + this.initPath.length > MAX_PATH_DEPTH) { + if (this.isReassigned) { return UnknownValue; } const knownValue = this.getKnownValue(); - this.expressionsDependingOnKnownValue.push(origin); + this.expressionsUseTheKnownValue.push(origin); return recursionTracker.withTrackedEntityAtPath( path, knownValue, - () => knownValue.getLiteralValueAtPath([...this.initPath, ...path], recursionTracker, origin), + () => knownValue.getLiteralValueAtPath(path, recursionTracker, origin), UnknownValue ); } @@ -187,34 +173,18 @@ export default class ParameterVariable extends LocalVariable { interaction: NodeInteraction, context: HasEffectsContext ): boolean { - const { type } = interaction; - if ( - this.isReassigned || - type === INTERACTION_ASSIGNED || - path.length + this.initPath.length > MAX_PATH_DEPTH - ) { + if (this.isReassigned || interaction.type === INTERACTION_ASSIGNED) { return super.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 - ) - ); + const knownValue = this.getKnownValue(); + return knownValue.hasEffectsOnInteractionAtPath(path, interaction, context); } deoptimizeArgumentsOnInteractionAtPath(interaction: NodeInteraction, path: ObjectPath): void { // For performance reasons, we fully deoptimize all deeper interactions if ( path.length >= 2 || - this.argumentsToBeDeoptimized.has(UNKNOWN_EXPRESSION) || + this.entitiesToBeDeoptimized.has(UNKNOWN_EXPRESSION) || this.deoptimizationInteractions.length >= MAX_TRACKED_INTERACTIONS || (path.length === 1 && (this.deoptimizedFields.has(UnknownKey) || @@ -224,14 +194,10 @@ export default class ParameterVariable extends LocalVariable { return; } if (!this.deoptimizations.trackEntityAtPathAndGetIfTracked(path, interaction.args)) { - for (const entity of this.argumentsToBeDeoptimized) { - entity.deoptimizeArgumentsOnInteractionAtPath( - interaction, - [...this.initPath, ...path], - SHARED_RECURSION_TRACKER - ); + for (const entity of this.entitiesToBeDeoptimized) { + entity.deoptimizeArgumentsOnInteractionAtPath(interaction, path, SHARED_RECURSION_TRACKER); } - if (!this.argumentsToBeDeoptimized.has(UNKNOWN_EXPRESSION)) { + if (!this.entitiesToBeDeoptimized.has(UNKNOWN_EXPRESSION)) { this.deoptimizationInteractions.push({ interaction, path @@ -253,17 +219,17 @@ export default class ParameterVariable extends LocalVariable { return; } this.deoptimizedFields.add(key); - for (const entity of this.argumentsToBeDeoptimized) { + for (const entity of this.entitiesToBeDeoptimized) { // We do not need a recursion tracker here as we already track whether // this field is deoptimized - entity.deoptimizePath([...this.initPath, key]); + entity.deoptimizePath([key]); } if (key === UnknownKey) { // save some memory this.deoptimizationInteractions = NO_INTERACTIONS; this.deoptimizations = EMPTY_PATH_TRACKER; this.deoptimizedFields = UNKNOWN_DEOPTIMIZED_FIELD; - this.argumentsToBeDeoptimized = UNKNOWN_DEOPTIMIZED_ENTITY; + this.entitiesToBeDeoptimized = UNKNOWN_DEOPTIMIZED_ENTITY; } } @@ -280,8 +246,4 @@ 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 685b4e78b..ce10f442e 100644 --- a/src/ast/variables/SyntheticNamedExportVariable.ts +++ b/src/ast/variables/SyntheticNamedExportVariable.ts @@ -1,7 +1,5 @@ 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'; @@ -46,9 +44,9 @@ export default class SyntheticNamedExportVariable extends Variable { return `${this.syntheticNamespace.getName(getPropertyAccess)}${getPropertyAccess(this.name)}`; } - includePath(path: ObjectPath, context: InclusionContext): void { - super.includePath(path, context); - this.context.includeVariableInModule(this.syntheticNamespace, path); + include(): void { + super.include(); + this.context.includeVariableInModule(this.syntheticNamespace); } setRenderNames(baseName: string | null, name: string | null): void { diff --git a/src/ast/variables/ThisVariable.ts b/src/ast/variables/ThisVariable.ts index b55b2c1f4..1d8ea9bcd 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 { EMPTY_PATH, type ObjectPath } from '../utils/PathTracker'; +import { type ObjectPath } from '../utils/PathTracker'; import ParameterVariable from './ParameterVariable'; export default class ThisVariable extends ParameterVariable { constructor(context: AstContext) { - super('this', null, EMPTY_PATH, context); + super('this', null, context); } hasEffectsOnInteractionAtPath( diff --git a/src/ast/variables/Variable.ts b/src/ast/variables/Variable.ts index 319608171..2b8a1924f 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, InclusionContext } from '../ExecutionContext'; +import type { HasEffectsContext } 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. */ - includePath(path: ObjectPath, context: InclusionContext): void { + include(): void { this.included = true; - this.renderedLikeHoisted?.includePath(path, context); + this.renderedLikeHoisted?.include(); } /** diff --git a/test/chunking-form/samples/dynamic-import-with-namespace/_config.js b/test/chunking-form/samples/dynamic-import-with-namespace/_config.js deleted file mode 100644 index e2744888c..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_config.js +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index 0317e08ce..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module1.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 0317e08ce..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module10.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 0317e08ce..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module2.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 3123adef9..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module3.js +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 0317e08ce..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module4.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 0317e08ce..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module5.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 3123adef9..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module6.js +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 0317e08ce..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module7.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 0317e08ce..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module8.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 3123adef9..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/generated-module9.js +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 012563bd0..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/amd/main.js +++ /dev/null @@ -1,87 +0,0 @@ -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 deleted file mode 100644 index 32b8c4cc0..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module1.js +++ /dev/null @@ -1,11 +0,0 @@ -'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 deleted file mode 100644 index 32b8c4cc0..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module10.js +++ /dev/null @@ -1,11 +0,0 @@ -'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 deleted file mode 100644 index 32b8c4cc0..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module2.js +++ /dev/null @@ -1,11 +0,0 @@ -'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 deleted file mode 100644 index 02ce8a98f..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module3.js +++ /dev/null @@ -1,5 +0,0 @@ -'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 deleted file mode 100644 index 32b8c4cc0..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module4.js +++ /dev/null @@ -1,11 +0,0 @@ -'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 deleted file mode 100644 index 32b8c4cc0..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module5.js +++ /dev/null @@ -1,11 +0,0 @@ -'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 deleted file mode 100644 index 02ce8a98f..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module6.js +++ /dev/null @@ -1,5 +0,0 @@ -'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 deleted file mode 100644 index 32b8c4cc0..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module7.js +++ /dev/null @@ -1,11 +0,0 @@ -'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 deleted file mode 100644 index 32b8c4cc0..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module8.js +++ /dev/null @@ -1,11 +0,0 @@ -'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 deleted file mode 100644 index 02ce8a98f..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/generated-module9.js +++ /dev/null @@ -1,5 +0,0 @@ -'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 deleted file mode 100644 index 9afab5592..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/cjs/main.js +++ /dev/null @@ -1,85 +0,0 @@ -'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 deleted file mode 100644 index 79e040814..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module1.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 79e040814..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module10.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 79e040814..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module2.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 4ac31b619..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module3.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 79e040814..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module4.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 79e040814..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module5.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 4ac31b619..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module6.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 79e040814..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module7.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 79e040814..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module8.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 4ac31b619..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/generated-module9.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 9d78ab9ac..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/es/main.js +++ /dev/null @@ -1,83 +0,0 @@ -(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 deleted file mode 100644 index e683d313e..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module1.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index e683d313e..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module10.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index e683d313e..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module2.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 72f26099c..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module3.js +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index e683d313e..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module4.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index e683d313e..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module5.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 72f26099c..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module6.js +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index e683d313e..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module7.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index e683d313e..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module8.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 72f26099c..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/generated-module9.js +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index e5eda4d49..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/_expected/system/main.js +++ /dev/null @@ -1,92 +0,0 @@ -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 deleted file mode 100644 index d7ad776d7..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/main.js +++ /dev/null @@ -1,83 +0,0 @@ -(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 deleted file mode 100644 index 711d56916..000000000 --- a/test/chunking-form/samples/dynamic-import-with-namespace/module.js +++ /dev/null @@ -1,4 +0,0 @@ -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 4825187a8..d8c0c92e3 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}; +const obj = { mutated: false, noEffect() {} }; function updateObj(target) { target.mutated = true; diff --git a/test/form/samples/computed-properties/_expected/amd.js b/test/form/samples/computed-properties/_expected/amd.js new file mode 100644 index 000000000..2d2e59712 --- /dev/null +++ b/test/form/samples/computed-properties/_expected/amd.js @@ -0,0 +1,19 @@ +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 new file mode 100644 index 000000000..605d4a3e5 --- /dev/null +++ b/test/form/samples/computed-properties/_expected/cjs.js @@ -0,0 +1,17 @@ +'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.js b/test/form/samples/computed-properties/_expected/es.js similarity index 100% rename from test/form/samples/computed-properties/_expected.js rename to test/form/samples/computed-properties/_expected/es.js diff --git a/test/form/samples/computed-properties/_expected/iife.js b/test/form/samples/computed-properties/_expected/iife.js new file mode 100644 index 000000000..07c70d5cc --- /dev/null +++ b/test/form/samples/computed-properties/_expected/iife.js @@ -0,0 +1,22 @@ +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 new file mode 100644 index 000000000..ec34fc724 --- /dev/null +++ b/test/form/samples/computed-properties/_expected/system.js @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..44eec078c --- /dev/null +++ b/test/form/samples/computed-properties/_expected/umd.js @@ -0,0 +1,23 @@ +(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 deleted file mode 100644 index 62a46d2e9..000000000 --- a/test/form/samples/destructured-known-arguments/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index cfd0627c1..000000000 --- a/test/form/samples/destructured-known-arguments/_expected.js +++ /dev/null @@ -1,28 +0,0 @@ -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 deleted file mode 100644 index c4a22063c..000000000 --- a/test/form/samples/destructured-known-arguments/main.js +++ /dev/null @@ -1,32 +0,0 @@ -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 b3755754a..9fefa5dfc 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'; -const {x} = stuff.y(); +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 deleted file mode 100644 index 2871b74fd..000000000 --- a/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_config.js +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 59b5fe8e2..000000000 --- a/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/_expected.js +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 2a5bf6c92..000000000 --- a/test/form/samples/object-expression-treeshaking/ignore-property-read-side-effects/main.js +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index a9129d7fb..000000000 --- a/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index b822667d2..000000000 --- a/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/_expected.js +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index d7e782dbd..000000000 --- a/test/form/samples/object-expression-treeshaking/only-include-destructured-parameter-props/main.js +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index c25c9638e..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 48ef3c76d..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/_expected.js +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index be4bfe8b7..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-props-via-destructuring/main.js +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index b0f0e703a..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index a87f92cf0..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/_expected.js +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 1a46a5cf6..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-nested-props/main.js +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 780dd0a50..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_config.js +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 7e3e405c4..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/_expected.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 4f29aed21..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props-external-call/main.js +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 8a1447349..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index ed6b0d512..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/_expected.js +++ /dev/null @@ -1,18 +0,0 @@ -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 deleted file mode 100644 index 3c381fe96..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-parameter-props/main.js +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index efeec207d..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-props/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index f73015887..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-props/_expected.js +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 3b2940aaa..000000000 --- a/test/form/samples/object-expression-treeshaking/remove-unused-props/main.js +++ /dev/null @@ -1,5 +0,0 @@ -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 0d2ad4b1d..ecd783e1d 100644 --- a/test/form/samples/object-literal-property-overwrites/_expected.js +++ b/test/form/samples/object-literal-property-overwrites/_expected.js @@ -18,14 +18,20 @@ const retained3 = { retained3.bar(); const retained4 = { + foo: {}, foo: globalThis.unknown }; retained4.foo.bar = 1; const retained5 = { - ['f' + 'oo']: globalThis.unknown}; + foo: {}, + ['f' + 'oo']: globalThis.unknown, + ['b' + 'ar']: {}, +}; 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 3a638fc2e..fcffb6f8f 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 = { }; +const foo = { nullVal: null }; foo?.x.x; // retained diff --git a/test/form/samples/recursive-destructuring/_config.js b/test/form/samples/recursive-destructuring/_config.js deleted file mode 100644 index 36b2eac78..000000000 --- a/test/form/samples/recursive-destructuring/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 5e79f768e..000000000 --- a/test/form/samples/recursive-destructuring/_expected.js +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 5e79f768e..000000000 --- a/test/form/samples/recursive-destructuring/main.js +++ /dev/null @@ -1 +0,0 @@ -var { 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 b7214fb1d..31917077c 100644 --- a/test/form/samples/side-effects-getters-and-setters/_expected.js +++ b/test/form/samples/side-effects-getters-and-setters/_expected.js @@ -1,7 +1,12 @@ 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 fc94706a8..5cd0363fa 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/amd.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/amd.js @@ -9,7 +9,9 @@ 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 a607ca44b..2efd87aac 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/cjs.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/cjs.js @@ -9,7 +9,9 @@ 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 cb958dd28..0814df63c 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/es.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/es.js @@ -7,7 +7,9 @@ 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 173c1f306..fb4bea51a 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/iife.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/iife.js @@ -10,7 +10,9 @@ 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 c4b22d8ac..8853d5df6 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/system.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/system.js @@ -12,7 +12,9 @@ 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 b9ceed374..6ff0de937 100644 --- a/test/form/samples/side-effects-logical-expressions/_expected/umd.js +++ b/test/form/samples/side-effects-logical-expressions/_expected/umd.js @@ -12,7 +12,9 @@ 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 f2e8bbcc0..f42a72e8a 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 = { }; +const retained2 = { x: {} }; 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 a86b21530..16c6cefd9 100644 --- a/test/form/samples/side-effects-pattern-assignment/_expected.js +++ b/test/form/samples/side-effects-pattern-assignment/_expected.js @@ -1,13 +1,3 @@ -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; @@ -22,16 +12,6 @@ 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 80e548b6c..81f189c97 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, {f3} = globalThis.obj; exports("f2", f2); + let {f1, f2} = 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 c7115bf3b..8a69e51d0 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; }); - const { doesNotExists } = await Promise.resolve().then(function () { return sub1; }); + await Promise.resolve().then(function () { return sub1; }); (await Promise.resolve().then(function () { return sub2; })).bar2(); - const { foo2 } = await Promise.resolve().then(function () { return sub2; }); - const { foo3 } = await Promise.resolve().then(function () { return sub2; }); + await Promise.resolve().then(function () { return sub2; }); + 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 - const { foo4, x } = await Promise.resolve().then(function () { return sub4; }); + 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 - const { named1 } = await Promise.resolve().then(function () { return bail1$1; }); + await Promise.resolve().then(function () { return bail1$1; }); Promise.resolve().then(function () { return bail1$1; }); // this make it bail out - const { ...named2 } = await Promise.resolve().then(function () { return bail2$1; }) + await Promise.resolve().then(function () { return bail2$1; }) (await Promise.resolve().then(function () { return bail3$1; }))[foo]; @@ -55,13 +55,9 @@ async function entry() { Promise.resolve().then(function () { return bail8$1; }), ]; - const { [foo]: bar } = await Promise.resolve().then(function () { return bail9$1; }); + 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() { @@ -240,13 +236,4 @@ 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 45bdcf41d..48bd026a6 100644 --- a/test/form/samples/treeshake-deterministic-dynamic-import/main.js +++ b/test/form/samples/treeshake-deterministic-dynamic-import/main.js @@ -43,8 +43,4 @@ 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 deleted file mode 100644 index c35dc775c..000000000 --- a/test/function/samples/deoptimize-nested-function-arg/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index ce66ae745..000000000 --- a/test/function/samples/deoptimize-nested-function-arg/main.js +++ /dev/null @@ -1,8 +0,0 @@ -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 9bebcde68..1b9072bb0 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, 'obj1'); -assert.ok(obj2.x ? true : false, 'obj2'); +assert.ok(obj1.x ? true : false); +assert.ok(obj2.x ? true : false); 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 deleted file mode 100644 index 575f09159..000000000 --- a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 9bcefbb31..000000000 --- a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/dep.js +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 29c4a2fc5..000000000 --- a/test/function/samples/object-expression-treeshaking/deconflict-destructured-for-side-effects/main.js +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 44ddb5912..000000000 --- a/test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 95a2287a8..000000000 --- a/test/function/samples/object-expression-treeshaking/deoptimize-arguments-via-destructuring/main.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index d7e2cced9..000000000 --- a/test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 60b29d3d5..000000000 --- a/test/function/samples/object-expression-treeshaking/deoptimize-object-via-destructured-getter/main.js +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 394122326..000000000 --- a/test/function/samples/object-expression-treeshaking/do-not-destructure-unused/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 8ccfd46e6..000000000 --- a/test/function/samples/object-expression-treeshaking/do-not-destructure-unused/main.js +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index 9c660cbb7..000000000 --- a/test/function/samples/object-expression-treeshaking/exports/_config.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index f905aa8f9..000000000 --- a/test/function/samples/object-expression-treeshaking/exports/main.js +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 283d1dc57..000000000 --- a/test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index fea5611f9..000000000 --- a/test/function/samples/object-expression-treeshaking/get-literal-value-via-destructuring/main.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 573a3cd45..000000000 --- a/test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 0f800d083..000000000 --- a/test/function/samples/object-expression-treeshaking/get-return-expression-via-destructuring/main.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 3021ca724..000000000 --- a/test/function/samples/object-expression-treeshaking/include-call-arguments-path/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 57709cc1a..000000000 --- a/test/function/samples/object-expression-treeshaking/include-call-arguments-path/main.js +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index a3e2d8dd3..000000000 --- a/test/function/samples/object-expression-treeshaking/include-destructuring-rest/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 1909f73bb..000000000 --- a/test/function/samples/object-expression-treeshaking/include-destructuring-rest/main.js +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index 8b1109d88..000000000 --- a/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/_config.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 5fb7ac565..000000000 --- a/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/dep.js +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 089c744b1..000000000 --- a/test/function/samples/object-expression-treeshaking/include-dynamic-import-properties-await/main.js +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 6fde0ab66..000000000 --- a/test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 7959bf5e3..000000000 --- a/test/function/samples/object-expression-treeshaking/include-redeclared-destructured-variable-paths/main.js +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 5d6a6ecac..000000000 --- a/test/function/samples/object-expression-treeshaking/include-this-unknown-function/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index f25e812ab..000000000 --- a/test/function/samples/object-expression-treeshaking/include-this-unknown-function/main.js +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 7669a3d47..000000000 --- a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 3049bcb52..000000000 --- a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-assignment/main.js +++ /dev/null @@ -1,36 +0,0 @@ -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 deleted file mode 100644 index 7368c76c3..000000000 --- a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 8597d0263..000000000 --- a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters-in-calls/main.js +++ /dev/null @@ -1,34 +0,0 @@ -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 deleted file mode 100644 index 1f855971b..000000000 --- a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 9750d2228..000000000 --- a/test/function/samples/object-expression-treeshaking/include-unused-destructured-getters/main.js +++ /dev/null @@ -1,33 +0,0 @@ -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 deleted file mode 100644 index dfb161d6e..000000000 --- a/test/function/samples/object-expression-treeshaking/object-side-effects/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index d32c4546a..000000000 --- a/test/function/samples/object-expression-treeshaking/object-side-effects/main.js +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index 07cdb40b8..000000000 --- a/test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index eff4d3124..000000000 --- a/test/function/samples/object-expression-treeshaking/track-access-side-effect-via-destructuring/main.js +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index 5f3148007..000000000 --- a/test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 1fae33053..000000000 --- a/test/function/samples/object-expression-treeshaking/track-assignment-side-effect-via-destructuring/main.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 993182b77..000000000 --- a/test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 09711eded..000000000 --- a/test/function/samples/object-expression-treeshaking/track-call-side-effect-via-destructuring/main.js +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 6268facf7..000000000 --- a/test/function/samples/object-expression-treeshaking/track-destructured-setter/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 58d231674..000000000 --- a/test/function/samples/object-expression-treeshaking/track-destructured-setter/main.js +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 342682dfe..000000000 --- a/test/function/samples/object-tree-shaking-for-global-assignment/_config.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 2b3e70c2b..000000000 --- a/test/function/samples/object-tree-shaking-for-global-assignment/main.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 78d27f625..000000000 --- a/test/function/samples/object-tree-shaking-for-parameter/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 0d18beb54..000000000 --- a/test/function/samples/object-tree-shaking-for-parameter/main.js +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index 0148514e6..000000000 --- a/test/function/samples/object-tree-shaking-in-function-self-call/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index bb5d1b013..000000000 --- a/test/function/samples/object-tree-shaking-in-function-self-call/main.js +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 7fc0d9a35..000000000 --- a/test/function/samples/object-tree-shaking-with-destructed-export/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 7d821e50c..000000000 --- a/test/function/samples/object-tree-shaking-with-destructed-export/main.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index b60c753a2..000000000 --- a/test/function/samples/object-tree-shaking-with-destructed-export/module.js +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 19f844a1e..000000000 --- a/test/function/samples/object-tree-shaking-with-duplicated-function-call/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 4a920f168..000000000 --- a/test/function/samples/object-tree-shaking-with-duplicated-function-call/main.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index c87382ae0..000000000 --- a/test/function/samples/preserve-exported-object-in-namespace/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 596387027..000000000 --- a/test/function/samples/preserve-exported-object-in-namespace/main.js +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 1399af4bb..000000000 --- a/test/function/samples/preserve-exported-object-in-namespace/module.js +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 5281c9eb6..000000000 --- a/test/function/samples/preserve-var-declaration/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 92df84abc..000000000 --- a/test/function/samples/preserve-var-declaration/main.js +++ /dev/null @@ -1,5 +0,0 @@ -{ - 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 deleted file mode 100644 index 9e859a0ff..000000000 --- a/test/function/samples/recursive-calls-without-treeshake/_config.js +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index df7cfe3f7..000000000 --- a/test/function/samples/recursive-calls-without-treeshake/main.js +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 51d3a0c6c..000000000 --- a/test/function/samples/wrap-empty-object-with-double-brackets/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index de9628c93..000000000 --- a/test/function/samples/wrap-empty-object-with-double-brackets/main.js +++ /dev/null @@ -1,2 +0,0 @@ -Object.prototype.customize_fn = () => {}; -const c = {}.customize_fn(); From 7c0b1f8810013b5a351a976df30a6a5da4fa164b Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Mon, 18 Nov 2024 16:57:19 +0100 Subject: [PATCH 11/12] 4.27.3 --- CHANGELOG.md | 12 ++++++++++++ browser/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d5b8c3c5..2569dce4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # rollup changelog +## 4.27.3 + +_2024-11-18_ + +### Bug Fixes + +- Revert object property tree-shaking for now (#5736) + +### Pull Requests + +- [#5736](https://github.com/rollup/rollup/pull/5736): Revert object tree-shaking until some issues have been resolved (@lukastaegert) + ## 4.27.2 _2024-11-15_ diff --git a/browser/package.json b/browser/package.json index 501eaeefd..d9948ff7f 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "4.27.2", + "version": "4.27.3", "description": "Next-generation ES module bundler browser build", "main": "dist/rollup.browser.js", "module": "dist/es/rollup.browser.js", diff --git a/package-lock.json b/package-lock.json index 057313976..7d5afb164 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rollup", - "version": "4.27.2", + "version": "4.27.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rollup", - "version": "4.27.2", + "version": "4.27.3", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" diff --git a/package.json b/package.json index 833f2cde6..bf93d44bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "4.27.2", + "version": "4.27.3", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", From 7b26e4138f4ecc939838d4da32392a855bae230b Mon Sep 17 00:00:00 2001 From: waynzh Date: Fri, 22 Nov 2024 17:05:11 +0800 Subject: [PATCH 12/12] docs(cn): resolve conflicts --- docs/migration/index.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/migration/index.md b/docs/migration/index.md index c5cb3aa1e..b221389b9 100644 --- a/docs/migration/index.md +++ b/docs/migration/index.md @@ -24,11 +24,7 @@ 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. ->>>>>>> 7c0b1f8810013b5a351a976df30a6a5da4fa164b +另外,一个明显的变化是,Rollup 现在在文件名中使用 URL 安全的 base64 哈希值,而不是旧的 base16 哈希值。这种方式提供了更高的哈希安全性,但由于技术原因,哈希长度现在最多限制为 21 个字符。 当打包 CLI 应用程序时,如果输出 [`format`](../configuration-options/index.md#output-format) 为 `es` 或 `cjs`,Rollup 现在将自动保留入口文件中的 shebang 注释。以前,你需要通过插件添加注释。