From 859c938d1c69a7d9941626213122852989757c2a Mon Sep 17 00:00:00 2001 From: alex-dunn Date: Wed, 28 Jul 2021 10:54:59 +0100 Subject: [PATCH 1/5] Update AsyncComputed to work with Vue3 --- package-lock.json | 910 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 11 +- src/index.js | 15 +- src/util.js | 8 +- test/index.js | 349 +++++++++--------- 5 files changed, 1094 insertions(+), 199 deletions(-) diff --git a/package-lock.json b/package-lock.json index 304d977..a9986c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,12 @@ "@babel/types": "^7.7.4" } }, + "@babel/helper-validator-identifier": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", + "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "dev": true + }, "@babel/highlight": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", @@ -186,6 +192,52 @@ } } }, + "@mapbox/node-pre-gyp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz", + "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.1", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "tar": "^6.1.0" + }, + "dependencies": { + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "@textlint/ast-node-types": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-4.2.5.tgz", @@ -207,6 +259,12 @@ "unified": "^6.1.6" } }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, "@types/estree": { "version": "0.0.41", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.41.tgz", @@ -219,6 +277,107 @@ "integrity": "sha512-hx6zWtudh3Arsbl3cXay+JnkvVgCKzCWKv42C9J01N2T2np4h8w5X8u6Tpz5mj38kE3M9FM0Pazx8vKFFMnjLQ==", "dev": true }, + "@vue/compiler-core": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.5.tgz", + "integrity": "sha512-TXBhFinoBaXKDykJzY26UEuQU1K07FOp/0Ie+OXySqqk0bS0ZO7Xvl7UmiTUPYcLrWbxWBR7Bs/y55AI0MNc2Q==", + "dev": true, + "requires": { + "@babel/parser": "^7.12.0", + "@babel/types": "^7.12.0", + "@vue/shared": "3.1.5", + "estree-walker": "^2.0.1", + "source-map": "^0.6.1" + }, + "dependencies": { + "@babel/parser": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.8.tgz", + "integrity": "sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==", + "dev": true + }, + "@babel/types": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz", + "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.8", + "to-fast-properties": "^2.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@vue/compiler-dom": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.5.tgz", + "integrity": "sha512-ZsL3jqJ52OjGU/YiT/9XiuZAmWClKInZM2aFJh9gnsAPqOrj2JIELMbkIFpVKR/CrVO/f2VxfPiiQdQTr65jcQ==", + "dev": true, + "requires": { + "@vue/compiler-core": "3.1.5", + "@vue/shared": "3.1.5" + } + }, + "@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "dev": true, + "requires": { + "@vue/shared": "3.1.5" + } + }, + "@vue/runtime-core": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.1.5.tgz", + "integrity": "sha512-YQbG5cBktN1RowQDKA22itmvQ+b40f0WgQ6CXK4VYoYICAiAfu6Cc14777ve8zp1rJRGtk5oIeS149TOculrTg==", + "dev": true, + "requires": { + "@vue/reactivity": "3.1.5", + "@vue/shared": "3.1.5" + } + }, + "@vue/runtime-dom": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.1.5.tgz", + "integrity": "sha512-tNcf3JhVR0RfW0kw1p8xZgv30nvX8Y9rsz7eiQ0dHe273sfoCngAG0y4GvMaY4Xd8FsjUwFedd4suQ8Lu8meXg==", + "dev": true, + "requires": { + "@vue/runtime-core": "3.1.5", + "@vue/shared": "3.1.5", + "csstype": "^2.6.8" + } + }, + "@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "dev": true + }, + "@vue/test-utils": { + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.0.0-rc.10.tgz", + "integrity": "sha512-Z8jY+askU08svsI37NcJSLmWrfkZ/1ATA1DENWezRUX2uv3QyEj7idwx+rfeNSOrlNNBh4NTzypBKOUOklxBRA==", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -226,17 +385,67 @@ "dev": true }, "acorn": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", - "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, "acorn-jsx": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", "dev": true }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "ajv": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", @@ -296,6 +505,22 @@ "normalize-path": "^2.0.0" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1282,6 +1507,12 @@ "repeat-element": "^1.1.2" } }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, "browserslist": { "version": "3.2.8", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", @@ -1298,6 +1529,15 @@ "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", "dev": true }, + "bufferutil": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.3.tgz", + "integrity": "sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw==", + "dev": true, + "requires": { + "node-gyp-build": "^4.2.0" + } + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -1337,6 +1577,17 @@ "integrity": "sha512-yYQ2QfotceRiH4U+h1Us86WJXtVHDmy3nEKIdYPsZCYnOV5/tMgGbmoIlrMzmh2VXlproqYtVaKeGDBkMZifFA==", "dev": true }, + "canvas": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.8.0.tgz", + "integrity": "sha512-gLTi17X8WY9Cf5GZ2Yns8T5lfBOcGgFehDFb+JQwDqdOoBOcECS9ZWMEAqMSVcMYwXD659J8NyzjRY/2aE+C2Q==", + "dev": true, + "requires": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.14.0", + "simple-get": "^3.0.3" + } + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -1398,6 +1649,12 @@ "readdirp": "^2.0.0" } }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -1445,6 +1702,12 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, "collapse-white-space": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.5.tgz", @@ -1505,6 +1768,12 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, "contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", @@ -1573,6 +1842,35 @@ "which": "^1.2.9" } }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "csstype": { + "version": "2.6.17", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz", + "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -1582,6 +1880,17 @@ "assert-plus": "^1.0.0" } }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1591,6 +1900,12 @@ "ms": "2.0.0" } }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -1598,6 +1913,15 @@ "dev": true, "optional": true }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -1698,6 +2022,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", @@ -1707,6 +2037,12 @@ "repeating": "^2.0.0" } }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, "doctoc": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/doctoc/-/doctoc-1.4.0.tgz", @@ -1768,6 +2104,23 @@ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", "dev": true }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, "domhandler": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", @@ -2194,6 +2547,14 @@ "acorn": "^7.1.0", "acorn-jsx": "^5.1.0", "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } } }, "esprima": { @@ -2248,6 +2609,12 @@ "integrity": "sha1-0yOky15awzHOoDNBOpJT4WQ+B8Q=", "dev": true }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2508,6 +2875,15 @@ "map-cache": "^0.2.2" } }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, "fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", @@ -3090,6 +3466,44 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + } + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -3219,6 +3633,12 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -3301,6 +3721,15 @@ "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", "dev": true }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, "htmlparser2": { "version": "3.9.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", @@ -3315,6 +3744,34 @@ "readable-stream": "^2.0.2" } }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -3326,6 +3783,33 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3656,6 +4140,12 @@ "dev": true, "optional": true }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, "is-primitive": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", @@ -3776,6 +4266,109 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jsdom": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", + "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.5", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "dev": true + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + } + } + }, + "jsdom-global": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsdom-global/-/jsdom-global-3.0.2.tgz", + "integrity": "sha1-a9KZwTsMRiay2iwDk81DhdYGrLk=", + "dev": true + }, "jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", @@ -3893,6 +4486,32 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -3972,6 +4591,12 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -3987,6 +4612,25 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -4059,8 +4703,7 @@ "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true + "dev": true }, "nanomatch": { "version": "1.2.13", @@ -4123,6 +4766,18 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + }, + "node-gyp-build": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", + "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==", + "dev": true + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -4154,12 +4809,30 @@ "remove-trailing-separator": "^1.0.1" } }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -4435,6 +5108,12 @@ "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=", "dev": true }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -5209,6 +5888,14 @@ "@types/estree": "*", "@types/node": "*", "acorn": "^7.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } } }, "run-async": { @@ -5251,12 +5938,27 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "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.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -5303,6 +6005,23 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "dev": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", @@ -5705,6 +6424,12 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -5859,6 +6584,28 @@ } } }, + "tar": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.2.tgz", + "integrity": "sha512-EwKEgqJ7nJoS+s8QfLYVGMDmAsj+StbI2AM/RTHeUSsOw6Z8bwNBRv5z3CY0m7laC5qUAqruLX5AhMuc5deY3Q==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5960,6 +6707,15 @@ } } }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, "traverse": { "version": "0.6.6", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", @@ -6128,6 +6884,12 @@ "unist-util-is": "^3.0.0" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -6214,6 +6976,15 @@ "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", "dev": true }, + "utf-8-validate": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.5.tgz", + "integrity": "sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ==", + "dev": true, + "requires": { + "node-gyp-build": "^4.2.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6290,10 +7061,33 @@ } }, "vue": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz", - "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==", - "dev": true + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.1.5.tgz", + "integrity": "sha512-Ho7HNb1nfDoO+HVb6qYZgeaobt1XbY6KXFe4HGs1b9X6RhkWG/113n4/SrtM1LUclM6OrP/Se5aPHHvAPG1iVQ==", + "dev": true, + "requires": { + "@vue/compiler-dom": "3.1.5", + "@vue/runtime-dom": "3.1.5", + "@vue/shared": "3.1.5" + } + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } }, "watch": { "version": "1.0.2", @@ -6313,6 +7107,38 @@ } } }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, "which": { "version": "1.2.14", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", @@ -6322,6 +7148,48 @@ "isexe": "^2.0.0" } }, + "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" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -6349,17 +7217,41 @@ "mkdirp": "^0.5.1" } }, + "ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true + }, "x-is-string": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", "dev": true }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } } diff --git a/package.json b/package.json index ee98f3a..e3d1b39 100644 --- a/package.json +++ b/package.json @@ -52,14 +52,18 @@ }, "homepage": "https://github.com/foxbenjaminfox/vue-async-computed#readme", "peerDependencies": { - "vue": "~2" + "vue": "~3" }, "devDependencies": { + "@vue/test-utils": "^2.0.0-rc.10", + "acorn": "^6.4.2", "babel-cli": "^6.26.0", "babel-core": "^6.26.3", "babel-eslint": "^10.0.2", "babel-istanbul": "^0.12.2", "babel-preset-env": "^1.7.0", + "bufferutil": "^4.0.3", + "canvas": "^2.8.0", "coveralls": "^3.0.4", "doctoc": "^1.4.0", "eslint": "^6.8.0", @@ -69,12 +73,15 @@ "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", "estraverse-fb": "^1.3.2", + "jsdom": "16.6.0", + "jsdom-global": "3.0.2", "mkdirp": "^0.5.1", "rimraf": "^3.0.0", "rollup": "^1.27.14", "tap-spec": "^5.0.0", "tape": "^4.12.0", - "vue": "^2.5.21", + "utf-8-validate": "^5.0.5", + "vue": "^3.1.1", "watch": "^1.0.2" } } diff --git a/src/index.js b/src/index.js index 06991df..532bfb5 100644 --- a/src/index.js +++ b/src/index.js @@ -25,9 +25,10 @@ const AsyncComputed = { Vue.config .optionMergeStrategies - .asyncComputed = Vue.config.optionMergeStrategies.computed + .asyncComputed = (mixinStructure, componentStructure) => Object.assign({}, mixinStructure, componentStructure) Vue.mixin({ + name: "AsyncComputed", data () { return { _asyncComputed: {}, @@ -89,7 +90,7 @@ function handleAsyncComputedPropetyChanges (vm, key, pluginOptions, Vue) { if (thisPromise !== promiseId) return setAsyncState(vm, key, 'error') - Vue.set(vm.$data._asyncComputed[key], 'exception', err) + vm.$data._asyncComputed[key].exception = err if (pluginOptions.errorHandler === false) return const handler = (pluginOptions.errorHandler === undefined) @@ -103,14 +104,14 @@ function handleAsyncComputedPropetyChanges (vm, key, pluginOptions, Vue) { } }) } - Vue.set(vm.$data._asyncComputed, key, { + vm.$data._asyncComputed[key] = { exception: null, update: () => { if (!vm._isDestroyed) { watcher(getterOnly(vm.$options.asyncComputed[key]).apply(vm)) } } - }) + } setAsyncState(vm, key, 'updating') vm.$watch(prefix + key, watcher, { immediate: true }) } @@ -181,9 +182,3 @@ function generateDefault (fn, pluginOptions) { } export default AsyncComputed - -/* istanbul ignore if */ -if (typeof window !== 'undefined' && window.Vue) { - // Auto install in dist mode - window.Vue.use(AsyncComputed) -} diff --git a/src/util.js b/src/util.js index 03e1bcf..950b7b5 100644 --- a/src/util.js +++ b/src/util.js @@ -1,8 +1,8 @@ export function setAsyncState (vm, stateObject, state) { - vm.$set(vm.$data._asyncComputed[stateObject], 'state', state) - vm.$set(vm.$data._asyncComputed[stateObject], 'updating', state === 'updating') - vm.$set(vm.$data._asyncComputed[stateObject], 'error', state === 'error') - vm.$set(vm.$data._asyncComputed[stateObject], 'success', state === 'success') + vm.$data._asyncComputed[stateObject].state = state + vm.$data._asyncComputed[stateObject].updating = state === 'updating' + vm.$data._asyncComputed[stateObject].error = state === 'error' + vm.$data._asyncComputed[stateObject].success = state === 'success' } export function getterOnly (fn) { diff --git a/test/index.js b/test/index.js index 9a263ba..6c38425 100644 --- a/test/index.js +++ b/test/index.js @@ -1,6 +1,9 @@ +import 'jsdom-global/register' import test from "tape" -import AsyncComputed from "../src" -import Vue from 'vue' +import AsyncComputed from "../src/index.js" +import { mount } from '@vue/test-utils' + +global.SVGElement = global.Element const baseErrorCallback = () => { throw new Error('Unexpected error thrown') @@ -10,12 +13,16 @@ const pluginOptions = { errorHandler: msg => baseErrorCallback(msg), } -Vue.use(AsyncComputed, pluginOptions) - test("Async computed values are computed", t => { t.plan(4) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { + testtesttest () { + return new Promise(resolve => { + setTimeout(() => resolve('done'), 10) + }) + }, a () { return new Promise(resolve => { setTimeout(() => resolve('done'), 10) @@ -27,7 +34,8 @@ test("Async computed values are computed", t => { }) } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM + t.equal(vm.a, null) t.equal(vm.b, null) vm.$watch('a', function (val) { @@ -40,20 +48,23 @@ test("Async computed values are computed", t => { test("An async computed value which is an pre-resolved promise updates at the next tick", t => { t.plan(2) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a () { return Promise.resolve('done') } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM + t.equal(vm.a, null) - Vue.nextTick(() => t.equal(vm.a, 'done')) + vm.$nextTick(() => t.equal(vm.a, 'done')) }) test("Sync and async computed data work together", t => { t.plan(4) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a () { return new Promise(resolve => { @@ -66,7 +77,7 @@ test("Sync and async computed data work together", t => { return 0 } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, null) t.equal(vm.b, 0) @@ -79,7 +90,8 @@ test("Sync and async computed data work together", t => { test("Async values are properly recalculated", t => { t.plan(6) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a () { const data = this.x @@ -96,7 +108,7 @@ test("Async values are properly recalculated", t => { data: { x: 0 } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, null) t.equal(vm.x, 0) @@ -116,7 +128,8 @@ test("Async values are properly recalculated", t => { test("Old async values are properly invalidated", t => { t.plan(2) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a () { return new Promise(resolve => { @@ -127,7 +140,7 @@ test("Old async values are properly invalidated", t => { data: { waitTime: 40 } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, null) setTimeout(() => { vm.waitTime = 10 }, 10) vm.$watch('a', function (val) { @@ -137,7 +150,8 @@ test("Old async values are properly invalidated", t => { test("Having only sync computed data still works", t => { t.plan(2) - const vm = new Vue({ + const vm = mount({ + template: '
', computed: { a () { return this.x @@ -146,7 +160,7 @@ test("Having only sync computed data still works", t => { data: { x: 2 } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, 2) vm.$watch('a', function (val) { t.equal(val, 3) @@ -156,13 +170,14 @@ test("Having only sync computed data still works", t => { test("Errors in computed properties are handled", t => { t.plan(3) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a () { return Promise.reject(new Error('error')) } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, null) pluginOptions.errorHandler = stack => { t.equal(vm.a, null) @@ -174,14 +189,15 @@ test("Errors in computed properties are handled", t => { test("Errors in computed properties are handled, with useRawError", t => { pluginOptions.useRawError = true t.plan(3) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a () { // eslint-disable-next-line prefer-promise-reject-errors return Promise.reject('error') } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, null) pluginOptions.errorHandler = err => { t.equal(vm.a, null) @@ -193,7 +209,8 @@ test("Errors in computed properties are handled, with useRawError", t => { test("Multiple asyncComputed objects are handled the same as normal computed property objects", t => { t.plan(3) - const vm = new Vue({ + const vm = mount({ + template: '
', mixins: [{ asyncComputed: { a () { @@ -212,8 +229,9 @@ test("Multiple asyncComputed objects are handled the same as normal computed pro return Promise.resolve('vm-c') } } - }) - Vue.nextTick(() => { + }, { global: { mixins: [], plugins: [[AsyncComputed, pluginOptions]] } }).componentVM + + vm.$nextTick(() => { t.equal(vm.a, 'vm-a') t.equal(vm.b, 'mixin-b') t.equal(vm.c, 'vm-c') @@ -222,7 +240,8 @@ test("Multiple asyncComputed objects are handled the same as normal computed pro test("Async computed values can have defaults", t => { t.plan(8) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { x: { default: false, @@ -256,12 +275,12 @@ test("Async computed values can have defaults", t => { return this.x }, }, - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM const computed = vm.computedFromX// Force computed execution t.equal(vm.x, false, 'x should default to false') t.equal(vm.y, null, 'y doesn\'t have a default') t.equal(vm.z, null, 'z doesn\'t have a default despite being defined with an object') - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.x, true, 'x resolves to true') t.equal(vm.y, true, 'y resolves to true') t.equal(vm.z, true, 'z resolves to true') @@ -270,7 +289,8 @@ test("Async computed values can have defaults", t => { test("Default values can be functions", t => { t.plan(4) - const vm = new Vue({ + const vm = mount({ + template: '
', data: { x: 1 }, @@ -288,10 +308,10 @@ test("Default values can be functions", t => { } } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.y, 2) t.equal(vm.z, 1) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.y, 3) t.equal(vm.z, 4) }) @@ -299,7 +319,8 @@ test("Default values can be functions", t => { test("Async computed values can be written to, and then will be properly overridden", t => { t.plan(5) - const vm = new Vue({ + const vm = mount({ + template: '
', data: { x: 1 }, @@ -311,15 +332,15 @@ test("Async computed values can be written to, and then will be properly overrid }) } } - }) - Vue.nextTick(() => { + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM + vm.$nextTick(() => { t.equal(vm.y, 2) const unwatch = vm.$watch('y', function (val) { t.equal(val, 1) unwatch() vm.x = 4 t.equal(vm.y, 1) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.y, 5) vm.$watch('y', function (val) { t.equal(val, 4) @@ -332,7 +353,8 @@ test("Async computed values can be written to, and then will be properly overrid test("Watchers rerun the computation when a value changes", t => { t.plan(4) let i = 0 - const vm = new Vue({ + const vm = mount({ + template: '
', data: { x: 0, y: 2, @@ -348,18 +370,18 @@ test("Watchers rerun the computation when a value changes", t => { } } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.z, null) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.z, 2) i++ - vm.x-- - Vue.nextTick(() => { + vm.$nextTick(() => { // This tick, Vue registers the change // in the watcher, and reevaluates // the getter function t.equal(vm.z, 2) - Vue.nextTick(() => { + vm.x-- + vm.$nextTick(() => { // Now in this tick the promise has // resolved, and z is 3. t.equal(vm.z, 3) @@ -371,7 +393,8 @@ test("Watchers rerun the computation when a value changes", t => { test("shouldUpdate controls when to rerun the computation when a value changes", t => { t.plan(6) let i = 0 - const vm = new Vue({ + const vm = mount({ + template: '
', data: { x: 0, y: 2, @@ -386,32 +409,32 @@ test("shouldUpdate controls when to rerun the computation when a value changes", } } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.z, null) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.z, 2) i++ // update x so it will be 1 // should update returns false now vm.x++ - Vue.nextTick(() => { + vm.$nextTick(() => { // This tick, Vue registers the change // in the watcher, and reevaluates // the getter function t.equal(vm.z, 2) - Vue.nextTick(() => { + vm.$nextTick(() => { // Now in this tick the promise has // resolved, and z is 2 since should update returned false. t.equal(vm.z, 2) // update x so it will be 2 // should update returns true now - vm.x++ - Vue.nextTick(() => { + vm.$nextTick(() => { // This tick, Vue registers the change // in the watcher, and reevaluates // the getter function t.equal(vm.z, 2) - Vue.nextTick(() => { + vm.x++ + vm.$nextTick(() => { // Now in this tick the promise has // resolved, and z is 3. t.equal(vm.z, 3) @@ -425,7 +448,8 @@ test("shouldUpdate controls when to rerun the computation when a value changes", test("Watchers trigger but shouldUpdate can still block their updates", t => { t.plan(6) let i = 0 - const vm = new Vue({ + const vm = mount({ + template: '
', data: { canUpdate: true, x: 0, @@ -445,18 +469,18 @@ test("Watchers trigger but shouldUpdate can still block their updates", t => { } } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.z, null) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.z, 2) i++ - vm.x-- - Vue.nextTick(() => { + vm.$nextTick(() => { // This tick, Vue registers the change // in the watcher, and reevaluates // the getter function t.equal(vm.z, 2) - Vue.nextTick(() => { + vm.x-- + vm.$nextTick(() => { // Now in this tick the promise has // resolved, and z is 3. t.equal(vm.z, 3) @@ -464,12 +488,12 @@ test("Watchers trigger but shouldUpdate can still block their updates", t => { vm.canUpdate = false i++ vm.x-- - Vue.nextTick(() => { + vm.$nextTick(() => { // This tick, Vue registers the change // in the watcher, and reevaluates // the getter function but no update t.equal(vm.z, 3) - Vue.nextTick(() => { + vm.$nextTick(() => { // Now in this tick the promise has // resolved, and z is still 3. t.equal(vm.z, 3) @@ -483,15 +507,16 @@ test("Watchers trigger but shouldUpdate can still block their updates", t => { test("The default default value can be set in the plugin options", t => { t.plan(2) pluginOptions.default = 53 - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { x () { return Promise.resolve(0) } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.x, 53) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.x, 0) delete pluginOptions.default }) @@ -500,15 +525,16 @@ test("The default default value can be set in the plugin options", t => { test("The default default value can be set to undefined in the plugin options", t => { t.plan(2) pluginOptions.default = undefined - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { x () { return Promise.resolve(0) } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.x, undefined) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.x, 0) delete pluginOptions.default }) @@ -516,32 +542,16 @@ test("The default default value can be set to undefined in the plugin options", test("Handle an async computed value returning synchronously", t => { t.plan(2) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { x () { return 1 } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.x, null) - Vue.nextTick(() => { - t.equal(vm.x, 1) - }) -}) - -test("Work correctly with Vue.extend", t => { - t.plan(2) - const SubVue = Vue.extend({ - asyncComputed: { - x () { - return Promise.resolve(1) - } - } - }) - const vm = new SubVue({}) - - t.equal(vm.x, null) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.x, 1) }) }) @@ -550,7 +560,8 @@ test("Async computed values can be calculated lazily", t => { t.plan(7) let called = false - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a: { lazy: true, @@ -560,17 +571,17 @@ test("Async computed values can be calculated lazily", t => { } } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(called, false) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(called, false) t.equal(vm.a, null) t.equal(vm.a, null) t.equal(called, false) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(called, true) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.a, 10) }) }) @@ -581,7 +592,8 @@ test("Async computed values aren't lazy with { lazy: false }", t => { t.plan(4) let called = false - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a: { lazy: false, @@ -591,11 +603,11 @@ test("Async computed values aren't lazy with { lazy: false }", t => { } } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(called, true) t.equal(vm.a, null) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(called, true) t.equal(vm.a, 10) }) @@ -605,7 +617,8 @@ test("Async computed values can be calculated lazily with a default", t => { t.plan(7) let called = false - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a: { lazy: true, @@ -616,17 +629,17 @@ test("Async computed values can be calculated lazily with a default", t => { } } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(called, false) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(called, false) t.equal(vm.a, 3) t.equal(vm.a, 3) t.equal(called, false) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(called, true) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.a, 4) }) }) @@ -635,7 +648,8 @@ test("Async computed values can be calculated lazily with a default", t => { test("Underscore prefixes work (issue #33)", t => { t.plan(4) - const vm = new Vue({ + const vm = mount({ + template: '
', computed: { sync_a () { return 1 @@ -662,7 +676,7 @@ test("Underscore prefixes work (issue #33)", t => { }) } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm._async_a, null) t.equal(vm.async_b, null) // _async_a is not reactive, because @@ -679,7 +693,8 @@ test("Underscore prefixes work (issue #33)", t => { test("shouldUpdate works with lazy", t => { t.plan(8) - const vm = new Vue({ + const vm = mount({ + template: '
', data: { a: 0, x: true, @@ -705,25 +720,25 @@ test("shouldUpdate works with lazy", t => { } } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.b, null) t.equal(vm.c, null) - Vue.nextTick(() => { - Vue.nextTick(() => { + vm.$nextTick(() => { + vm.$nextTick(() => { t.equal(vm.b, 0) t.equal(vm.c, null) vm.a++ - Vue.nextTick(() => { - Vue.nextTick(() => { + vm.$nextTick(() => { + vm.$nextTick(() => { t.equal(vm.b, 1) t.equal(vm.c, null) vm.x = false vm.y = true vm.a++ - Vue.nextTick(() => { - Vue.nextTick(() => { + vm.$nextTick(() => { + vm.$nextTick(() => { t.equal(vm.b, 1) t.equal(vm.c, 2) }) @@ -737,14 +752,16 @@ test("shouldUpdate works with lazy", t => { test("$asyncComputed is empty if there are no async computed properties", t => { t.plan(1) - const vm = new Vue({ - }) + const vm = mount({ + template: '
', + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.deepEqual(vm.$asyncComputed, {}) }) test("$asyncComputed[name] is created for all async computed properties", t => { t.plan(15) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a () { return Promise.resolve(1) @@ -753,7 +770,7 @@ test("$asyncComputed[name] is created for all async computed properties", t => { return Promise.resolve(2) } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.deepEqual(Object.keys(vm.$asyncComputed), ['a', 'b']) t.equal(vm.$asyncComputed.a.state, 'updating') t.equal(vm.$asyncComputed.b.state, 'updating') @@ -762,7 +779,7 @@ test("$asyncComputed[name] is created for all async computed properties", t => { t.equal(vm.$asyncComputed.a.error, false) t.equal(vm.$asyncComputed.a.exception, null) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.a, 1) t.equal(vm.b, 2) t.equal(vm.$asyncComputed.a.state, 'success') @@ -776,14 +793,15 @@ test("$asyncComputed[name] is created for all async computed properties", t => { test("$asyncComputed[name] handles errors and captures exceptions", t => { t.plan(7) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a () { // eslint-disable-next-line prefer-promise-reject-errors return Promise.reject('error-message') } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.$asyncComputed.a.state, 'updating') pluginOptions.errorHandler = stack => { t.equal(vm.a, null) @@ -799,7 +817,8 @@ test("$asyncComputed[name] handles errors and captures exceptions", t => { test("$asyncComputed[name].update triggers re-evaluation", t => { let valueToReturn = 1 t.plan(5) - const vm = new Vue({ + const vm = mount({ + template: '
', asyncComputed: { a () { return new Promise(resolve => { @@ -807,20 +826,20 @@ test("$asyncComputed[name].update triggers re-evaluation", t => { }) } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.a, 1) valueToReturn = 2 t.equal(vm.$asyncComputed.a.state, 'success') vm.$asyncComputed.a.update() t.equal(vm.$asyncComputed.a.state, 'updating') - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.a, 2) valueToReturn = 3 - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.a, 2) }) }) @@ -830,7 +849,8 @@ test("$asyncComputed[name].update triggers re-evaluation", t => { test("$asyncComputed[name].update has the correct execution context", t => { t.plan(8) let addedValue = 1 - const vm = new Vue({ + const vm = mount({ + template: '
', data () { return { valueToReturn: 1, @@ -850,9 +870,9 @@ test("$asyncComputed[name].update has the correct execution context", t => { }, }, }, - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM - Vue.nextTick(() => { + vm.$nextTick(() => { // case 1: a is a function t.equal(vm.a, 2) t.equal(vm.$asyncComputed.a.state, 'success') @@ -868,7 +888,7 @@ test("$asyncComputed[name].update has the correct execution context", t => { vm.$asyncComputed.b.update() t.equal(vm.$asyncComputed.b.state, 'updating') - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.a, 5) t.equal(vm.b, 5) }) @@ -877,20 +897,22 @@ test("$asyncComputed[name].update has the correct execution context", t => { test("Plain components with neither `data` nor `asyncComputed` still work (issue #50)", t => { t.plan(1) - const vm = new Vue({ + const vm = mount({ + template: '
', computed: { a () { return 1 } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, 1) }) test('Data of component still work as function and got vm', t => { t.plan(1) let _vmContext = null - const vm = new Vue({ + const vm = mount({ + template: '
', data (vmContext) { _vmContext = vmContext }, @@ -900,14 +922,15 @@ test('Data of component still work as function and got vm', t => { }, }, - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm, _vmContext) }) test("Watch as a function", t => { t.plan(4) let i = 0 - const vm = new Vue({ + const vm = mount({ + template: '
', data: { y: 2, obj: { @@ -925,18 +948,18 @@ test("Watch as a function", t => { } } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.z, null) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.z, 2) i++ - vm.obj.t-- - Vue.nextTick(() => { + vm.$nextTick(() => { // This tick, Vue registers the change // in the watcher, and reevaluates // the getter function t.equal(vm.z, 2) - Vue.nextTick(() => { + vm.obj.t-- + vm.$nextTick(() => { // Now in this tick the promise has // resolved, and z is 3. t.equal(vm.z, 3) @@ -948,7 +971,8 @@ test("Watch as a function", t => { test("Watchers as array with nested path rerun the computation when a value changes", t => { t.plan(4) let i = 0 - const vm = new Vue({ + const vm = mount({ + template: '
', data: { y: 2, obj: { @@ -963,18 +987,18 @@ test("Watchers as array with nested path rerun the computation when a value chan watch: ['obj.t'] } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.z, null) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.z, 2) i++ - vm.obj.t-- - Vue.nextTick(() => { + vm.$nextTick(() => { // This tick, Vue registers the change // in the watcher, and reevaluates // the getter function t.equal(vm.z, 2) - Vue.nextTick(() => { + vm.obj.t-- + vm.$nextTick(() => { // Now in this tick the promise has // resolved, and z is 3. t.equal(vm.z, 3) @@ -986,7 +1010,8 @@ test("Watchers as array with nested path rerun the computation when a value chan test("Watch as array with more then one value", t => { t.plan(5) let i = 0 - const vm = new Vue({ + const vm = mount({ + template: '
', data: { y: 2, obj: { @@ -1002,19 +1027,19 @@ test("Watch as array with more then one value", t => { watch: ['obj.t', 'r'] } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.z, null) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.z, 2) i++ // checking for nested property - vm.obj.t-- - Vue.nextTick(() => { + vm.$nextTick(() => { // This tick, Vue registers the change // in the watcher, and reevaluates // the getter function t.equal(vm.z, 2) - Vue.nextTick(() => { + vm.obj.t-- + vm.$nextTick(() => { // Now in this tick the promise has // resolved, and z is 3. t.equal(vm.z, 3) @@ -1022,8 +1047,8 @@ test("Watch as array with more then one value", t => { i++ // one level and multiple watchers vm.r-- - Vue.nextTick(() => { - Vue.nextTick(() => { + vm.$nextTick(() => { + vm.$nextTick(() => { t.equal(vm.z, 4) }) }) @@ -1034,7 +1059,8 @@ test("Watch as array with more then one value", t => { test("$asyncComputed[name].state resolves to 'success' even if the computed value is 0 (issue #75)", t => { t.plan(13) - const vm = new Vue({ + const vm = mount({ + template: '
', computed: { isUpdating () { return this.$asyncComputed.a.updating @@ -1048,7 +1074,7 @@ test("$asyncComputed[name].state resolves to 'success' even if the computed valu default: null } } - }) + }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.$asyncComputed.a.state, 'updating') t.equal(vm.$asyncComputed.a.updating, true) t.equal(vm.$asyncComputed.a.success, false) @@ -1056,7 +1082,7 @@ test("$asyncComputed[name].state resolves to 'success' even if the computed valu t.equal(vm.$asyncComputed.a.exception, null) t.equal(vm.isUpdating, true) - Vue.nextTick(() => { + vm.$nextTick(() => { t.equal(vm.a, 0) t.equal(vm.$asyncComputed.a.state, 'success') t.equal(vm.$asyncComputed.a.updating, false) @@ -1066,28 +1092,3 @@ test("$asyncComputed[name].state resolves to 'success' even if the computed valu t.equal(vm.isUpdating, false) }) }) - -test("$asyncComputed[name].update does nothing if called after the component is destroyed", t => { - t.plan(4) - let i = 0 - const vm = new Vue({ - asyncComputed: { - a: { - async get () { - return ++i - } - } - } - }) - - t.equal(vm.a, null) - Vue.nextTick(() => { - t.equal(vm.a, 1) - vm.$destroy() - vm.$asyncComputed.a.update() - Vue.nextTick(() => { - t.equal(i, 1) - t.equal(vm.a, 1) - }) - }) -}) From 9bb31782ff61dc74033f6a1de890099bcfcd7ae9 Mon Sep 17 00:00:00 2001 From: alex-dunn Date: Fri, 30 Jul 2021 10:57:00 +0100 Subject: [PATCH 2/5] Fix data merge issues --- src/constants.js | 8 ++++ src/index.js | 25 ++++++++---- src/util.js | 10 +++-- test/index.js | 102 +++++++++++++++++++++++++++++------------------ 4 files changed, 94 insertions(+), 51 deletions(-) create mode 100644 src/constants.js diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..20393fa --- /dev/null +++ b/src/constants.js @@ -0,0 +1,8 @@ + +export const dataPrefix = '_asyncComputed$' +export const computedPrefix = '_async_computed$' + +export default { + dataPrefix, + computedPrefix +} diff --git a/src/index.js b/src/index.js index 532bfb5..0b775e5 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,10 @@ import { getGetterWithShouldUpdate, shouldNotUpdate, } from './shouldUpdate' +import { + dataPrefix, + computedPrefix +} from './constants' const prefix = '_async_computed$' @@ -30,13 +34,18 @@ const AsyncComputed = { Vue.mixin({ name: "AsyncComputed", data () { - return { - _asyncComputed: {}, - } + return {} }, computed: { $asyncComputed () { - return this.$data._asyncComputed + return Object.entries(this.$data) + .reduce((acc, entry) => { + const key = entry[0] + if (key.startsWith(dataPrefix)) { + acc[key.substring(dataPrefix.length)] = entry[1] + } + return acc + }, {}) } }, beforeCreate () { @@ -46,7 +55,7 @@ const AsyncComputed = { for (const key in asyncComputed) { const getter = getterFn(key, asyncComputed[key]) - this.$options.computed[prefix + key] = getter + this.$options.computed[computedPrefix + key] = getter } this.$options.data = initDataWithAsyncComputed(this.$options, pluginOptions) @@ -90,7 +99,7 @@ function handleAsyncComputedPropetyChanges (vm, key, pluginOptions, Vue) { if (thisPromise !== promiseId) return setAsyncState(vm, key, 'error') - vm.$data._asyncComputed[key].exception = err + vm.$data[dataPrefix + key].exception = err if (pluginOptions.errorHandler === false) return const handler = (pluginOptions.errorHandler === undefined) @@ -104,7 +113,7 @@ function handleAsyncComputedPropetyChanges (vm, key, pluginOptions, Vue) { } }) } - vm.$data._asyncComputed[key] = { + vm.$data[dataPrefix + key] = { exception: null, update: () => { if (!vm._isDestroyed) { @@ -113,7 +122,7 @@ function handleAsyncComputedPropetyChanges (vm, key, pluginOptions, Vue) { } } setAsyncState(vm, key, 'updating') - vm.$watch(prefix + key, watcher, { immediate: true }) + vm.$watch(computedPrefix + key, watcher, { immediate: true }) } function initDataWithAsyncComputed (options, pluginOptions) { diff --git a/src/util.js b/src/util.js index 950b7b5..8ee42be 100644 --- a/src/util.js +++ b/src/util.js @@ -1,8 +1,10 @@ +import { dataPrefix } from './constants' + export function setAsyncState (vm, stateObject, state) { - vm.$data._asyncComputed[stateObject].state = state - vm.$data._asyncComputed[stateObject].updating = state === 'updating' - vm.$data._asyncComputed[stateObject].error = state === 'error' - vm.$data._asyncComputed[stateObject].success = state === 'success' + vm.$data[dataPrefix + stateObject].state = state + vm.$data[dataPrefix + stateObject].updating = state === 'updating' + vm.$data[dataPrefix + stateObject].error = state === 'error' + vm.$data[dataPrefix + stateObject].success = state === 'success' } export function getterOnly (fn) { diff --git a/test/index.js b/test/index.js index 6c38425..e47cdab 100644 --- a/test/index.js +++ b/test/index.js @@ -105,9 +105,11 @@ test("Async values are properly recalculated", t => { }) } }, - data: { - x: 0 - } + data: function () { + return { + x: 0 + } + }, }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, null) t.equal(vm.x, 0) @@ -137,8 +139,10 @@ test("Old async values are properly invalidated", t => { }) } }, - data: { - waitTime: 40 + data: function () { + return { + waitTime: 40 + } } }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, null) @@ -157,8 +161,10 @@ test("Having only sync computed data still works", t => { return this.x } }, - data: { - x: 2 + data: function () { + return { + x: 2 + } } }, { global: { plugins: [[AsyncComputed, pluginOptions]] } }).componentVM t.equal(vm.a, 2) @@ -291,8 +297,10 @@ test("Default values can be functions", t => { t.plan(4) const vm = mount({ template: '
', - data: { - x: 1 + data: function () { + return { + x: 1 + } }, asyncComputed: { y: { @@ -321,8 +329,10 @@ test("Async computed values can be written to, and then will be properly overrid t.plan(5) const vm = mount({ template: '
', - data: { - x: 1 + data: function () { + return { + x: 1 + } }, asyncComputed: { y () { @@ -355,9 +365,11 @@ test("Watchers rerun the computation when a value changes", t => { let i = 0 const vm = mount({ template: '
', - data: { - x: 0, - y: 2, + data: function () { + return { + x: 0, + y: 2, + } }, asyncComputed: { z: { @@ -395,9 +407,11 @@ test("shouldUpdate controls when to rerun the computation when a value changes", let i = 0 const vm = mount({ template: '
', - data: { - x: 0, - y: 2, + data: function () { + return { + x: 0, + y: 2, + } }, asyncComputed: { z: { @@ -450,10 +464,12 @@ test("Watchers trigger but shouldUpdate can still block their updates", t => { let i = 0 const vm = mount({ template: '
', - data: { - canUpdate: true, - x: 0, - y: 2, + data: function () { + return { + canUpdate: true, + x: 0, + y: 2, + } }, asyncComputed: { z: { @@ -695,10 +711,12 @@ test("shouldUpdate works with lazy", t => { t.plan(8) const vm = mount({ template: '
', - data: { - a: 0, - x: true, - y: false, + data: function () { + return { + a: 0, + x: true, + y: false, + } }, asyncComputed: { b: { @@ -931,10 +949,12 @@ test("Watch as a function", t => { let i = 0 const vm = mount({ template: '
', - data: { - y: 2, - obj: { - t: 0 + data: function () { + return { + y: 2, + obj: { + t: 0 + } } }, asyncComputed: { @@ -973,10 +993,12 @@ test("Watchers as array with nested path rerun the computation when a value chan let i = 0 const vm = mount({ template: '
', - data: { - y: 2, - obj: { - t: 0 + data: function () { + return { + y: 2, + obj: { + t: 0 + } } }, asyncComputed: { @@ -1012,12 +1034,14 @@ test("Watch as array with more then one value", t => { let i = 0 const vm = mount({ template: '
', - data: { - y: 2, - obj: { - t: 0 - }, - r: 0 + data: function () { + return { + y: 2, + obj: { + t: 0 + }, + r: 0 + } }, asyncComputed: { z: { From ca65952ac9e6d656321e0b833b43475b19b88b4a Mon Sep 17 00:00:00 2001 From: alex-dunn Date: Wed, 4 Aug 2021 08:55:06 +0100 Subject: [PATCH 3/5] Temporarily add dist --- .gitignore | 1 - dist/vue-async-computed.esm.esnext.js | 281 +++++++++++++++++++++++++ dist/vue-async-computed.esm.js | 281 +++++++++++++++++++++++++ dist/vue-async-computed.esnext.js | 289 ++++++++++++++++++++++++++ dist/vue-async-computed.js | 289 ++++++++++++++++++++++++++ 5 files changed, 1140 insertions(+), 1 deletion(-) create mode 100644 dist/vue-async-computed.esm.esnext.js create mode 100644 dist/vue-async-computed.esm.js create mode 100644 dist/vue-async-computed.esnext.js create mode 100644 dist/vue-async-computed.js diff --git a/.gitignore b/.gitignore index 1f2fa23..bb2670b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ logs *.log node_modules -dist tmp coverage npm-debug.log diff --git a/dist/vue-async-computed.esm.esnext.js b/dist/vue-async-computed.esm.esnext.js new file mode 100644 index 0000000..3ea615e --- /dev/null +++ b/dist/vue-async-computed.esm.esnext.js @@ -0,0 +1,281 @@ +const dataPrefix = '_asyncComputed$'; +const computedPrefix = '_async_computed$'; + +function setAsyncState (vm, stateObject, state) { + vm.$data[dataPrefix + stateObject].state = state; + vm.$data[dataPrefix + stateObject].updating = state === 'updating'; + vm.$data[dataPrefix + stateObject].error = state === 'error'; + vm.$data[dataPrefix + stateObject].success = state === 'success'; +} + +function getterOnly (fn) { + if (typeof fn === 'function') return fn + + return fn.get +} + +function hasOwnProperty (object, property) { + return Object.prototype.hasOwnProperty.call(object, property) +} + +function isComputedLazy (item) { + return hasOwnProperty(item, 'lazy') && item.lazy +} + +function isLazyActive (vm, key) { + return vm[lazyActivePrefix + key] +} + +const lazyActivePrefix = 'async_computed$lazy_active$', + lazyDataPrefix = 'async_computed$lazy_data$'; + +function initLazy (data, key, value) { + data[lazyActivePrefix + key] = false; + data[lazyDataPrefix + key] = value; +} + +function makeLazyComputed (key) { + return { + get () { + this[lazyActivePrefix + key] = true; + return this[lazyDataPrefix + key] + }, + set (value) { + this[lazyDataPrefix + key] = value; + } + } +} + +function silentSetLazy (vm, key, value) { + vm[lazyDataPrefix + key] = value; +} +function silentGetLazy (vm, key) { + return vm[lazyDataPrefix + key] +} + +const getGetterWatchedByArray = computedAsyncProperty => + function getter () { + computedAsyncProperty.watch.forEach(key => { + // Check if nested key is watched. + const splittedByDot = key.split('.'); + if (splittedByDot.length === 1) { + // If not, just access it. + // eslint-disable-next-line no-unused-expressions + this[key]; + } else { + // Access the nested propety. + try { + let start = this; + splittedByDot.forEach(part => { + start = start[part]; + }); + } catch (error) { + console.error('AsyncComputed: bad path: ', key); + throw error + } + } + }); + return computedAsyncProperty.get.call(this) + }; + +const getGetterWatchedByFunction = computedAsyncProperty => + function getter () { + computedAsyncProperty.watch.call(this); + return computedAsyncProperty.get.call(this) + }; + +function getWatchedGetter (computedAsyncProperty) { + if (typeof computedAsyncProperty.watch === 'function') { + return getGetterWatchedByFunction(computedAsyncProperty) + } else if (Array.isArray(computedAsyncProperty.watch)) { + computedAsyncProperty.watch.forEach(key => { + if (typeof key !== 'string') { + throw new Error('AsyncComputed: watch elemnts must be strings') + } + }); + return getGetterWatchedByArray(computedAsyncProperty) + } else { + throw Error('AsyncComputed: watch should be function or an array') + } +} + +const DidNotUpdate = typeof Symbol === 'function' ? Symbol('did-not-update') : {}; + +const getGetterWithShouldUpdate = (asyncProprety, currentGetter) => { + return function getter () { + return (asyncProprety.shouldUpdate.call(this)) + ? currentGetter.call(this) + : DidNotUpdate + } +}; + +const shouldNotUpdate = (value) => DidNotUpdate === value; + +const AsyncComputed = { + install (Vue, pluginOptions) { + pluginOptions = pluginOptions || {}; + + Vue.config + .optionMergeStrategies + .asyncComputed = (mixinStructure, componentStructure) => Object.assign({}, mixinStructure, componentStructure); + + Vue.mixin({ + name: "AsyncComputed", + data () { + return {} + }, + computed: { + $asyncComputed () { + return Object.entries(this.$data) + .reduce((acc, entry) => { + const key = entry[0]; + if (key.startsWith(dataPrefix)) { + acc[key.substring(dataPrefix.length)] = entry[1]; + } + return acc + }, {}) + } + }, + beforeCreate () { + const asyncComputed = this.$options.asyncComputed || {}; + + if (!Object.keys(asyncComputed).length) return + + for (const key in asyncComputed) { + const getter = getterFn(key, asyncComputed[key]); + this.$options.computed[computedPrefix + key] = getter; + } + + this.$options.data = initDataWithAsyncComputed(this.$options, pluginOptions); + }, + created () { + for (const key in this.$options.asyncComputed || {}) { + const item = this.$options.asyncComputed[key], + value = generateDefault.call(this, item, pluginOptions); + if (isComputedLazy(item)) { + silentSetLazy(this, key, value); + } else { + this[key] = value; + } + } + + for (const key in this.$options.asyncComputed || {}) { + handleAsyncComputedPropetyChanges(this, key, pluginOptions); + } + } + }); + } +}; + +function handleAsyncComputedPropetyChanges (vm, key, pluginOptions, Vue) { + let promiseId = 0; + const watcher = newPromise => { + const thisPromise = ++promiseId; + + if (shouldNotUpdate(newPromise)) return + + if (!newPromise || !newPromise.then) { + newPromise = Promise.resolve(newPromise); + } + setAsyncState(vm, key, 'updating'); + + newPromise.then(value => { + if (thisPromise !== promiseId) return + setAsyncState(vm, key, 'success'); + vm[key] = value; + }).catch(err => { + if (thisPromise !== promiseId) return + + setAsyncState(vm, key, 'error'); + vm.$data[dataPrefix + key].exception = err; + if (pluginOptions.errorHandler === false) return + + const handler = (pluginOptions.errorHandler === undefined) + ? console.error.bind(console, 'Error evaluating async computed property:') + : pluginOptions.errorHandler; + + if (pluginOptions.useRawError) { + handler(err, vm, err.stack); + } else { + handler(err.stack); + } + }); + }; + vm.$data[dataPrefix + key] = { + exception: null, + update: () => { + if (!vm._isDestroyed) { + watcher(getterOnly(vm.$options.asyncComputed[key]).apply(vm)); + } + } + }; + setAsyncState(vm, key, 'updating'); + vm.$watch(computedPrefix + key, watcher, { immediate: true }); +} + +function initDataWithAsyncComputed (options, pluginOptions) { + const optionData = options.data; + const asyncComputed = options.asyncComputed || {}; + + return function vueAsyncComputedInjectedDataFn (vm) { + const data = ((typeof optionData === 'function') + ? optionData.call(this, vm) + : optionData) || {}; + for (const key in asyncComputed) { + const item = this.$options.asyncComputed[key]; + + var value = generateDefault.call(this, item, pluginOptions); + if (isComputedLazy(item)) { + initLazy(data, key, value); + this.$options.computed[key] = makeLazyComputed(key); + } else { + data[key] = value; + } + } + return data + } +} + +function getterFn (key, fn) { + if (typeof fn === 'function') return fn + + let getter = fn.get; + + if (hasOwnProperty(fn, 'watch')) { + getter = getWatchedGetter(fn); + } + + if (hasOwnProperty(fn, 'shouldUpdate')) { + getter = getGetterWithShouldUpdate(fn, getter); + } + + if (isComputedLazy(fn)) { + const nonLazy = getter; + getter = function lazyGetter () { + if (isLazyActive(this, key)) { + return nonLazy.call(this) + } else { + return silentGetLazy(this, key) + } + }; + } + return getter +} + +function generateDefault (fn, pluginOptions) { + let defaultValue = null; + + if ('default' in fn) { + defaultValue = fn.default; + } else if ('default' in pluginOptions) { + defaultValue = pluginOptions.default; + } + + if (typeof defaultValue === 'function') { + return defaultValue.call(this) + } else { + return defaultValue + } +} + +export default AsyncComputed; diff --git a/dist/vue-async-computed.esm.js b/dist/vue-async-computed.esm.js new file mode 100644 index 0000000..572a5c8 --- /dev/null +++ b/dist/vue-async-computed.esm.js @@ -0,0 +1,281 @@ +var dataPrefix = '_asyncComputed$'; +var computedPrefix = '_async_computed$'; + +function setAsyncState(vm, stateObject, state) { + vm.$data[dataPrefix + stateObject].state = state; + vm.$data[dataPrefix + stateObject].updating = state === 'updating'; + vm.$data[dataPrefix + stateObject].error = state === 'error'; + vm.$data[dataPrefix + stateObject].success = state === 'success'; +} + +function getterOnly(fn) { + if (typeof fn === 'function') return fn; + + return fn.get; +} + +function hasOwnProperty(object, property) { + return Object.prototype.hasOwnProperty.call(object, property); +} + +function isComputedLazy(item) { + return hasOwnProperty(item, 'lazy') && item.lazy; +} + +function isLazyActive(vm, key) { + return vm[lazyActivePrefix + key]; +} + +var lazyActivePrefix = 'async_computed$lazy_active$', + lazyDataPrefix = 'async_computed$lazy_data$'; + +function initLazy(data, key, value) { + data[lazyActivePrefix + key] = false; + data[lazyDataPrefix + key] = value; +} + +function makeLazyComputed(key) { + return { + get: function get() { + this[lazyActivePrefix + key] = true; + return this[lazyDataPrefix + key]; + }, + set: function set(value) { + this[lazyDataPrefix + key] = value; + } + }; +} + +function silentSetLazy(vm, key, value) { + vm[lazyDataPrefix + key] = value; +} +function silentGetLazy(vm, key) { + return vm[lazyDataPrefix + key]; +} + +var getGetterWatchedByArray = function getGetterWatchedByArray(computedAsyncProperty) { + return function getter() { + var _this = this; + + computedAsyncProperty.watch.forEach(function (key) { + // Check if nested key is watched. + var splittedByDot = key.split('.'); + if (splittedByDot.length === 1) { + // If not, just access it. + // eslint-disable-next-line no-unused-expressions + _this[key]; + } else { + // Access the nested propety. + try { + var start = _this; + splittedByDot.forEach(function (part) { + start = start[part]; + }); + } catch (error) { + console.error('AsyncComputed: bad path: ', key); + throw error; + } + } + }); + return computedAsyncProperty.get.call(this); + }; +}; + +var getGetterWatchedByFunction = function getGetterWatchedByFunction(computedAsyncProperty) { + return function getter() { + computedAsyncProperty.watch.call(this); + return computedAsyncProperty.get.call(this); + }; +}; + +function getWatchedGetter(computedAsyncProperty) { + if (typeof computedAsyncProperty.watch === 'function') { + return getGetterWatchedByFunction(computedAsyncProperty); + } else if (Array.isArray(computedAsyncProperty.watch)) { + computedAsyncProperty.watch.forEach(function (key) { + if (typeof key !== 'string') { + throw new Error('AsyncComputed: watch elemnts must be strings'); + } + }); + return getGetterWatchedByArray(computedAsyncProperty); + } else { + throw Error('AsyncComputed: watch should be function or an array'); + } +} + +var DidNotUpdate = typeof Symbol === 'function' ? Symbol('did-not-update') : {}; + +var getGetterWithShouldUpdate = function getGetterWithShouldUpdate(asyncProprety, currentGetter) { + return function getter() { + return asyncProprety.shouldUpdate.call(this) ? currentGetter.call(this) : DidNotUpdate; + }; +}; + +var shouldNotUpdate = function shouldNotUpdate(value) { + return DidNotUpdate === value; +}; + +var AsyncComputed = { + install: function install(Vue, pluginOptions) { + pluginOptions = pluginOptions || {}; + + Vue.config.optionMergeStrategies.asyncComputed = function (mixinStructure, componentStructure) { + return Object.assign({}, mixinStructure, componentStructure); + }; + + Vue.mixin({ + name: "AsyncComputed", + data: function data() { + return {}; + }, + + computed: { + $asyncComputed: function $asyncComputed() { + return Object.entries(this.$data).reduce(function (acc, entry) { + var key = entry[0]; + if (key.startsWith(dataPrefix)) { + acc[key.substring(dataPrefix.length)] = entry[1]; + } + return acc; + }, {}); + } + }, + beforeCreate: function beforeCreate() { + var asyncComputed = this.$options.asyncComputed || {}; + + if (!Object.keys(asyncComputed).length) return; + + for (var key in asyncComputed) { + var getter = getterFn(key, asyncComputed[key]); + this.$options.computed[computedPrefix + key] = getter; + } + + this.$options.data = initDataWithAsyncComputed(this.$options, pluginOptions); + }, + created: function created() { + for (var key in this.$options.asyncComputed || {}) { + var item = this.$options.asyncComputed[key], + value = generateDefault.call(this, item, pluginOptions); + if (isComputedLazy(item)) { + silentSetLazy(this, key, value); + } else { + this[key] = value; + } + } + + for (var _key in this.$options.asyncComputed || {}) { + handleAsyncComputedPropetyChanges(this, _key, pluginOptions); + } + } + }); + } +}; + +function handleAsyncComputedPropetyChanges(vm, key, pluginOptions, Vue) { + var promiseId = 0; + var watcher = function watcher(newPromise) { + var thisPromise = ++promiseId; + + if (shouldNotUpdate(newPromise)) return; + + if (!newPromise || !newPromise.then) { + newPromise = Promise.resolve(newPromise); + } + setAsyncState(vm, key, 'updating'); + + newPromise.then(function (value) { + if (thisPromise !== promiseId) return; + setAsyncState(vm, key, 'success'); + vm[key] = value; + }).catch(function (err) { + if (thisPromise !== promiseId) return; + + setAsyncState(vm, key, 'error'); + vm.$data[dataPrefix + key].exception = err; + if (pluginOptions.errorHandler === false) return; + + var handler = pluginOptions.errorHandler === undefined ? console.error.bind(console, 'Error evaluating async computed property:') : pluginOptions.errorHandler; + + if (pluginOptions.useRawError) { + handler(err, vm, err.stack); + } else { + handler(err.stack); + } + }); + }; + vm.$data[dataPrefix + key] = { + exception: null, + update: function update() { + if (!vm._isDestroyed) { + watcher(getterOnly(vm.$options.asyncComputed[key]).apply(vm)); + } + } + }; + setAsyncState(vm, key, 'updating'); + vm.$watch(computedPrefix + key, watcher, { immediate: true }); +} + +function initDataWithAsyncComputed(options, pluginOptions) { + var optionData = options.data; + var asyncComputed = options.asyncComputed || {}; + + return function vueAsyncComputedInjectedDataFn(vm) { + var data = (typeof optionData === 'function' ? optionData.call(this, vm) : optionData) || {}; + for (var key in asyncComputed) { + var item = this.$options.asyncComputed[key]; + + var value = generateDefault.call(this, item, pluginOptions); + if (isComputedLazy(item)) { + initLazy(data, key, value); + this.$options.computed[key] = makeLazyComputed(key); + } else { + data[key] = value; + } + } + return data; + }; +} + +function getterFn(key, fn) { + if (typeof fn === 'function') return fn; + + var getter = fn.get; + + if (hasOwnProperty(fn, 'watch')) { + getter = getWatchedGetter(fn); + } + + if (hasOwnProperty(fn, 'shouldUpdate')) { + getter = getGetterWithShouldUpdate(fn, getter); + } + + if (isComputedLazy(fn)) { + var nonLazy = getter; + getter = function lazyGetter() { + if (isLazyActive(this, key)) { + return nonLazy.call(this); + } else { + return silentGetLazy(this, key); + } + }; + } + return getter; +} + +function generateDefault(fn, pluginOptions) { + var defaultValue = null; + + if ('default' in fn) { + defaultValue = fn.default; + } else if ('default' in pluginOptions) { + defaultValue = pluginOptions.default; + } + + if (typeof defaultValue === 'function') { + return defaultValue.call(this); + } else { + return defaultValue; + } +} + +export default AsyncComputed; diff --git a/dist/vue-async-computed.esnext.js b/dist/vue-async-computed.esnext.js new file mode 100644 index 0000000..1b0af3c --- /dev/null +++ b/dist/vue-async-computed.esnext.js @@ -0,0 +1,289 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.AsyncComputed = factory()); +}(this, (function () { 'use strict'; + + const dataPrefix = '_asyncComputed$'; + const computedPrefix = '_async_computed$'; + + function setAsyncState (vm, stateObject, state) { + vm.$data[dataPrefix + stateObject].state = state; + vm.$data[dataPrefix + stateObject].updating = state === 'updating'; + vm.$data[dataPrefix + stateObject].error = state === 'error'; + vm.$data[dataPrefix + stateObject].success = state === 'success'; + } + + function getterOnly (fn) { + if (typeof fn === 'function') return fn + + return fn.get + } + + function hasOwnProperty (object, property) { + return Object.prototype.hasOwnProperty.call(object, property) + } + + function isComputedLazy (item) { + return hasOwnProperty(item, 'lazy') && item.lazy + } + + function isLazyActive (vm, key) { + return vm[lazyActivePrefix + key] + } + + const lazyActivePrefix = 'async_computed$lazy_active$', + lazyDataPrefix = 'async_computed$lazy_data$'; + + function initLazy (data, key, value) { + data[lazyActivePrefix + key] = false; + data[lazyDataPrefix + key] = value; + } + + function makeLazyComputed (key) { + return { + get () { + this[lazyActivePrefix + key] = true; + return this[lazyDataPrefix + key] + }, + set (value) { + this[lazyDataPrefix + key] = value; + } + } + } + + function silentSetLazy (vm, key, value) { + vm[lazyDataPrefix + key] = value; + } + function silentGetLazy (vm, key) { + return vm[lazyDataPrefix + key] + } + + const getGetterWatchedByArray = computedAsyncProperty => + function getter () { + computedAsyncProperty.watch.forEach(key => { + // Check if nested key is watched. + const splittedByDot = key.split('.'); + if (splittedByDot.length === 1) { + // If not, just access it. + // eslint-disable-next-line no-unused-expressions + this[key]; + } else { + // Access the nested propety. + try { + let start = this; + splittedByDot.forEach(part => { + start = start[part]; + }); + } catch (error) { + console.error('AsyncComputed: bad path: ', key); + throw error + } + } + }); + return computedAsyncProperty.get.call(this) + }; + + const getGetterWatchedByFunction = computedAsyncProperty => + function getter () { + computedAsyncProperty.watch.call(this); + return computedAsyncProperty.get.call(this) + }; + + function getWatchedGetter (computedAsyncProperty) { + if (typeof computedAsyncProperty.watch === 'function') { + return getGetterWatchedByFunction(computedAsyncProperty) + } else if (Array.isArray(computedAsyncProperty.watch)) { + computedAsyncProperty.watch.forEach(key => { + if (typeof key !== 'string') { + throw new Error('AsyncComputed: watch elemnts must be strings') + } + }); + return getGetterWatchedByArray(computedAsyncProperty) + } else { + throw Error('AsyncComputed: watch should be function or an array') + } + } + + const DidNotUpdate = typeof Symbol === 'function' ? Symbol('did-not-update') : {}; + + const getGetterWithShouldUpdate = (asyncProprety, currentGetter) => { + return function getter () { + return (asyncProprety.shouldUpdate.call(this)) + ? currentGetter.call(this) + : DidNotUpdate + } + }; + + const shouldNotUpdate = (value) => DidNotUpdate === value; + + const AsyncComputed = { + install (Vue, pluginOptions) { + pluginOptions = pluginOptions || {}; + + Vue.config + .optionMergeStrategies + .asyncComputed = (mixinStructure, componentStructure) => Object.assign({}, mixinStructure, componentStructure); + + Vue.mixin({ + name: "AsyncComputed", + data () { + return {} + }, + computed: { + $asyncComputed () { + return Object.entries(this.$data) + .reduce((acc, entry) => { + const key = entry[0]; + if (key.startsWith(dataPrefix)) { + acc[key.substring(dataPrefix.length)] = entry[1]; + } + return acc + }, {}) + } + }, + beforeCreate () { + const asyncComputed = this.$options.asyncComputed || {}; + + if (!Object.keys(asyncComputed).length) return + + for (const key in asyncComputed) { + const getter = getterFn(key, asyncComputed[key]); + this.$options.computed[computedPrefix + key] = getter; + } + + this.$options.data = initDataWithAsyncComputed(this.$options, pluginOptions); + }, + created () { + for (const key in this.$options.asyncComputed || {}) { + const item = this.$options.asyncComputed[key], + value = generateDefault.call(this, item, pluginOptions); + if (isComputedLazy(item)) { + silentSetLazy(this, key, value); + } else { + this[key] = value; + } + } + + for (const key in this.$options.asyncComputed || {}) { + handleAsyncComputedPropetyChanges(this, key, pluginOptions); + } + } + }); + } + }; + + function handleAsyncComputedPropetyChanges (vm, key, pluginOptions, Vue) { + let promiseId = 0; + const watcher = newPromise => { + const thisPromise = ++promiseId; + + if (shouldNotUpdate(newPromise)) return + + if (!newPromise || !newPromise.then) { + newPromise = Promise.resolve(newPromise); + } + setAsyncState(vm, key, 'updating'); + + newPromise.then(value => { + if (thisPromise !== promiseId) return + setAsyncState(vm, key, 'success'); + vm[key] = value; + }).catch(err => { + if (thisPromise !== promiseId) return + + setAsyncState(vm, key, 'error'); + vm.$data[dataPrefix + key].exception = err; + if (pluginOptions.errorHandler === false) return + + const handler = (pluginOptions.errorHandler === undefined) + ? console.error.bind(console, 'Error evaluating async computed property:') + : pluginOptions.errorHandler; + + if (pluginOptions.useRawError) { + handler(err, vm, err.stack); + } else { + handler(err.stack); + } + }); + }; + vm.$data[dataPrefix + key] = { + exception: null, + update: () => { + if (!vm._isDestroyed) { + watcher(getterOnly(vm.$options.asyncComputed[key]).apply(vm)); + } + } + }; + setAsyncState(vm, key, 'updating'); + vm.$watch(computedPrefix + key, watcher, { immediate: true }); + } + + function initDataWithAsyncComputed (options, pluginOptions) { + const optionData = options.data; + const asyncComputed = options.asyncComputed || {}; + + return function vueAsyncComputedInjectedDataFn (vm) { + const data = ((typeof optionData === 'function') + ? optionData.call(this, vm) + : optionData) || {}; + for (const key in asyncComputed) { + const item = this.$options.asyncComputed[key]; + + var value = generateDefault.call(this, item, pluginOptions); + if (isComputedLazy(item)) { + initLazy(data, key, value); + this.$options.computed[key] = makeLazyComputed(key); + } else { + data[key] = value; + } + } + return data + } + } + + function getterFn (key, fn) { + if (typeof fn === 'function') return fn + + let getter = fn.get; + + if (hasOwnProperty(fn, 'watch')) { + getter = getWatchedGetter(fn); + } + + if (hasOwnProperty(fn, 'shouldUpdate')) { + getter = getGetterWithShouldUpdate(fn, getter); + } + + if (isComputedLazy(fn)) { + const nonLazy = getter; + getter = function lazyGetter () { + if (isLazyActive(this, key)) { + return nonLazy.call(this) + } else { + return silentGetLazy(this, key) + } + }; + } + return getter + } + + function generateDefault (fn, pluginOptions) { + let defaultValue = null; + + if ('default' in fn) { + defaultValue = fn.default; + } else if ('default' in pluginOptions) { + defaultValue = pluginOptions.default; + } + + if (typeof defaultValue === 'function') { + return defaultValue.call(this) + } else { + return defaultValue + } + } + + return AsyncComputed; + +}))); diff --git a/dist/vue-async-computed.js b/dist/vue-async-computed.js new file mode 100644 index 0000000..f89b220 --- /dev/null +++ b/dist/vue-async-computed.js @@ -0,0 +1,289 @@ +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +(function (global, factory) { + (typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.AsyncComputed = factory()); +})(this, function () { + 'use strict'; + + var dataPrefix = '_asyncComputed$'; + var computedPrefix = '_async_computed$'; + + function setAsyncState(vm, stateObject, state) { + vm.$data[dataPrefix + stateObject].state = state; + vm.$data[dataPrefix + stateObject].updating = state === 'updating'; + vm.$data[dataPrefix + stateObject].error = state === 'error'; + vm.$data[dataPrefix + stateObject].success = state === 'success'; + } + + function getterOnly(fn) { + if (typeof fn === 'function') return fn; + + return fn.get; + } + + function hasOwnProperty(object, property) { + return Object.prototype.hasOwnProperty.call(object, property); + } + + function isComputedLazy(item) { + return hasOwnProperty(item, 'lazy') && item.lazy; + } + + function isLazyActive(vm, key) { + return vm[lazyActivePrefix + key]; + } + + var lazyActivePrefix = 'async_computed$lazy_active$', + lazyDataPrefix = 'async_computed$lazy_data$'; + + function initLazy(data, key, value) { + data[lazyActivePrefix + key] = false; + data[lazyDataPrefix + key] = value; + } + + function makeLazyComputed(key) { + return { + get: function get() { + this[lazyActivePrefix + key] = true; + return this[lazyDataPrefix + key]; + }, + set: function set(value) { + this[lazyDataPrefix + key] = value; + } + }; + } + + function silentSetLazy(vm, key, value) { + vm[lazyDataPrefix + key] = value; + } + function silentGetLazy(vm, key) { + return vm[lazyDataPrefix + key]; + } + + var getGetterWatchedByArray = function getGetterWatchedByArray(computedAsyncProperty) { + return function getter() { + var _this = this; + + computedAsyncProperty.watch.forEach(function (key) { + // Check if nested key is watched. + var splittedByDot = key.split('.'); + if (splittedByDot.length === 1) { + // If not, just access it. + // eslint-disable-next-line no-unused-expressions + _this[key]; + } else { + // Access the nested propety. + try { + var start = _this; + splittedByDot.forEach(function (part) { + start = start[part]; + }); + } catch (error) { + console.error('AsyncComputed: bad path: ', key); + throw error; + } + } + }); + return computedAsyncProperty.get.call(this); + }; + }; + + var getGetterWatchedByFunction = function getGetterWatchedByFunction(computedAsyncProperty) { + return function getter() { + computedAsyncProperty.watch.call(this); + return computedAsyncProperty.get.call(this); + }; + }; + + function getWatchedGetter(computedAsyncProperty) { + if (typeof computedAsyncProperty.watch === 'function') { + return getGetterWatchedByFunction(computedAsyncProperty); + } else if (Array.isArray(computedAsyncProperty.watch)) { + computedAsyncProperty.watch.forEach(function (key) { + if (typeof key !== 'string') { + throw new Error('AsyncComputed: watch elemnts must be strings'); + } + }); + return getGetterWatchedByArray(computedAsyncProperty); + } else { + throw Error('AsyncComputed: watch should be function or an array'); + } + } + + var DidNotUpdate = typeof Symbol === 'function' ? Symbol('did-not-update') : {}; + + var getGetterWithShouldUpdate = function getGetterWithShouldUpdate(asyncProprety, currentGetter) { + return function getter() { + return asyncProprety.shouldUpdate.call(this) ? currentGetter.call(this) : DidNotUpdate; + }; + }; + + var shouldNotUpdate = function shouldNotUpdate(value) { + return DidNotUpdate === value; + }; + + var AsyncComputed = { + install: function install(Vue, pluginOptions) { + pluginOptions = pluginOptions || {}; + + Vue.config.optionMergeStrategies.asyncComputed = function (mixinStructure, componentStructure) { + return Object.assign({}, mixinStructure, componentStructure); + }; + + Vue.mixin({ + name: "AsyncComputed", + data: function data() { + return {}; + }, + + computed: { + $asyncComputed: function $asyncComputed() { + return Object.entries(this.$data).reduce(function (acc, entry) { + var key = entry[0]; + if (key.startsWith(dataPrefix)) { + acc[key.substring(dataPrefix.length)] = entry[1]; + } + return acc; + }, {}); + } + }, + beforeCreate: function beforeCreate() { + var asyncComputed = this.$options.asyncComputed || {}; + + if (!Object.keys(asyncComputed).length) return; + + for (var key in asyncComputed) { + var getter = getterFn(key, asyncComputed[key]); + this.$options.computed[computedPrefix + key] = getter; + } + + this.$options.data = initDataWithAsyncComputed(this.$options, pluginOptions); + }, + created: function created() { + for (var key in this.$options.asyncComputed || {}) { + var item = this.$options.asyncComputed[key], + value = generateDefault.call(this, item, pluginOptions); + if (isComputedLazy(item)) { + silentSetLazy(this, key, value); + } else { + this[key] = value; + } + } + + for (var _key in this.$options.asyncComputed || {}) { + handleAsyncComputedPropetyChanges(this, _key, pluginOptions); + } + } + }); + } + }; + + function handleAsyncComputedPropetyChanges(vm, key, pluginOptions, Vue) { + var promiseId = 0; + var watcher = function watcher(newPromise) { + var thisPromise = ++promiseId; + + if (shouldNotUpdate(newPromise)) return; + + if (!newPromise || !newPromise.then) { + newPromise = Promise.resolve(newPromise); + } + setAsyncState(vm, key, 'updating'); + + newPromise.then(function (value) { + if (thisPromise !== promiseId) return; + setAsyncState(vm, key, 'success'); + vm[key] = value; + }).catch(function (err) { + if (thisPromise !== promiseId) return; + + setAsyncState(vm, key, 'error'); + vm.$data[dataPrefix + key].exception = err; + if (pluginOptions.errorHandler === false) return; + + var handler = pluginOptions.errorHandler === undefined ? console.error.bind(console, 'Error evaluating async computed property:') : pluginOptions.errorHandler; + + if (pluginOptions.useRawError) { + handler(err, vm, err.stack); + } else { + handler(err.stack); + } + }); + }; + vm.$data[dataPrefix + key] = { + exception: null, + update: function update() { + if (!vm._isDestroyed) { + watcher(getterOnly(vm.$options.asyncComputed[key]).apply(vm)); + } + } + }; + setAsyncState(vm, key, 'updating'); + vm.$watch(computedPrefix + key, watcher, { immediate: true }); + } + + function initDataWithAsyncComputed(options, pluginOptions) { + var optionData = options.data; + var asyncComputed = options.asyncComputed || {}; + + return function vueAsyncComputedInjectedDataFn(vm) { + var data = (typeof optionData === 'function' ? optionData.call(this, vm) : optionData) || {}; + for (var key in asyncComputed) { + var item = this.$options.asyncComputed[key]; + + var value = generateDefault.call(this, item, pluginOptions); + if (isComputedLazy(item)) { + initLazy(data, key, value); + this.$options.computed[key] = makeLazyComputed(key); + } else { + data[key] = value; + } + } + return data; + }; + } + + function getterFn(key, fn) { + if (typeof fn === 'function') return fn; + + var getter = fn.get; + + if (hasOwnProperty(fn, 'watch')) { + getter = getWatchedGetter(fn); + } + + if (hasOwnProperty(fn, 'shouldUpdate')) { + getter = getGetterWithShouldUpdate(fn, getter); + } + + if (isComputedLazy(fn)) { + var nonLazy = getter; + getter = function lazyGetter() { + if (isLazyActive(this, key)) { + return nonLazy.call(this); + } else { + return silentGetLazy(this, key); + } + }; + } + return getter; + } + + function generateDefault(fn, pluginOptions) { + var defaultValue = null; + + if ('default' in fn) { + defaultValue = fn.default; + } else if ('default' in pluginOptions) { + defaultValue = pluginOptions.default; + } + + if (typeof defaultValue === 'function') { + return defaultValue.call(this); + } else { + return defaultValue; + } + } + + return AsyncComputed; +}); From 9ad7e7cd3dbc0519622aa72fbcfacd9694714309 Mon Sep 17 00:00:00 2001 From: alex-dunn Date: Wed, 4 Aug 2021 09:11:49 +0100 Subject: [PATCH 4/5] Updated types --- .eslintrc | 3 ++- types/index.d.ts | 15 +++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/.eslintrc b/.eslintrc index 2938822..72e0b99 100644 --- a/.eslintrc +++ b/.eslintrc @@ -29,5 +29,6 @@ ], "one-var": 0, "promise/catch-or-return": 2 - } + }, + "ignorePatterns": [ "/**/*.d.ts" ] } diff --git a/types/index.d.ts b/types/index.d.ts index 3da51d5..39467f0 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,4 +1,4 @@ -import Vue, { PluginFunction } from 'vue'; +import Vue, { Plugin } from 'vue'; export interface IAsyncComputedOptions { errorHandler?: (error: string | Error) => void; @@ -7,8 +7,7 @@ export interface IAsyncComputedOptions { } export default class AsyncComputed { - constructor(options?: IAsyncComputedOptions); - static install: PluginFunction; + static install: Plugin; static version: string; } @@ -35,14 +34,10 @@ export interface IASyncComputedState { update: () => void; } -declare module 'vue/types/options' { - interface ComponentOptions { +declare module '@vue/runtime-core' { + export interface AsyncComponentOptions { asyncComputed?: AsyncComputedObject; - } -} - -declare module 'vue/types/vue' { - interface Vue { $asyncComputed: {[K: string]: IASyncComputedState}; } } + From b8a57721be4be86f49042784e398b477a288fb63 Mon Sep 17 00:00:00 2001 From: alex-dunn Date: Wed, 4 Aug 2021 09:13:40 +0100 Subject: [PATCH 5/5] Update package veriosn --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e3d1b39..ce0ef9e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-async-computed", - "version": "3.8.2", + "version": "4.0.0", "description": "Async computed properties for Vue", "main": "dist/vue-async-computed.js", "module": "dist/vue-async-computed.esm.js",