diff --git a/bin/src/help.md b/bin/src/help.md index e9526e600f9..6d95d741620 100644 --- a/bin/src/help.md +++ b/bin/src/help.md @@ -48,6 +48,7 @@ Basic options: --sourcemapExcludeSources Do not include source code in source maps --sourcemapFile Specify bundle position for source maps --no-treeshake Disable tree-shaking optimisations +--no-treeshake.annotations Ignore pure call annotations --no-treeshake.propertyReadSideEffects Ignore property access side-effects --treeshake.pureExternalModules Assume side-effect free externals diff --git a/docs/01-command-line-reference.md b/docs/01-command-line-reference.md index 0db1cd3d96f..cad22f778c4 100755 --- a/docs/01-command-line-reference.md +++ b/docs/01-command-line-reference.md @@ -229,6 +229,7 @@ Many options have command line equivalents. In those cases, any arguments passed --sourcemapExcludeSources Do not include source code in source maps --sourcemapFile Specify bundle position for source maps --no-treeshake Disable tree-shaking optimisations +--no-treeshake.annotations Ignore pure call annotations --no-treeshake.propertyReadSideEffects Ignore property access side-effects --treeshake.pureExternalModules Assume side-effect free externals ``` diff --git a/docs/999-big-list-of-options.md b/docs/999-big-list-of-options.md index 86d155c8fbd..b6fa3a4ead9 100755 --- a/docs/999-big-list-of-options.md +++ b/docs/999-big-list-of-options.md @@ -702,7 +702,7 @@ Default: `false` If this option is provided, bundling will not fail if bindings are imported from a file that does not define these bindings. Instead, new variables will be created for these bindings with the value `undefined`. #### treeshake -Type: `boolean | { propertyReadSideEffects?: boolean, pureExternalModules?: boolean }`
+Type: `boolean | { propertyReadSideEffects?: boolean, annotations?: boolean, pureExternalModules?: boolean }`
CLI: `--treeshake`/`--no-treeshake`
Default: `true` @@ -728,6 +728,25 @@ const result = foo.bar; const illegalAccess = foo.quux.tooDeep; ``` +**treeshake.annotations**
+Type: `boolean`
+CLI: `--treeshake.annotations`/`--no-treeshake.annotations`
+Default: `true` + +If `false`, ignore hints from pure annotations, i.e. comments containing `@__PURE__` or `#__PURE__`, when determining side-effects of function calls and constructor invocations. These annotations need to immediately precede the call invocation to take effect. The following code will be completely removed unless this option is set to `false`, in which case it will remain unchanged. + +```javascript +/*@__PURE__*/console.log('side-effect'); + +class Impure { + constructor() { + console.log('side-effect') + } +} + +/*@__PURE__*/new Impure(); +``` + **treeshake.pureExternalModules**
Type: `boolean`
CLI: `--treeshake.pureExternalModules`/`--no-treeshake.pureExternalModules`
diff --git a/package-lock.json b/package-lock.json index a01524a58f9..6b610307d80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -162,9 +162,9 @@ "dev": true }, "@types/node": { - "version": "11.9.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.4.tgz", - "integrity": "sha512-Zl8dGvAcEmadgs1tmSPcvwzO1YRsz38bVJQvH1RvRqSR9/5n61Q1ktcDL0ht3FXWR+ZpVmXVwN1LuH4Ax23NsA==" + "version": "11.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.5.tgz", + "integrity": "sha512-vVjM0SVzgaOUpflq4GYBvCpozes8OgIIS5gVXVka+OfK3hvnkC1i93U8WiY2OtNE4XUWyyy/86Kf6e0IHTQw1Q==" }, "@types/pretty-ms": { "version": "4.0.0", @@ -207,10 +207,16 @@ "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", "dev": true }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "dev": true + }, "ajv": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", - "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz", + "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", @@ -276,6 +282,17 @@ "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } } }, "argparse": { @@ -599,6 +616,12 @@ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==", "dev": true }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -617,9 +640,9 @@ "dev": true }, "chokidar": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.1.tgz", - "integrity": "sha512-gfw3p2oQV2wEt+8VuMlNsPjCxDxvvgnm/kz+uATu805mWVF8IJN7uz9DN7iBz+RMJISmiVbCOBFs9qBGMjtPfQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz", + "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -634,14 +657,6 @@ "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", "upath": "^1.1.0" - }, - "dependencies": { - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - } } }, "ci-info": { @@ -741,6 +756,66 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + } + } + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -875,6 +950,12 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -899,6 +980,15 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -962,6 +1052,12 @@ } } }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -1013,6 +1109,31 @@ "is-arrayish": "^0.2.1" } }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", @@ -1063,9 +1184,9 @@ } }, "eslint": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.14.0.tgz", - "integrity": "sha512-jrOhiYyENRrRnWlMYANlGZTqb89r2FuRT+615AabBoajhNjeh9ywDNlh2LU9vTqf0WYN+L3xdXuIi7xuj/tK9w==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.14.1.tgz", + "integrity": "sha512-CyUMbmsjxedx8B0mr79mNOqetvkbij/zrXnFeK2zc3pGRn3/tibjiNAv/3UxFEyfMDjh+ZqTrJrEGBFiGfD5Og==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -1317,6 +1438,15 @@ } } }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -1488,6 +1618,29 @@ "locate-path": "^2.0.0" } }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, "fixturify": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/fixturify/-/fixturify-0.3.4.tgz", @@ -1498,6 +1651,23 @@ "matcher-collection": "^1.0.4" } }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true + } + } + }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -2137,6 +2307,12 @@ "simple-git": "^1.85.0" } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, "get-own-enumerable-property-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", @@ -2199,6 +2375,30 @@ } } }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, "globals": { "version": "11.11.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", @@ -2291,6 +2491,12 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -2334,11 +2540,20 @@ } }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", @@ -2587,6 +2802,12 @@ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", "dev": true }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -2637,6 +2858,12 @@ "builtin-modules": "^1.0.0" } }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, "is-ci": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", @@ -2666,6 +2893,12 @@ } } }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -2807,6 +3040,15 @@ "@types/estree": "0.0.39" } }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, "is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", @@ -2819,6 +3061,15 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -2992,6 +3243,15 @@ "graceful-fs": "^4.1.9" } }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -3262,6 +3522,15 @@ "sourcemap-codec": "^1.4.4" } }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -3291,18 +3560,18 @@ } }, "markdownlint": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.11.0.tgz", - "integrity": "sha512-wE5WdKD6zW2DQaPQ5TFBTXh5j76DnWd/IFffnDQgHmi6Y61DJXBDfLftZ/suJHuv6cwPjM6gKw2GaRLJMOR+Mg==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.12.0.tgz", + "integrity": "sha512-bjur6ZP0yKHVYh1U5+xD+bVkouKiUyiVzg9c9qkytYRW2nDfSowifKSmpPeO0uZHxzZOYMcV2Oe7sycPOEqMOQ==", "dev": true, "requires": { "markdown-it": "8.4.2" } }, "markdownlint-cli": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.13.0.tgz", - "integrity": "sha512-DCjhAIb4EDny6xdjOcu2Rckp1sOSktQU9PjRN2pK9p0Azby7YYBqZXO8I+vhv5XKd2Pk0IbbOhyZ/+zjfH4oww==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.14.0.tgz", + "integrity": "sha512-EE2YBEgw7W38eCXeOA79I2pv33Rr5hLAkRqZtXJtEbTHKMQtVaVGLD2Qb4RDekYXgXyuR+LRXFE947WlxOvLXQ==", "dev": true, "requires": { "commander": "~2.9.0", @@ -3312,7 +3581,7 @@ "js-yaml": "~3.11.0", "lodash.differencewith": "~4.5.0", "lodash.flatten": "~4.4.0", - "markdownlint": "~0.11.0", + "markdownlint": "~0.12.0", "minimatch": "~3.0.4", "rc": "~1.2.7" }, @@ -3368,6 +3637,17 @@ "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", "dev": true }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, "merge-stream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", @@ -3464,57 +3744,61 @@ } }, "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.0.2.tgz", + "integrity": "sha512-RtTJsmmToGyeTznSOMoM6TPEk1A84FQaHIciKrRqARZx+B5ccJ5tXlmJzEKGBxZdqk9UjpRsesZTUkZmR5YnuQ==", "dev": true, "requires": { + "ansi-colors": "3.2.3", "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", + "debug": "3.2.6", "diff": "3.5.0", "escape-string-regexp": "1.0.5", - "glob": "7.1.2", + "findup-sync": "2.0.0", + "glob": "7.1.3", "growl": "1.10.5", - "he": "1.1.1", + "he": "1.2.0", + "js-yaml": "3.12.0", + "log-symbols": "2.2.0", "minimatch": "3.0.4", "mkdirp": "0.5.1", - "supports-color": "5.4.0" + "ms": "2.1.1", + "node-environment-flags": "1.0.4", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "12.0.5", + "yargs-parser": "11.1.1", + "yargs-unparser": "1.5.0" }, "dependencies": { - "commander": { - "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", "dev": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -3572,6 +3856,15 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-environment-flags": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.4.tgz", + "integrity": "sha512-M9rwCnWVLW7PX+NUWe3ejEdiLYinRpsEre9hMkU/6NS4h+EEulYaDH1gCEZ2gyXsmw+RXYDaV2JkkTNcsPDJ0Q==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -3594,13 +3887,10 @@ } }, "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "npm-path": { "version": "2.0.4", @@ -3674,6 +3964,12 @@ } } }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", + "dev": true + }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -3683,6 +3979,28 @@ "isobject": "^3.0.0" } }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -3754,18 +4072,41 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", + "dev": true + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -3820,6 +4161,12 @@ "integrity": "sha512-AddiXFSLLCqj+tCRJ9MrUtHZB4DWojO3tk0NVZ+g5MaMQHF2+p2ktqxuoXyPFLljz/aUK0Nfhd/uGWnhXVXEyA==", "dev": true }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -4168,6 +4515,18 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, "require-relative": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", @@ -4189,6 +4548,16 @@ "path-parse": "^1.0.5" } }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4227,9 +4596,9 @@ } }, "rollup": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.2.0.tgz", - "integrity": "sha512-oHNv2NSX5y/rY6W8MuKW8BVQwpIfwfTNp4PJiQYR7cLcBYeR5wP0LsPqKNw3Heji6hYci5Nd53q/fVfTuNe3GA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.2.4.tgz", + "integrity": "sha512-usXjwEWL4alCogotkbGXICTdea+qwTdBXJtl59n9nzlXL2/wm6/rJskWqRI8YBkvm0ZuQjLTajzXOW75dlyd+g==", "dev": true, "requires": { "@types/estree": "0.0.39", @@ -4257,15 +4626,26 @@ } }, "rollup-plugin-commonjs": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.2.0.tgz", - "integrity": "sha512-0RM5U4Vd6iHjL6rLvr3lKBwnPsaVml+qxOGaaNUWN1lSq6S33KhITOfHmvxV3z2vy9Mk4t0g4rNlVaJJsNQPWA==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.2.1.tgz", + "integrity": "sha512-X0A/Cp/t+zbONFinBhiTZrfuUaVwRIp4xsbKq/2ohA2CDULa/7ONSJTelqxon+Vds2R2t2qJTqJQucKUC8GKkw==", "dev": true, "requires": { "estree-walker": "^0.5.2", "magic-string": "^0.25.1", - "resolve": "^1.8.1", + "resolve": "^1.10.0", "rollup-pluginutils": "^2.3.3" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } } }, "rollup-plugin-json": { @@ -4278,14 +4658,14 @@ } }, "rollup-plugin-node-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.0.0.tgz", - "integrity": "sha512-7Ni+/M5RPSUBfUaP9alwYQiIKnKeXCOHiqBpKUl9kwp3jX5ZJtgXAait1cne6pGEVUUztPD6skIKH9Kq9sNtfw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.0.1.tgz", + "integrity": "sha512-fSS7YDuCe0gYqKsr5OvxMloeZYUSgN43Ypi1WeRZzQcWtHgFayV5tUSPYpxuaioIIWaBXl6NrVk0T2/sKwueLg==", "dev": true, "requires": { "builtin-modules": "^3.0.0", "is-module": "^1.0.0", - "resolve": "^1.8.1" + "resolve": "^1.10.0" }, "dependencies": { "builtin-modules": { @@ -4293,6 +4673,15 @@ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.0.0.tgz", "integrity": "sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg==", "dev": true + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } } } }, @@ -4308,30 +4697,12 @@ } }, "rollup-plugin-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-string/-/rollup-plugin-string-2.0.2.tgz", - "integrity": "sha1-9TI6Is/XOLRQy+piq2WTcF6sdEs=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-string/-/rollup-plugin-string-3.0.0.tgz", + "integrity": "sha512-vqyzgn9QefAgeKi+Y4A7jETeIAU1zQmS6VotH6bzm/zmUQEnYkpIGRaOBPY41oiWYV4JyBoGAaBjYMYuv+6wVw==", "dev": true, "requires": { - "rollup-pluginutils": "^1.5.0" - }, - "dependencies": { - "estree-walker": { - "version": "0.2.1", - "resolved": "http://registry.npmjs.org/estree-walker/-/estree-walker-0.2.1.tgz", - "integrity": "sha1-va/oCVOD2EFNXcLs9MkXO225QS4=", - "dev": true - }, - "rollup-pluginutils": { - "version": "1.5.2", - "resolved": "http://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz", - "integrity": "sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg=", - "dev": true, - "requires": { - "estree-walker": "^0.2.1", - "minimatch": "^3.0.2" - } - } + "rollup-pluginutils": "^2.4.1" } }, "rollup-plugin-terser": { @@ -4357,14 +4728,13 @@ } }, "rollup-pluginutils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.4.0.tgz", - "integrity": "sha512-5FxKDfsReYRubQfrRYvX99HqC5qd1u2Gd8vyZ6Ee9ESvTm/C45RV/IVFIEddIXXbM1y/7x8t1WjsXfrSJACHkg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.4.1.tgz", + "integrity": "sha512-wesMQ9/172IJDIW/lYWm0vW0LiKe5Ekjws481R7z9WTRtmO59cqyM/2uUlxvf6yzm/fElFmHUobeQOYz46dZJw==", "dev": true, "requires": { "estree-walker": "^0.6.0", - "micromatch": "^3.1.10", - "tslint": "^5.12.1" + "micromatch": "^3.1.10" }, "dependencies": { "estree-walker": { @@ -4449,6 +4819,12 @@ "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==", "dev": true }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -5013,9 +5389,9 @@ "dev": true }, "tslint": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz", - "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.13.0.tgz", + "integrity": "sha512-ECOOQRxXCYnUUePG5h/+Z1Zouobk3KFpIHA9aKBB/nnMxs97S1JJPDGt5J4cGm1y9U9VmVlfboOxA8n1kSNzGw==", "dev": true, "requires": { "babel-code-frame": "^6.22.0", @@ -5026,6 +5402,7 @@ "glob": "^7.1.1", "js-yaml": "^3.7.0", "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", "resolve": "^1.3.2", "semver": "^5.3.0", "tslib": "^1.8.0", @@ -5057,15 +5434,15 @@ } }, "typescript": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3.tgz", - "integrity": "sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A==", + "version": "3.3.3333", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3333.tgz", + "integrity": "sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==", "dev": true }, "uc.micro": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz", - "integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, "uglify-js": { @@ -5250,6 +5627,21 @@ "isexe": "^2.0.0" } }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -5287,6 +5679,98 @@ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + } + }, "yup": { "version": "0.26.10", "resolved": "https://registry.npmjs.org/yup/-/yup-0.26.10.tgz", diff --git a/package.json b/package.json index d90033f5b57..3b117b03c18 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "homepage": "https://github.com/rollup/rollup", "dependencies": { "@types/estree": "0.0.39", - "@types/node": "*", + "@types/node": "^11.9.5", "acorn": "^6.1.0" }, "devDependencies": { @@ -71,12 +71,13 @@ "acorn-dynamic-import": "^4.0.0", "acorn-import-meta": "^1.0.0", "acorn-jsx": "^5.0.1", + "acorn-walk": "^6.1.1", "ansi-escapes": "^3.2.0", "buble": "^0.19.6", - "chokidar": "^2.1.1", - "console-group": "^0.3.1", + "chokidar": "^2.1.2", + "console-group": "^0.3.3", "date-time": "^2.1.0", - "eslint": "^5.14.0", + "eslint": "^5.14.1", "eslint-plugin-import": "^2.16.0", "execa": "^1.0.0", "fixturify": "^0.3.4", @@ -84,29 +85,29 @@ "husky": "^1.3.1", "immutable": "^4.0.0-rc.12", "is-reference": "^1.1.1", - "istanbul": "^0.4.3", + "istanbul": "^0.4.5", "lint-staged": "^8.1.4", "locate-character": "^2.0.5", "magic-string": "^0.25.2", - "markdownlint-cli": "^0.13.0", + "markdownlint-cli": "^0.14.0", "minimist": "^1.2.0", - "mocha": "^5.2.0", + "mocha": "^6.0.2", "prettier": "^1.16.4", "pretty-bytes": "^5.1.0", "pretty-ms": "^4.0.0", "remap-istanbul": "^0.13.0", "require-relative": "^0.8.7", - "rollup": "^1.2.0", + "rollup": "^1.2.4", "rollup-plugin-alias": "^1.5.1", "rollup-plugin-buble": "^0.19.6", - "rollup-plugin-commonjs": "^9.2.0", + "rollup-plugin-commonjs": "^9.2.1", "rollup-plugin-json": "^3.1.0", - "rollup-plugin-node-resolve": "^4.0.0", + "rollup-plugin-node-resolve": "^4.0.1", "rollup-plugin-replace": "^2.1.0", - "rollup-plugin-string": "^2.0.2", + "rollup-plugin-string": "^3.0.0", "rollup-plugin-terser": "^4.0.4", "rollup-plugin-typescript": "^1.0.0", - "rollup-pluginutils": "^2.4.0", + "rollup-pluginutils": "^2.4.1", "sander": "^0.6.0", "shx": "^0.3.2", "signal-exit": "^3.0.2", @@ -115,9 +116,9 @@ "sourcemap-codec": "^1.4.4", "terser": "^3.16.1", "tslib": "^1.9.3", - "tslint": "^5.12.1", + "tslint": "^5.13.0", "turbocolor": "^2.6.1", - "typescript": "^3.3.3", + "typescript": "^3.3.3333", "url-parse": "^1.4.4" }, "files": [ diff --git a/rollup.config.js b/rollup.config.js index 05162f0a784..127f73bbe24 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,7 +4,7 @@ import alias from 'rollup-plugin-alias'; import commonjs from 'rollup-plugin-commonjs'; import json from 'rollup-plugin-json'; import resolve from 'rollup-plugin-node-resolve'; -import string from 'rollup-plugin-string'; +import { string } from 'rollup-plugin-string'; import { terser } from 'rollup-plugin-terser'; import typescript from 'rollup-plugin-typescript'; import pkg from './package.json'; @@ -90,6 +90,10 @@ export default command => { ], // acorn needs to be external as some plugins rely on a shared acorn instance external: ['fs', 'path', 'events', 'module', 'util', 'crypto', 'acorn', 'tty', 'net', 'url'], + treeshake: { + pureExternalModules: true, + propertyReadSideEffects: false + }, output: [ { file: 'dist/rollup.js', format: 'cjs', sourcemap: true, banner }, { file: 'dist/rollup.es.js', format: 'esm', banner } @@ -108,6 +112,10 @@ export default command => { commonjs({ include: 'node_modules/**' }) ], external: ['fs', 'path', 'module', 'events', 'rollup', 'assert', 'os', 'util'], + treeshake: { + pureExternalModules: true, + propertyReadSideEffects: false + }, output: { file: 'bin/rollup', format: 'cjs', @@ -141,6 +149,10 @@ export default command => { commonjs(), terser({ module: true, output: { comments: 'some' } }) ], + treeshake: { + pureExternalModules: true, + propertyReadSideEffects: false + }, output: [ { file: 'dist/rollup.browser.js', format: 'umd', name: 'rollup', banner }, { file: 'dist/rollup.browser.es.js', format: 'esm', banner } diff --git a/src/Graph.ts b/src/Graph.ts index e369042092d..62b2c83a6e3 100644 --- a/src/Graph.ts +++ b/src/Graph.ts @@ -105,14 +105,14 @@ export default class Graph { this.treeshake = options.treeshake !== false; if (this.treeshake) { - this.treeshakingOptions = { - propertyReadSideEffects: options.treeshake - ? (options.treeshake).propertyReadSideEffects !== false - : true, - pureExternalModules: options.treeshake - ? (options.treeshake).pureExternalModules - : false - }; + this.treeshakingOptions = options.treeshake + ? { + propertyReadSideEffects: + (options.treeshake).propertyReadSideEffects !== false, + annotations: (options.treeshake).annotations !== false, + pureExternalModules: (options.treeshake).pureExternalModules + } + : { propertyReadSideEffects: true, annotations: true, pureExternalModules: false }; if (this.treeshakingOptions.pureExternalModules === true) { this.isPureExternalModule = () => true; } else if (typeof this.treeshakingOptions.pureExternalModules === 'function') { diff --git a/src/Module.ts b/src/Module.ts index a7827e92dc5..dc369fe36f6 100644 --- a/src/Module.ts +++ b/src/Module.ts @@ -34,6 +34,7 @@ import getCodeFrame from './utils/getCodeFrame'; import { getOriginalLocation } from './utils/getOriginalLocation'; import { makeLegal } from './utils/identifierHelpers'; import { basename, extname } from './utils/path'; +import { markPureCallExpressions } from './utils/pureComments'; import relativeId from './utils/relativeId'; import { RenderOptions } from './utils/renderHelpers'; import { SOURCEMAPPING_URL_RE } from './utils/sourceMappingURL'; @@ -76,6 +77,7 @@ export interface AstContext { addImport: (node: ImportDeclaration) => void; addImportMeta: (node: MetaProperty) => void; code: string; + deoptimizationTracker: EntityPathTracker; error: (props: RollupError, pos: number) => void; fileName: string; getAssetFileName: (assetId: string) => string; @@ -93,7 +95,7 @@ export interface AstContext { nodeConstructors: { [name: string]: typeof NodeBase }; preserveModules: boolean; propertyReadSideEffects: boolean; - deoptimizationTracker: EntityPathTracker; + annotations: boolean; traceExport: (name: string) => Variable; traceVariable: (name: string) => Variable; treeshake: boolean; @@ -231,6 +233,7 @@ export default class Module { this.esTreeAst = ( (ast || tryParse(this, this.graph.acornParser, this.graph.acornOptions)) ); + markPureCallExpressions(this.comments, this.esTreeAst); timeEnd('generate ast', 3); @@ -272,6 +275,7 @@ export default class Module { preserveModules: this.graph.preserveModules, propertyReadSideEffects: !this.graph.treeshake || this.graph.treeshakingOptions.propertyReadSideEffects, + annotations: this.graph.treeshake && this.graph.treeshakingOptions.annotations, deoptimizationTracker: this.graph.deoptimizationTracker, traceExport: this.getVariableForExportName.bind(this), traceVariable: this.traceVariable.bind(this), diff --git a/src/ast/nodes/CallExpression.ts b/src/ast/nodes/CallExpression.ts index 172bd2594ef..d143a47d1e4 100644 --- a/src/ast/nodes/CallExpression.ts +++ b/src/ast/nodes/CallExpression.ts @@ -26,6 +26,7 @@ export default class CallExpression extends NodeBase implements DeoptimizableEnt type: NodeType.tCallExpression; callee: ExpressionNode; arguments: (ExpressionNode | SpreadElement)[]; + annotatedPure?: boolean; private callOptions: CallOptions; @@ -138,6 +139,7 @@ export default class CallExpression extends NodeBase implements DeoptimizableEnt for (const argument of this.arguments) { if (argument.hasEffects(options)) return true; } + if (this.context.annotations && this.annotatedPure) return false; return ( this.callee.hasEffects(options) || this.callee.hasEffectsWhenCalledAtPath( diff --git a/src/ast/nodes/ConditionalExpression.ts b/src/ast/nodes/ConditionalExpression.ts index be693bcfc67..617ba72c4de 100644 --- a/src/ast/nodes/ConditionalExpression.ts +++ b/src/ast/nodes/ConditionalExpression.ts @@ -1,6 +1,7 @@ import MagicString from 'magic-string'; import { BLANK } from '../../utils/blank'; import { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers'; +import { removeAnnotations } from '../../utils/treeshakeNode'; import CallOptions from '../CallOptions'; import { DeoptimizableEntity } from '../DeoptimizableEntity'; import { ExecutionPathOptions } from '../ExecutionPathOptions'; @@ -160,6 +161,7 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz if (!this.test.included) { code.remove(this.start, this.usedBranch.start); code.remove(this.usedBranch.end, this.end); + removeAnnotations(this, code); this.usedBranch.render(code, options, { renderedParentType: renderedParentType || this.parent.type, isCalleeOfRenderedParent: renderedParentType diff --git a/src/ast/nodes/EmptyStatement.ts b/src/ast/nodes/EmptyStatement.ts index e4925e1cb44..fca9f6ce0c4 100644 --- a/src/ast/nodes/EmptyStatement.ts +++ b/src/ast/nodes/EmptyStatement.ts @@ -1,14 +1,10 @@ -import MagicString from 'magic-string'; -import { RenderOptions } from '../../utils/renderHelpers'; import * as NodeType from './NodeType'; import { StatementBase } from './shared/Node'; export default class EmptyStatement extends StatementBase { type: NodeType.tEmptyStatement; - render(code: MagicString, _options: RenderOptions) { - if (this.parent.type === NodeType.BlockStatement || this.parent.type === NodeType.Program) { - code.remove(this.start, this.end); - } + hasEffects(): boolean { + return false; } } diff --git a/src/ast/nodes/ExportAllDeclaration.ts b/src/ast/nodes/ExportAllDeclaration.ts index 29363f7d0aa..36ff9562e3e 100644 --- a/src/ast/nodes/ExportAllDeclaration.ts +++ b/src/ast/nodes/ExportAllDeclaration.ts @@ -11,6 +11,10 @@ export default class ExportAllDeclaration extends NodeBase { needsBoundaries: true; + hasEffects() { + return false; + } + initialise() { this.included = false; this.context.addExport(this); diff --git a/src/ast/nodes/ExportDefaultDeclaration.ts b/src/ast/nodes/ExportDefaultDeclaration.ts index cc9ce573401..b87580ec741 100644 --- a/src/ast/nodes/ExportDefaultDeclaration.ts +++ b/src/ast/nodes/ExportDefaultDeclaration.ts @@ -5,6 +5,7 @@ import { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers'; +import { treeshakeNode } from '../../utils/treeshakeNode'; import ModuleScope from '../scopes/ModuleScope'; import ExportDefaultVariable from '../variables/ExportDefaultVariable'; import ClassDeclaration, { isClassDeclaration } from './ClassDeclaration'; @@ -88,7 +89,7 @@ export default class ExportDefaultDeclaration extends NodeBase { `exports('${this.variable.exportName}', ${this.variable.getName()});` ); } else { - code.remove(start, end); + treeshakeNode(this, code, start, end); } return; } else if (this.variable.included) { diff --git a/src/ast/nodes/ExpressionStatement.ts b/src/ast/nodes/ExpressionStatement.ts index b07cc054fa0..0980c6fe9f0 100644 --- a/src/ast/nodes/ExpressionStatement.ts +++ b/src/ast/nodes/ExpressionStatement.ts @@ -1,10 +1,11 @@ import MagicString from 'magic-string'; import { RenderOptions } from '../../utils/renderHelpers'; import * as NodeType from './NodeType'; -import { StatementBase } from './shared/Node'; +import { ExpressionNode, StatementBase } from './shared/Node'; export default class ExpressionStatement extends StatementBase { directive?: string; + expression: ExpressionNode; initialise() { this.included = false; diff --git a/src/ast/nodes/IfStatement.ts b/src/ast/nodes/IfStatement.ts index c2eda61bffb..020326d9f5b 100644 --- a/src/ast/nodes/IfStatement.ts +++ b/src/ast/nodes/IfStatement.ts @@ -1,5 +1,6 @@ import MagicString from 'magic-string'; import { RenderOptions } from '../../utils/renderHelpers'; +import { removeAnnotations } from '../../utils/treeshakeNode'; import { DeoptimizableEntity } from '../DeoptimizableEntity'; import { ExecutionPathOptions } from '../ExecutionPathOptions'; import { EMPTY_IMMUTABLE_TRACKER } from '../utils/ImmutableEntityPathTracker'; @@ -83,6 +84,7 @@ export default class IfStatement extends StatementBase implements DeoptimizableE const singleRetainedBranch = this.testValue ? this.consequent : this.alternate; code.remove(this.start, singleRetainedBranch.start); code.remove(singleRetainedBranch.end, this.end); + removeAnnotations(this, code); singleRetainedBranch.render(code, options); } else { if (this.test.included) { diff --git a/src/ast/nodes/LogicalExpression.ts b/src/ast/nodes/LogicalExpression.ts index 91caf81426e..b5549f08378 100644 --- a/src/ast/nodes/LogicalExpression.ts +++ b/src/ast/nodes/LogicalExpression.ts @@ -1,6 +1,7 @@ import MagicString from 'magic-string'; import { BLANK } from '../../utils/blank'; import { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers'; +import { removeAnnotations } from '../../utils/treeshakeNode'; import CallOptions from '../CallOptions'; import { DeoptimizableEntity } from '../DeoptimizableEntity'; import { ExecutionPathOptions } from '../ExecutionPathOptions'; @@ -164,6 +165,7 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable if (!this.left.included || !this.right.included) { code.remove(this.start, this.usedBranch.start); code.remove(this.usedBranch.end, this.end); + removeAnnotations(this, code); this.usedBranch.render(code, options, { renderedParentType: renderedParentType || this.parent.type, isCalleeOfRenderedParent: renderedParentType diff --git a/src/ast/nodes/NewExpression.ts b/src/ast/nodes/NewExpression.ts index accc2d707a6..bb96071831c 100644 --- a/src/ast/nodes/NewExpression.ts +++ b/src/ast/nodes/NewExpression.ts @@ -8,6 +8,7 @@ export default class NewExpression extends NodeBase { type: NodeType.tNewExpression; callee: ExpressionNode; arguments: ExpressionNode[]; + annotatedPure?: boolean; private callOptions: CallOptions; @@ -23,6 +24,7 @@ export default class NewExpression extends NodeBase { for (const argument of this.arguments) { if (argument.hasEffects(options)) return true; } + if (this.annotatedPure) return false; return this.callee.hasEffectsWhenCalledAtPath( EMPTY_PATH, this.callOptions, diff --git a/src/ast/nodes/SequenceExpression.ts b/src/ast/nodes/SequenceExpression.ts index d06aae78965..522b917a004 100644 --- a/src/ast/nodes/SequenceExpression.ts +++ b/src/ast/nodes/SequenceExpression.ts @@ -5,6 +5,7 @@ import { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers'; +import { treeshakeNode } from '../../utils/treeshakeNode'; import CallOptions from '../CallOptions'; import { DeoptimizableEntity } from '../DeoptimizableEntity'; import { ExecutionPathOptions } from '../ExecutionPathOptions'; @@ -92,7 +93,7 @@ export default class SequenceExpression extends NodeBase { this.end )) { if (!node.included) { - code.remove(start, end); + treeshakeNode(node, code, start, end); continue; } includedNodes++; diff --git a/src/ast/nodes/shared/Node.ts b/src/ast/nodes/shared/Node.ts index 59c74fdf405..849c30ef7a3 100644 --- a/src/ast/nodes/shared/Node.ts +++ b/src/ast/nodes/shared/Node.ts @@ -1,6 +1,6 @@ import { locate } from 'locate-character'; import MagicString from 'magic-string'; -import { AstContext } from '../../../Module'; +import { AstContext, CommentDescription } from '../../../Module'; import { NodeRenderOptions, RenderOptions } from '../../../utils/renderHelpers'; import CallOptions from '../../CallOptions'; import { DeoptimizableEntity } from '../../DeoptimizableEntity'; @@ -19,15 +19,16 @@ export interface GenericEsTreeNode { } export interface Node extends Entity { + annotations?: CommentDescription[]; + context: AstContext; end: number; included: boolean; keys: string[]; - context: AstContext; + needsBoundaries?: boolean; parent: Node | { type?: string }; + preventChildBlockScope?: boolean; start: number; type: string; - needsBoundaries?: boolean; - preventChildBlockScope?: boolean; variable?: Variable | null; /** @@ -90,7 +91,6 @@ export class NodeBase implements ExpressionNode { constructor( esTreeNode: GenericEsTreeNode, - // we need to pass down the node constructors to avoid a circular dependency parent: Node | { type: string; context: AstContext }, parentScope: ChildScope ) { @@ -111,7 +111,7 @@ export class NodeBase implements ExpressionNode { bind() { for (const key of this.keys) { const value = (this)[key]; - if (value === null) continue; + if (value === null || key === 'annotations') continue; if (Array.isArray(value)) { for (const child of value) { if (child !== null) child.bind(); @@ -150,7 +150,7 @@ export class NodeBase implements ExpressionNode { hasEffects(options: ExecutionPathOptions): boolean { for (const key of this.keys) { const value = (this)[key]; - if (value === null) continue; + if (value === null || key === 'annotations') continue; if (Array.isArray(value)) { for (const child of value) { if (child !== null && child.hasEffects(options)) return true; @@ -180,7 +180,7 @@ export class NodeBase implements ExpressionNode { this.included = true; for (const key of this.keys) { const value = (this)[key]; - if (value === null) continue; + if (value === null || key === 'annotations') continue; if (Array.isArray(value)) { for (const child of value) { if (child !== null) child.include(includeAllChildrenRecursively); @@ -222,7 +222,7 @@ export class NodeBase implements ExpressionNode { // That way, we can override this function to add custom initialisation and then call super.parseNode if (this.hasOwnProperty(key)) continue; const value = esTreeNode[key]; - if (typeof value !== 'object' || value === null) { + if (typeof value !== 'object' || value === null || key === 'annotations') { (this)[key] = value; } else if (Array.isArray(value)) { (this)[key] = []; @@ -246,7 +246,7 @@ export class NodeBase implements ExpressionNode { render(code: MagicString, options: RenderOptions) { for (const key of this.keys) { const value = (this)[key]; - if (value === null) continue; + if (value === null || key === 'annotations') continue; if (Array.isArray(value)) { for (const child of value) { if (child !== null) child.render(code, options); diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 58e7830d020..8489f31d6f6 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -248,6 +248,7 @@ export interface Plugin { export interface TreeshakingOptions { propertyReadSideEffects: boolean; + annotations: boolean; pureExternalModules: boolean; } diff --git a/src/utils/pureComments.ts b/src/utils/pureComments.ts new file mode 100644 index 00000000000..406f540ae78 --- /dev/null +++ b/src/utils/pureComments.ts @@ -0,0 +1,47 @@ +import * as acorn from 'acorn'; +// @ts-ignore +import { base as basicWalker } from 'acorn-walk'; +import * as ESTree from 'estree'; +import { CommentDescription } from '../Module'; + +function handlePureAnnotationsOfNode( + node: ESTree.Node & acorn.Node, + state: { commentIndex: number; commentNodes: CommentDescription[] }, + type: string = node.type +) { + let commentNode = state.commentNodes[state.commentIndex]; + while (commentNode && node.start >= commentNode.end) { + markPureNode(node, commentNode); + commentNode = state.commentNodes[++state.commentIndex]; + } + if (commentNode && commentNode.end <= node.end) { + basicWalker[type](node, state, handlePureAnnotationsOfNode); + } +} + +function markPureNode( + node: ESTree.Node & { annotations?: CommentDescription[] }, + comment: CommentDescription +) { + if (node.annotations) { + node.annotations.push(comment); + } else { + node.annotations = [comment]; + } + if (node.type === 'ExpressionStatement') { + node = node.expression; + } + if (node.type === 'CallExpression' || node.type === 'NewExpression') { + (node).annotatedPure = true; + } +} + +const pureCommentRegex = /[@#]__PURE__/; +const isPureComment = (comment: CommentDescription) => pureCommentRegex.test(comment.text); + +export function markPureCallExpressions(comments: CommentDescription[], esTreeAst: ESTree.Program) { + handlePureAnnotationsOfNode(esTreeAst, { + commentNodes: comments.filter(isPureComment), + commentIndex: 0 + }); +} diff --git a/src/utils/renderHelpers.ts b/src/utils/renderHelpers.ts index 2d97e9cb038..dc11da21357 100644 --- a/src/utils/renderHelpers.ts +++ b/src/utils/renderHelpers.ts @@ -1,5 +1,6 @@ import MagicString from 'magic-string'; import { Node, StatementNode } from '../ast/nodes/shared/Node'; +import { treeshakeNode } from './treeshakeNode'; export interface RenderOptions { compact: boolean; @@ -104,7 +105,7 @@ export function renderStatementList( }) : currentNode.render(code, options); } else { - code.remove(currentNodeStart, nextNodeStart); + treeshakeNode(currentNode, code, currentNodeStart, nextNodeStart); } } else { currentNode.render(code, options); diff --git a/src/utils/treeshakeNode.ts b/src/utils/treeshakeNode.ts new file mode 100644 index 00000000000..13ac51d07ee --- /dev/null +++ b/src/utils/treeshakeNode.ts @@ -0,0 +1,27 @@ +import MagicString from 'magic-string'; +import * as NodeType from '../ast/nodes/NodeType'; +import { Node } from '../ast/nodes/shared/Node'; + +export function treeshakeNode(node: Node, code: MagicString, start: number, end: number) { + code.remove(start, end); + if (node.annotations) { + for (const annotation of node.annotations) { + if (annotation.start < start) { + code.remove(annotation.start, annotation.end); + } else { + return; + } + } + } +} + +export function removeAnnotations(node: Node, code: MagicString) { + if (!node.annotations && node.parent.type === NodeType.ExpressionStatement) { + node = node.parent as Node; + } + if (node.annotations) { + for (const annotation of node.annotations) { + code.remove(annotation.start, annotation.end); + } + } +} diff --git a/test/form/samples/pure-comment-line-break/_config.js b/test/form/samples/pure-comment-line-break/_config.js new file mode 100644 index 00000000000..3a1351bf2fd --- /dev/null +++ b/test/form/samples/pure-comment-line-break/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'adjust line-break handling when dealing with pure annotations' +}; diff --git a/test/form/samples/pure-comment-line-break/_expected.js b/test/form/samples/pure-comment-line-break/_expected.js new file mode 100644 index 00000000000..36fbc276e41 --- /dev/null +++ b/test/form/samples/pure-comment-line-break/_expected.js @@ -0,0 +1,37 @@ +const x = 'code'; +console.log('should remain impure'); + +console.log('should remain impure'); + +console.log('code'); +console.log('should remain impure'); + +console.log('code'); +console.log('should remain impure'); +console.log('should remain impure'); + +console.log('code'); +console.log('should remain impure'); + +console.log('code'), +console.log('should remain impure'); + +console.log('code'), +console.log('should remain impure'); + +console.log('should remain impure'); + +console.log('code'); +console.log('should remain impure'); + +console.log('should remain impure'); + +console.log('should remain impure'); + +console.log('code'); +console.log('should remain impure', x); + + +{ + console.log('should remain impure'); +} diff --git a/test/form/samples/pure-comment-line-break/dep.js b/test/form/samples/pure-comment-line-break/dep.js new file mode 100644 index 00000000000..724095977ba --- /dev/null +++ b/test/form/samples/pure-comment-line-break/dep.js @@ -0,0 +1,3 @@ +const x = 'code';//@__PURE__ +export default x; +console.log('should remain impure'); diff --git a/test/form/samples/pure-comment-line-break/main.js b/test/form/samples/pure-comment-line-break/main.js new file mode 100644 index 00000000000..a3a6d9a181c --- /dev/null +++ b/test/form/samples/pure-comment-line-break/main.js @@ -0,0 +1,44 @@ +/*@__PURE__*/ +(() => {})(); +console.log('should remain impure'); + +console.log('code');//@__PURE__ +(() => {})(); +console.log('should remain impure'); + +console.log('code')/*@__PURE__*/; +(() => {})(); +console.log('should remain impure'); + +(() => {})();//@__PURE__ +(() => {})(); +console.log('should remain impure'); + +console.log('code');/*@__PURE__*///@__PURE__ +/*@__PURE__*/ (() => {})(); +console.log('should remain impure'); + +console.log('code'),//@__PURE__ +(() => {})(),console.log('should remain impure'); + +console.log('code')/*@__PURE__*/, +(() => {})(),console.log('should remain impure'); + +(() => {})(),//@__PURE__ +(() => {})(),console.log('should remain impure'); + +console.log('code');//@__PURE__ +;console.log('should remain impure'); + +/*@__PURE__*/true && console.log('should remain impure'); + +/*@__PURE__*/true ? console.log('should remain impure') : console.log('code'); + +console.log('code');//@__PURE__ +import code from './dep.js'; +console.log('should remain impure', code); + +/*@__PURE__*/ +if (true) { + console.log('should remain impure'); +} diff --git a/test/form/samples/pure-comment-scenarios-complex/_config.js b/test/form/samples/pure-comment-scenarios-complex/_config.js new file mode 100644 index 00000000000..d045ae566bc --- /dev/null +++ b/test/form/samples/pure-comment-scenarios-complex/_config.js @@ -0,0 +1,5 @@ +// tests compiled from https://github.com/mishoo/UglifyJS2/blob/bcebacbb9e7ddac7d9c0e4ca2c7e0faf0e0bca7c/test/compress/issue-1261.js + +module.exports = { + description: 'correctly handles various advanced pure comment scenarios' +}; diff --git a/test/form/samples/pure-comment-scenarios-complex/_expected.js b/test/form/samples/pure-comment-scenarios-complex/_expected.js new file mode 100644 index 00000000000..9706f378bab --- /dev/null +++ b/test/form/samples/pure-comment-scenarios-complex/_expected.js @@ -0,0 +1,21 @@ +// pure top-level IIFE will be dropped + +// pure top-level IIFE assigned to unreferenced var will not be dropped +global.iife1 = /*@__PURE__*/(function() { + console.log("iife1"); + function iife1() {} + return iife1; +})(); + +// comment #__PURE__ comment +bar(), baz(), quux(); +a.b(), f.g(); +/* @__PURE__ */(function(){})() || true ? foo() : bar(); +foo(); +/* @__PURE__ */(function(){})() && false ? foo() : bar(); +bar(); +/* @__PURE__ */(function(){})() + "foo" ? bar() : baz(); +"foo" + /* @__PURE__ */(function(){})() ? bar() : baz(); +/* @__PURE__ */(function(){})() ? foo() : foo(); +[/* @__PURE__ */(function(){})()] ? foo() : bar(); +!{ foo: /* @__PURE__ */(function(){})() } ? bar() : baz(); diff --git a/test/form/samples/pure-comment-scenarios-complex/main.js b/test/form/samples/pure-comment-scenarios-complex/main.js new file mode 100644 index 00000000000..e2260078450 --- /dev/null +++ b/test/form/samples/pure-comment-scenarios-complex/main.js @@ -0,0 +1,46 @@ +// pure top-level IIFE will be dropped +// @__PURE__ - comment +(function() { + console.log("iife0"); +})(); + +// pure top-level IIFE assigned to unreferenced var will not be dropped +global.iife1 = /*@__PURE__*/(function() { + console.log("iife1"); + function iife1() {} + return iife1; +})(); + +(function(){ + // pure IIFE in function scope assigned to unreferenced var will be dropped + var iife2 = /*#__PURE__*/(function() { + console.log("iife2"); + function iife2() {} + return iife2; + })(); +})(); + +// pure top-level calls will be dropped regardless of the leading comments position +var MyClass = /*#__PURE__*//*@class*/(function(){ + function MyClass() {} + MyClass.prototype.method = function() {}; + return MyClass; +})(); + +// comment #__PURE__ comment +bar(); + +// comment #__PURE__ comment +bar(), baz(), quux(); +a.b(), /* @__PURE__ */ c.d.e(), f.g(); + +/* @__PURE__ */(function(){x})(), void/* @__PURE__ */(function(){y})(); +/* @__PURE__ */(function(){x})() || true ? foo() : bar(); +true || /* @__PURE__ */(function(){y})() ? foo() : bar(); +/* @__PURE__ */(function(){x})() && false ? foo() : bar(); +false && /* @__PURE__ */(function(){y})() ? foo() : bar(); +/* @__PURE__ */(function(){x})() + "foo" ? bar() : baz(); +"foo" + /* @__PURE__ */(function(){y})() ? bar() : baz(); +/* @__PURE__ */(function(){x})() ? foo() : foo(); +[/* @__PURE__ */(function(){x})()] ? foo() : bar(); +!{ foo: /* @__PURE__ */(function(){x})() } ? bar() : baz(); diff --git a/test/form/samples/pure-comment-scenarios-simple/_config.js b/test/form/samples/pure-comment-scenarios-simple/_config.js new file mode 100644 index 00000000000..71266b1bba0 --- /dev/null +++ b/test/form/samples/pure-comment-scenarios-simple/_config.js @@ -0,0 +1,5 @@ +// tests compiled from https://github.com/mishoo/UglifyJS2/blob/88c8f4e363e0d585b33ea29df560243d3dc74ce1/test/compress/pure_funcs.js + +module.exports = { + description: 'correctly handles various pure comment scenarios' +}; diff --git a/test/form/samples/pure-comment-scenarios-simple/_expected.js b/test/form/samples/pure-comment-scenarios-simple/_expected.js new file mode 100644 index 00000000000..4ff2aeb3e3e --- /dev/null +++ b/test/form/samples/pure-comment-scenarios-simple/_expected.js @@ -0,0 +1,37 @@ +// removed + +// can be simplified +(/*@__PURE__*/ x(), y()); +(/*@__PURE__*/ new x(), y()); +(w(), y()); +(w(), y()); +/*@__PURE__*/(g() || h())(x(), y()); +/*@__PURE__*/new (g() || h())(x(), y()); +(/*@__PURE__*/ (a() || b()))(c(), d()); +new (/*@__PURE__*/ (a() || b()))(c(), d()); +[ /*@__PURE__*/ x(), y() ]; +[ /*@__PURE__*/ new x(), y() ]; +[ w(), /*@__PURE__*/ x(), y() ]; +[ w(), /*@__PURE__*/ new x(), y() ]; + +// retained +(/*@__PURE__*/ a)(); +(/*@__PURE__*/ b)(1)(2)(3); +(/*@__PURE__*/ c(1))(2)(3); +(/*@__PURE__*/ d(1)(2))(3); +(/*@__PURE__*/ e).x(1).y(2).z(3); +(/*@__PURE__*/ f.x)(1).y(2).z(3); +(/*@__PURE__*/ g.x(1)).y(2).z(3); +(/*@__PURE__*/ h.x(1).y)(2).z(3); +(/*@__PURE__*/ i.x(1).y(2)).z(3); +(/*@__PURE__*/ j.x(1).y(2).z)(3); +new (/*@__PURE__*/ k)(); +new (/*@__PURE__*/ l)(1)(2)(3); +(/*@__PURE__*/ new m(1))(2)(3); +(/*@__PURE__*/ new n(1)(2))(3); +new (/*@__PURE__*/ o).x(1).y(2).z(3); +/* */ new (/*@__PURE__*/ p.x)(1).y(2).z(3); +(/*@__PURE__*/ new q.x(1)).y(2).z(3); +(/*@__PURE__*/ new r.x(1).y)(2).z(3); +(/*@__PURE__*/ new s.x(1).y(2)).z(3); +(/*@__PURE__*/ new t.x(1).y(2).z)(3); diff --git a/test/form/samples/pure-comment-scenarios-simple/main.js b/test/form/samples/pure-comment-scenarios-simple/main.js new file mode 100644 index 00000000000..80d5a4443e0 --- /dev/null +++ b/test/form/samples/pure-comment-scenarios-simple/main.js @@ -0,0 +1,71 @@ +// removed +/*@__PURE__*/ a(); +/*@__PURE__*/ (b()); +(/*@__PURE__*/ c()); +/*@__PURE__*/ d(1)(2)(3); +/*@__PURE__*/ (e(1))(2)(3); +/*@__PURE__*/ (f(1)(2))(3); +/*@__PURE__*/ (g(1)(2)(3)); +(/*@__PURE__*/ h(1)(2)(3)); +/*@__PURE__*/ i.x(1).y(2).z(3); +/*@__PURE__*/ (j.x)(1).y(2).z(3); +/*@__PURE__*/ (k.x(1)).y(2).z(3); +/*@__PURE__*/ (l.x(1).y)(2).z(3); +/*@__PURE__*/ (m.x(1).y(2)).z(3); +/*@__PURE__*/ (n.x(1).y(2).z)(3); +/*@__PURE__*/ (o.x(1).y(2).z(3)); +(/*@__PURE__*/ p.x(1).y(2).z(3)); +[ /*@__PURE__*/ q() ]; +/*@__PURE__*/ new r(); +/*@__PURE__*/ (new s()); +(/*@__PURE__*/ new t()); +/*@__PURE__*/ new u(1)(2)(3); +/*@__PURE__*/ new (v(1))(2)(3); +/*@__PURE__*/ new (w(1)(2))(3); +/*@__PURE__*/ new (x(1)(2)(3)); +(/*@__PURE__*/ new y(1)(2)(3)); +/*@__PURE__*/ new z.x(1).y(2).z(3); +/*@__PURE__*/ new (a.x)(1).y(2).z(3); +/*@__PURE__*/ new (b.x(1)).y(2).z(3); +/*@__PURE__*/ new (c.x(1).y)(2).z(3); +/*@__PURE__*/ new (d.x(1).y(2)).z(3); +/*@__PURE__*/ new (e.x(1).y(2).z)(3); +/*@__PURE__*/ new (f.x(1).y(2).z(3)); +(/*@__PURE__*/ new g.x(1).y(2).z(3)); +[ /*@__PURE__*/ new h() ]; + +// can be simplified +(/*@__PURE__*/ x(), y()); +(/*@__PURE__*/ new x(), y()); +(w(), /*@__PURE__*/ x(), y()); +(w(), /*@__PURE__*/ new x(), y()); +/*@__PURE__*/(g() || h())(x(), y()); +/*@__PURE__*/new (g() || h())(x(), y()); +(/*@__PURE__*/ (a() || b()))(c(), d()); +new (/*@__PURE__*/ (a() || b()))(c(), d()); +[ /*@__PURE__*/ x(), y() ]; +[ /*@__PURE__*/ new x(), y() ]; +[ w(), /*@__PURE__*/ x(), y() ]; +[ w(), /*@__PURE__*/ new x(), y() ]; + +// retained +(/*@__PURE__*/ a)(); +(/*@__PURE__*/ b)(1)(2)(3); +(/*@__PURE__*/ c(1))(2)(3); +(/*@__PURE__*/ d(1)(2))(3); +(/*@__PURE__*/ e).x(1).y(2).z(3); +(/*@__PURE__*/ f.x)(1).y(2).z(3); +(/*@__PURE__*/ g.x(1)).y(2).z(3); +(/*@__PURE__*/ h.x(1).y)(2).z(3); +(/*@__PURE__*/ i.x(1).y(2)).z(3); +(/*@__PURE__*/ j.x(1).y(2).z)(3); +new (/*@__PURE__*/ k)(); +new (/*@__PURE__*/ l)(1)(2)(3); +(/*@__PURE__*/ new m(1))(2)(3); +(/*@__PURE__*/ new n(1)(2))(3); +new (/*@__PURE__*/ o).x(1).y(2).z(3); +/* */ new (/*@__PURE__*/ p.x)(1).y(2).z(3); +(/*@__PURE__*/ new q.x(1)).y(2).z(3); +(/*@__PURE__*/ new r.x(1).y)(2).z(3); +(/*@__PURE__*/ new s.x(1).y(2)).z(3); +(/*@__PURE__*/ new t.x(1).y(2).z)(3); diff --git a/test/form/samples/pure-comments-disabled/_config.js b/test/form/samples/pure-comments-disabled/_config.js new file mode 100644 index 00000000000..bd647a41ced --- /dev/null +++ b/test/form/samples/pure-comments-disabled/_config.js @@ -0,0 +1,8 @@ +module.exports = { + description: 'does not rely on pure annotations if they are disabled', + options: { + treeshake: { + annotations: false + } + } +}; diff --git a/test/form/samples/pure-comments-disabled/_expected.js b/test/form/samples/pure-comments-disabled/_expected.js new file mode 100644 index 00000000000..069d154290f --- /dev/null +++ b/test/form/samples/pure-comments-disabled/_expected.js @@ -0,0 +1,5 @@ +// should be retained +/*@__PURE__*/ a(); + +console.log('code'); +console.log('should remain impure'); diff --git a/test/form/samples/pure-comments-disabled/main.js b/test/form/samples/pure-comments-disabled/main.js new file mode 100644 index 00000000000..b3c947ad6dd --- /dev/null +++ b/test/form/samples/pure-comments-disabled/main.js @@ -0,0 +1,6 @@ +// should be retained +/*@__PURE__*/ a(); + +console.log('code')/*@__PURE__*/; +/*@__PURE__*/(() => {})(); +console.log('should remain impure'); diff --git a/test/form/samples/pure-comments-multiple/_config.js b/test/form/samples/pure-comments-multiple/_config.js new file mode 100644 index 00000000000..3c29e89762d --- /dev/null +++ b/test/form/samples/pure-comments-multiple/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'does not associate multiple "pure" comments before a token with subsequent tokens' +}; diff --git a/test/form/samples/pure-comments-multiple/_expected.js b/test/form/samples/pure-comments-multiple/_expected.js new file mode 100644 index 00000000000..6fcbcf76933 --- /dev/null +++ b/test/form/samples/pure-comments-multiple/_expected.js @@ -0,0 +1 @@ +retained(); diff --git a/test/form/samples/pure-comments-multiple/main.js b/test/form/samples/pure-comments-multiple/main.js new file mode 100644 index 00000000000..efd5ba77bbb --- /dev/null +++ b/test/form/samples/pure-comments-multiple/main.js @@ -0,0 +1,2 @@ +/*#__PURE__*//*#__PURE__*//*#__PURE__*//*#__PURE__*//*#__PURE__*/ removed(); +retained(); diff --git a/test/function/samples/call-marked-pure/_config.js b/test/function/samples/call-marked-pure/_config.js new file mode 100644 index 00000000000..df52a595dcf --- /dev/null +++ b/test/function/samples/call-marked-pure/_config.js @@ -0,0 +1,20 @@ +const assert = require('assert'); + +module.exports = { + description: 'external function calls marked with pure comment do not have effects', + options: { + external: ['socks'] + }, + context: { + require(id) { + if (id === 'socks') { + return () => { + throw new Error('Not all socks were removed.'); + }; + } + } + }, + code(code) { + assert.ok(code.search(/socks\(\)/) === -1); + } +}; diff --git a/test/function/samples/call-marked-pure/main.js b/test/function/samples/call-marked-pure/main.js new file mode 100644 index 00000000000..7679d1897d7 --- /dev/null +++ b/test/function/samples/call-marked-pure/main.js @@ -0,0 +1,8 @@ +import socks from 'socks'; + +/*#__PURE__*/ socks(); +/* #__PURE__*/ socks(); +/*#__PURE__ */ socks(); +/* #__PURE__ */ socks(); +// #__PURE__ +socks();