├── .github └── workflows │ └── main.yml ├── .gitignore ├── CNAME ├── LICENSE ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── revision-hash.js └── src ├── assets ├── favicon.png ├── favicon.svg └── loading.gif ├── js ├── ErrorMessage.js ├── Filter.js ├── Leaderboard.js ├── LeaderboardControl.js ├── Loading.js ├── app.js ├── store.js └── templates.js └── styles.css /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: 3 | pull_request: 4 | branches: master 5 | types: [closed] 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | if: github.event.pull_request.merged == true 10 | steps: 11 | - name: Checkout 🛎️ 12 | uses: actions/checkout@v2.3.1 13 | with: 14 | persist-credentials: false 15 | 16 | - name: Use Node.js 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: '12.x' 20 | 21 | - name: Install and Build 🔧 22 | run: | 23 | npm ci 24 | npm run dist 25 | 26 | - name: Deploy 🚀 27 | uses: JamesIves/github-pages-deploy-action@3.7.1 28 | with: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | BRANCH: gh-pages 31 | FOLDER: dist 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .DS_Store 107 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | vitals-leaderboard.pazguille.me 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Guille Paz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web Vitals Leaderboard 2 | > The simplest way to understand how your site’s user experience compares to other major sites in your industry. 3 | 4 | ⚡️ https://web-vitals.pazguille.me/ 5 | 6 | ## Development 7 | 8 | 1. Install [Git](http://git-scm.com/) and [Node](http://nodejs.org/). 9 | 10 | 2. Open your terminal and clone th repository by running: 11 | 12 | ```sh 13 | git clone git@github.com:pazguille/web-vitals-leaderboard.git 14 | ``` 15 | 16 | 3. Go to folder `web-vitals-leaderboard`: 17 | 18 | ```sh 19 | cd web-vitals-leaderboard 20 | ``` 21 | 22 | 5. Move to `develop` branch: 23 | 24 | ```sh 25 | git checkout develop 26 | ``` 27 | 28 | 6. Install development dependencies: 29 | 30 | ```sh 31 | npm install 32 | ``` 33 | 34 | 7. Run a local server: 35 | 36 | ```sh 37 | npm start 38 | ``` 39 | 40 | 7. 👨‍💻 Code, code code! 41 | 42 | 8. Send a pull request if you wish. 43 | 44 | ## Made with ❤ by 45 | 46 | - Guille Paz (👨‍💻 Front End Web Developer | ⚡️ Web Performance Lover) 47 | - E-mail: [guille87paz@gmail.com](mailto:guille87paz@gmail.com) 48 | - Twitter: [@pazguille](https://twitter.com/pazguille) 49 | - Web: [https://pazguille.me](https://pazguille.me) 50 | 51 | ## License 52 | 53 | MIT license. Copyright © 2020. 54 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Web Vitals Leaderboard 34 | 35 | 36 | 37 |
38 |
39 |
40 |

Compare your speed against competitors

41 |

Web Vitals Leaderboard

42 |

The simplest way to understand how your website’s performance compares to other sites in your industry.

43 |
44 |
45 |
46 | 50 | 53 | 54 |
55 |

56 |
57 | 58 | 59 |
60 |
61 |
62 | 63 | 64 | Upvote Web Vitals Leaderboard on Product Hunt 68 | 69 |
70 | 71 |

Made with by Guille Paz 💸 Support Me!

72 |
73 |
74 |
75 | 76 |
77 | Loading 78 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 97 | 102 | 107 | 112 | 113 | 114 | 115 | 116 |
117 | 118 |
119 |

Frequently Asked Questions

120 |
121 | What are Web Vitals? 122 |

Web Vitals is an initiative by Google to provide unified guidance for quality signals that are essential to delivering a great user experience on the web. This metrics represent the best available signals developers have today to measure quality of experience across the web.

123 |
124 |
125 | Where does data come from? 126 |

Web Vitals Leaderboard makes use of Chrome UX Report API that dataset represents how real-world Chrome users experience popular destinations on the web. CRUX aggregates user experience metrics by origin and url, for all websites that are known by Google's web crawlers.

127 |
128 |
129 | How frequently is data updated? 130 |

The data in the Chrome UX Report is a 28-day rolling average of aggregated metrics. This means that the data presented in the Chrome UX Report at any given time is actually data for the past 28 days aggregated together.

131 |
132 |
133 | How rankings are determined? 134 |

Largest Contentful Paint (LCP) measures when a user perceives that the largest content of a page is visible. The metric value for LCP represents the time duration between the user initiating the page load and the page rendering its primary content. Based on real website data, top-performing sites render LCP in about 1,2 sec, so this metric is the most important to represent a fast speed.

135 |
136 |
137 |
138 | 139 | 142 | 143 | 177 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-vitals-leaderboard", 3 | "version": "1.2.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@zeit/schemas": { 8 | "version": "2.6.0", 9 | "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz", 10 | "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==", 11 | "dev": true 12 | }, 13 | "accepts": { 14 | "version": "1.3.7", 15 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 16 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 17 | "dev": true, 18 | "requires": { 19 | "mime-types": "~2.1.24", 20 | "negotiator": "0.6.2" 21 | } 22 | }, 23 | "ajv": { 24 | "version": "6.5.3", 25 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", 26 | "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", 27 | "dev": true, 28 | "requires": { 29 | "fast-deep-equal": "^2.0.1", 30 | "fast-json-stable-stringify": "^2.0.0", 31 | "json-schema-traverse": "^0.4.1", 32 | "uri-js": "^4.2.2" 33 | } 34 | }, 35 | "ansi-align": { 36 | "version": "2.0.0", 37 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", 38 | "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", 39 | "dev": true, 40 | "requires": { 41 | "string-width": "^2.0.0" 42 | } 43 | }, 44 | "ansi-regex": { 45 | "version": "3.0.0", 46 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 47 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 48 | "dev": true 49 | }, 50 | "ansi-styles": { 51 | "version": "3.2.1", 52 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 53 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 54 | "dev": true, 55 | "requires": { 56 | "color-convert": "^1.9.0" 57 | } 58 | }, 59 | "arch": { 60 | "version": "2.1.2", 61 | "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", 62 | "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==", 63 | "dev": true 64 | }, 65 | "arg": { 66 | "version": "2.0.0", 67 | "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz", 68 | "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==", 69 | "dev": true 70 | }, 71 | "balanced-match": { 72 | "version": "1.0.0", 73 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 74 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 75 | "dev": true 76 | }, 77 | "boxen": { 78 | "version": "1.3.0", 79 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", 80 | "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", 81 | "dev": true, 82 | "requires": { 83 | "ansi-align": "^2.0.0", 84 | "camelcase": "^4.0.0", 85 | "chalk": "^2.0.1", 86 | "cli-boxes": "^1.0.0", 87 | "string-width": "^2.0.0", 88 | "term-size": "^1.2.0", 89 | "widest-line": "^2.0.0" 90 | } 91 | }, 92 | "brace-expansion": { 93 | "version": "1.1.11", 94 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 95 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 96 | "dev": true, 97 | "requires": { 98 | "balanced-match": "^1.0.0", 99 | "concat-map": "0.0.1" 100 | } 101 | }, 102 | "buffer-from": { 103 | "version": "1.1.1", 104 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 105 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 106 | "dev": true 107 | }, 108 | "bytes": { 109 | "version": "3.0.0", 110 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 111 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", 112 | "dev": true 113 | }, 114 | "camelcase": { 115 | "version": "4.1.0", 116 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 117 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", 118 | "dev": true 119 | }, 120 | "chalk": { 121 | "version": "2.4.1", 122 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 123 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 124 | "dev": true, 125 | "requires": { 126 | "ansi-styles": "^3.2.1", 127 | "escape-string-regexp": "^1.0.5", 128 | "supports-color": "^5.3.0" 129 | } 130 | }, 131 | "clean-css": { 132 | "version": "4.2.3", 133 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", 134 | "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", 135 | "dev": true, 136 | "requires": { 137 | "source-map": "~0.6.0" 138 | } 139 | }, 140 | "cli-boxes": { 141 | "version": "1.0.0", 142 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", 143 | "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", 144 | "dev": true 145 | }, 146 | "clipboardy": { 147 | "version": "1.2.3", 148 | "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.3.tgz", 149 | "integrity": "sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA==", 150 | "dev": true, 151 | "requires": { 152 | "arch": "^2.1.0", 153 | "execa": "^0.8.0" 154 | }, 155 | "dependencies": { 156 | "execa": { 157 | "version": "0.8.0", 158 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", 159 | "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", 160 | "dev": true, 161 | "requires": { 162 | "cross-spawn": "^5.0.1", 163 | "get-stream": "^3.0.0", 164 | "is-stream": "^1.1.0", 165 | "npm-run-path": "^2.0.0", 166 | "p-finally": "^1.0.0", 167 | "signal-exit": "^3.0.0", 168 | "strip-eof": "^1.0.0" 169 | } 170 | } 171 | } 172 | }, 173 | "cliui": { 174 | "version": "7.0.4", 175 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 176 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 177 | "dev": true, 178 | "requires": { 179 | "string-width": "^4.2.0", 180 | "strip-ansi": "^6.0.0", 181 | "wrap-ansi": "^7.0.0" 182 | }, 183 | "dependencies": { 184 | "ansi-regex": { 185 | "version": "5.0.0", 186 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 187 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 188 | "dev": true 189 | }, 190 | "is-fullwidth-code-point": { 191 | "version": "3.0.0", 192 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 193 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 194 | "dev": true 195 | }, 196 | "string-width": { 197 | "version": "4.2.2", 198 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", 199 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", 200 | "dev": true, 201 | "requires": { 202 | "emoji-regex": "^8.0.0", 203 | "is-fullwidth-code-point": "^3.0.0", 204 | "strip-ansi": "^6.0.0" 205 | } 206 | }, 207 | "strip-ansi": { 208 | "version": "6.0.0", 209 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 210 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 211 | "dev": true, 212 | "requires": { 213 | "ansi-regex": "^5.0.0" 214 | } 215 | } 216 | } 217 | }, 218 | "color-convert": { 219 | "version": "1.9.3", 220 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 221 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 222 | "dev": true, 223 | "requires": { 224 | "color-name": "1.1.3" 225 | } 226 | }, 227 | "color-name": { 228 | "version": "1.1.3", 229 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 230 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 231 | "dev": true 232 | }, 233 | "commander": { 234 | "version": "2.20.3", 235 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 236 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 237 | "dev": true 238 | }, 239 | "compressible": { 240 | "version": "2.0.18", 241 | "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", 242 | "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", 243 | "dev": true, 244 | "requires": { 245 | "mime-db": ">= 1.43.0 < 2" 246 | } 247 | }, 248 | "compression": { 249 | "version": "1.7.3", 250 | "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", 251 | "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", 252 | "dev": true, 253 | "requires": { 254 | "accepts": "~1.3.5", 255 | "bytes": "3.0.0", 256 | "compressible": "~2.0.14", 257 | "debug": "2.6.9", 258 | "on-headers": "~1.0.1", 259 | "safe-buffer": "5.1.2", 260 | "vary": "~1.1.2" 261 | } 262 | }, 263 | "concat-map": { 264 | "version": "0.0.1", 265 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 266 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 267 | "dev": true 268 | }, 269 | "content-disposition": { 270 | "version": "0.5.2", 271 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 272 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", 273 | "dev": true 274 | }, 275 | "core-util-is": { 276 | "version": "1.0.2", 277 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 278 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 279 | "dev": true 280 | }, 281 | "cross-spawn": { 282 | "version": "5.1.0", 283 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 284 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 285 | "dev": true, 286 | "requires": { 287 | "lru-cache": "^4.0.1", 288 | "shebang-command": "^1.2.0", 289 | "which": "^1.2.9" 290 | } 291 | }, 292 | "cssauron": { 293 | "version": "1.4.0", 294 | "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", 295 | "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", 296 | "dev": true, 297 | "requires": { 298 | "through": "X.X.X" 299 | } 300 | }, 301 | "debug": { 302 | "version": "2.6.9", 303 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 304 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 305 | "dev": true, 306 | "requires": { 307 | "ms": "2.0.0" 308 | } 309 | }, 310 | "deep-extend": { 311 | "version": "0.6.0", 312 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 313 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 314 | "dev": true 315 | }, 316 | "dot-case": { 317 | "version": "3.0.3", 318 | "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz", 319 | "integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==", 320 | "dev": true, 321 | "requires": { 322 | "no-case": "^3.0.3", 323 | "tslib": "^1.10.0" 324 | }, 325 | "dependencies": { 326 | "lower-case": { 327 | "version": "2.0.1", 328 | "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", 329 | "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", 330 | "dev": true, 331 | "requires": { 332 | "tslib": "^1.10.0" 333 | } 334 | }, 335 | "no-case": { 336 | "version": "3.0.3", 337 | "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", 338 | "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", 339 | "dev": true, 340 | "requires": { 341 | "lower-case": "^2.0.1", 342 | "tslib": "^1.10.0" 343 | } 344 | } 345 | } 346 | }, 347 | "duplexer2": { 348 | "version": "0.0.2", 349 | "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", 350 | "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", 351 | "dev": true, 352 | "requires": { 353 | "readable-stream": "~1.1.9" 354 | }, 355 | "dependencies": { 356 | "readable-stream": { 357 | "version": "1.1.14", 358 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 359 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 360 | "dev": true, 361 | "requires": { 362 | "core-util-is": "~1.0.0", 363 | "inherits": "~2.0.1", 364 | "isarray": "0.0.1", 365 | "string_decoder": "~0.10.x" 366 | } 367 | } 368 | } 369 | }, 370 | "emoji-regex": { 371 | "version": "8.0.0", 372 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 373 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 374 | "dev": true 375 | }, 376 | "escalade": { 377 | "version": "3.1.1", 378 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 379 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 380 | "dev": true 381 | }, 382 | "escape-string-regexp": { 383 | "version": "1.0.5", 384 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 385 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 386 | "dev": true 387 | }, 388 | "execa": { 389 | "version": "0.7.0", 390 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", 391 | "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", 392 | "dev": true, 393 | "requires": { 394 | "cross-spawn": "^5.0.1", 395 | "get-stream": "^3.0.0", 396 | "is-stream": "^1.1.0", 397 | "npm-run-path": "^2.0.0", 398 | "p-finally": "^1.0.0", 399 | "signal-exit": "^3.0.0", 400 | "strip-eof": "^1.0.0" 401 | } 402 | }, 403 | "fast-deep-equal": { 404 | "version": "2.0.1", 405 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 406 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", 407 | "dev": true 408 | }, 409 | "fast-json-stable-stringify": { 410 | "version": "2.1.0", 411 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 412 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 413 | "dev": true 414 | }, 415 | "fast-url-parser": { 416 | "version": "1.1.3", 417 | "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", 418 | "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", 419 | "dev": true, 420 | "requires": { 421 | "punycode": "^1.3.2" 422 | }, 423 | "dependencies": { 424 | "punycode": { 425 | "version": "1.4.1", 426 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 427 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", 428 | "dev": true 429 | } 430 | } 431 | }, 432 | "fs.realpath": { 433 | "version": "1.0.0", 434 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 435 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 436 | "dev": true 437 | }, 438 | "get-caller-file": { 439 | "version": "2.0.5", 440 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 441 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 442 | "dev": true 443 | }, 444 | "get-stream": { 445 | "version": "3.0.0", 446 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 447 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", 448 | "dev": true 449 | }, 450 | "glob": { 451 | "version": "7.1.7", 452 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", 453 | "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", 454 | "dev": true, 455 | "requires": { 456 | "fs.realpath": "^1.0.0", 457 | "inflight": "^1.0.4", 458 | "inherits": "2", 459 | "minimatch": "^3.0.4", 460 | "once": "^1.3.0", 461 | "path-is-absolute": "^1.0.0" 462 | } 463 | }, 464 | "has-flag": { 465 | "version": "3.0.0", 466 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 467 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 468 | "dev": true 469 | }, 470 | "he": { 471 | "version": "1.2.0", 472 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 473 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 474 | "dev": true 475 | }, 476 | "html-inline": { 477 | "version": "1.2.0", 478 | "resolved": "https://registry.npmjs.org/html-inline/-/html-inline-1.2.0.tgz", 479 | "integrity": "sha1-eFSUam9cMSK5k7gdPTfWvQYAvsE=", 480 | "dev": true, 481 | "requires": { 482 | "minimist": "~1.1.0", 483 | "through2": "~0.6.3", 484 | "trumpet": "~1.7.0" 485 | }, 486 | "dependencies": { 487 | "minimist": { 488 | "version": "1.1.3", 489 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", 490 | "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=", 491 | "dev": true 492 | } 493 | } 494 | }, 495 | "html-minifier-terser": { 496 | "version": "5.1.1", 497 | "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", 498 | "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", 499 | "dev": true, 500 | "requires": { 501 | "camel-case": "^4.1.1", 502 | "clean-css": "^4.2.3", 503 | "commander": "^4.1.1", 504 | "he": "^1.2.0", 505 | "param-case": "^3.0.3", 506 | "relateurl": "^0.2.7", 507 | "terser": "^4.6.3" 508 | }, 509 | "dependencies": { 510 | "camel-case": { 511 | "version": "4.1.1", 512 | "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", 513 | "integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==", 514 | "dev": true, 515 | "requires": { 516 | "pascal-case": "^3.1.1", 517 | "tslib": "^1.10.0" 518 | } 519 | }, 520 | "commander": { 521 | "version": "4.1.1", 522 | "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", 523 | "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", 524 | "dev": true 525 | }, 526 | "param-case": { 527 | "version": "3.0.3", 528 | "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz", 529 | "integrity": "sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==", 530 | "dev": true, 531 | "requires": { 532 | "dot-case": "^3.0.3", 533 | "tslib": "^1.10.0" 534 | } 535 | }, 536 | "terser": { 537 | "version": "4.8.0", 538 | "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", 539 | "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", 540 | "dev": true, 541 | "requires": { 542 | "commander": "^2.20.0", 543 | "source-map": "~0.6.1", 544 | "source-map-support": "~0.5.12" 545 | }, 546 | "dependencies": { 547 | "commander": { 548 | "version": "2.20.3", 549 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 550 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 551 | "dev": true 552 | } 553 | } 554 | } 555 | } 556 | }, 557 | "html-select": { 558 | "version": "2.3.24", 559 | "resolved": "https://registry.npmjs.org/html-select/-/html-select-2.3.24.tgz", 560 | "integrity": "sha1-Rq1tcS5zLPMcZznV0BEKX6vxdYU=", 561 | "dev": true, 562 | "requires": { 563 | "cssauron": "^1.1.0", 564 | "duplexer2": "~0.0.2", 565 | "inherits": "^2.0.1", 566 | "minimist": "~0.0.8", 567 | "readable-stream": "^1.0.27-1", 568 | "split": "~0.3.0", 569 | "stream-splicer": "^1.2.0", 570 | "through2": "^1.0.0" 571 | }, 572 | "dependencies": { 573 | "minimist": { 574 | "version": "0.0.10", 575 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 576 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", 577 | "dev": true 578 | }, 579 | "through2": { 580 | "version": "1.1.1", 581 | "resolved": "https://registry.npmjs.org/through2/-/through2-1.1.1.tgz", 582 | "integrity": "sha1-CEfLxESfNAVXTb3M2buEG4OsNUU=", 583 | "dev": true, 584 | "requires": { 585 | "readable-stream": ">=1.1.13-1 <1.2.0-0", 586 | "xtend": ">=4.0.0 <4.1.0-0" 587 | }, 588 | "dependencies": { 589 | "readable-stream": { 590 | "version": "1.1.14", 591 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 592 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 593 | "dev": true, 594 | "requires": { 595 | "core-util-is": "~1.0.0", 596 | "inherits": "~2.0.1", 597 | "isarray": "0.0.1", 598 | "string_decoder": "~0.10.x" 599 | } 600 | } 601 | } 602 | } 603 | } 604 | }, 605 | "html-tokenize": { 606 | "version": "1.2.5", 607 | "resolved": "https://registry.npmjs.org/html-tokenize/-/html-tokenize-1.2.5.tgz", 608 | "integrity": "sha1-flupnstR75Buyaf83ubKMmfHiX4=", 609 | "dev": true, 610 | "requires": { 611 | "inherits": "~2.0.1", 612 | "minimist": "~0.0.8", 613 | "readable-stream": "~1.0.27-1", 614 | "through2": "~0.4.1" 615 | }, 616 | "dependencies": { 617 | "minimist": { 618 | "version": "0.0.10", 619 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 620 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", 621 | "dev": true 622 | }, 623 | "through2": { 624 | "version": "0.4.2", 625 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", 626 | "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", 627 | "dev": true, 628 | "requires": { 629 | "readable-stream": "~1.0.17", 630 | "xtend": "~2.1.1" 631 | } 632 | }, 633 | "xtend": { 634 | "version": "2.1.2", 635 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", 636 | "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", 637 | "dev": true, 638 | "requires": { 639 | "object-keys": "~0.4.0" 640 | } 641 | } 642 | } 643 | }, 644 | "indexof": { 645 | "version": "0.0.1", 646 | "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", 647 | "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", 648 | "dev": true 649 | }, 650 | "inflight": { 651 | "version": "1.0.6", 652 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 653 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 654 | "dev": true, 655 | "requires": { 656 | "once": "^1.3.0", 657 | "wrappy": "1" 658 | } 659 | }, 660 | "inherits": { 661 | "version": "2.0.4", 662 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 663 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 664 | "dev": true 665 | }, 666 | "ini": { 667 | "version": "1.3.8", 668 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 669 | "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", 670 | "dev": true 671 | }, 672 | "is-fullwidth-code-point": { 673 | "version": "2.0.0", 674 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 675 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 676 | "dev": true 677 | }, 678 | "is-stream": { 679 | "version": "1.1.0", 680 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 681 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 682 | "dev": true 683 | }, 684 | "isarray": { 685 | "version": "0.0.1", 686 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 687 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 688 | "dev": true 689 | }, 690 | "isexe": { 691 | "version": "2.0.0", 692 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 693 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 694 | "dev": true 695 | }, 696 | "json-schema-traverse": { 697 | "version": "0.4.1", 698 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 699 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 700 | "dev": true 701 | }, 702 | "lru-cache": { 703 | "version": "4.1.5", 704 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 705 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 706 | "dev": true, 707 | "requires": { 708 | "pseudomap": "^1.0.2", 709 | "yallist": "^2.1.2" 710 | } 711 | }, 712 | "mime-db": { 713 | "version": "1.44.0", 714 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 715 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", 716 | "dev": true 717 | }, 718 | "mime-types": { 719 | "version": "2.1.27", 720 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 721 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 722 | "dev": true, 723 | "requires": { 724 | "mime-db": "1.44.0" 725 | } 726 | }, 727 | "minimatch": { 728 | "version": "3.0.4", 729 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 730 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 731 | "dev": true, 732 | "requires": { 733 | "brace-expansion": "^1.1.7" 734 | } 735 | }, 736 | "minimist": { 737 | "version": "1.2.5", 738 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 739 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 740 | "dev": true 741 | }, 742 | "ms": { 743 | "version": "2.0.0", 744 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 745 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 746 | "dev": true 747 | }, 748 | "negotiator": { 749 | "version": "0.6.2", 750 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 751 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 752 | "dev": true 753 | }, 754 | "npm-run-path": { 755 | "version": "2.0.2", 756 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 757 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 758 | "dev": true, 759 | "requires": { 760 | "path-key": "^2.0.0" 761 | } 762 | }, 763 | "object-keys": { 764 | "version": "0.4.0", 765 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", 766 | "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", 767 | "dev": true 768 | }, 769 | "on-headers": { 770 | "version": "1.0.2", 771 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 772 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", 773 | "dev": true 774 | }, 775 | "once": { 776 | "version": "1.4.0", 777 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 778 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 779 | "dev": true, 780 | "requires": { 781 | "wrappy": "1" 782 | } 783 | }, 784 | "p-finally": { 785 | "version": "1.0.0", 786 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 787 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", 788 | "dev": true 789 | }, 790 | "pascal-case": { 791 | "version": "3.1.1", 792 | "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz", 793 | "integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==", 794 | "dev": true, 795 | "requires": { 796 | "no-case": "^3.0.3", 797 | "tslib": "^1.10.0" 798 | }, 799 | "dependencies": { 800 | "lower-case": { 801 | "version": "2.0.1", 802 | "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", 803 | "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", 804 | "dev": true, 805 | "requires": { 806 | "tslib": "^1.10.0" 807 | } 808 | }, 809 | "no-case": { 810 | "version": "3.0.3", 811 | "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", 812 | "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", 813 | "dev": true, 814 | "requires": { 815 | "lower-case": "^2.0.1", 816 | "tslib": "^1.10.0" 817 | } 818 | } 819 | } 820 | }, 821 | "path-is-absolute": { 822 | "version": "1.0.1", 823 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 824 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 825 | "dev": true 826 | }, 827 | "path-is-inside": { 828 | "version": "1.0.2", 829 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 830 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 831 | "dev": true 832 | }, 833 | "path-key": { 834 | "version": "2.0.1", 835 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 836 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 837 | "dev": true 838 | }, 839 | "path-to-regexp": { 840 | "version": "2.2.1", 841 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", 842 | "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==", 843 | "dev": true 844 | }, 845 | "pseudomap": { 846 | "version": "1.0.2", 847 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 848 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 849 | "dev": true 850 | }, 851 | "punycode": { 852 | "version": "2.1.1", 853 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 854 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 855 | "dev": true 856 | }, 857 | "range-parser": { 858 | "version": "1.2.0", 859 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 860 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", 861 | "dev": true 862 | }, 863 | "rc": { 864 | "version": "1.2.8", 865 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 866 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 867 | "dev": true, 868 | "requires": { 869 | "deep-extend": "^0.6.0", 870 | "ini": "~1.3.0", 871 | "minimist": "^1.2.0", 872 | "strip-json-comments": "~2.0.1" 873 | } 874 | }, 875 | "readable-stream": { 876 | "version": "1.0.34", 877 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 878 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 879 | "dev": true, 880 | "requires": { 881 | "core-util-is": "~1.0.0", 882 | "inherits": "~2.0.1", 883 | "isarray": "0.0.1", 884 | "string_decoder": "~0.10.x" 885 | } 886 | }, 887 | "readable-wrap": { 888 | "version": "1.0.0", 889 | "resolved": "https://registry.npmjs.org/readable-wrap/-/readable-wrap-1.0.0.tgz", 890 | "integrity": "sha1-O1ohHGMeEjA6VJkcgGwX564ga/8=", 891 | "dev": true, 892 | "requires": { 893 | "readable-stream": "^1.1.13-1" 894 | }, 895 | "dependencies": { 896 | "readable-stream": { 897 | "version": "1.1.14", 898 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 899 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 900 | "dev": true, 901 | "requires": { 902 | "core-util-is": "~1.0.0", 903 | "inherits": "~2.0.1", 904 | "isarray": "0.0.1", 905 | "string_decoder": "~0.10.x" 906 | } 907 | } 908 | } 909 | }, 910 | "registry-auth-token": { 911 | "version": "3.3.2", 912 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", 913 | "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", 914 | "dev": true, 915 | "requires": { 916 | "rc": "^1.1.6", 917 | "safe-buffer": "^5.0.1" 918 | } 919 | }, 920 | "registry-url": { 921 | "version": "3.1.0", 922 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", 923 | "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", 924 | "dev": true, 925 | "requires": { 926 | "rc": "^1.0.1" 927 | } 928 | }, 929 | "relateurl": { 930 | "version": "0.2.7", 931 | "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", 932 | "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", 933 | "dev": true 934 | }, 935 | "replace-in-file": { 936 | "version": "6.2.0", 937 | "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.2.0.tgz", 938 | "integrity": "sha512-Im2AF9G/qgkYneOc9QwWwUS/efyyonTUBvzXS2VXuxPawE5yQIjT/e6x4CTijO0Quq48lfAujuo+S89RR2TP2Q==", 939 | "dev": true, 940 | "requires": { 941 | "chalk": "^4.1.0", 942 | "glob": "^7.1.6", 943 | "yargs": "^16.2.0" 944 | }, 945 | "dependencies": { 946 | "ansi-styles": { 947 | "version": "4.3.0", 948 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 949 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 950 | "dev": true, 951 | "requires": { 952 | "color-convert": "^2.0.1" 953 | } 954 | }, 955 | "chalk": { 956 | "version": "4.1.1", 957 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", 958 | "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", 959 | "dev": true, 960 | "requires": { 961 | "ansi-styles": "^4.1.0", 962 | "supports-color": "^7.1.0" 963 | } 964 | }, 965 | "color-convert": { 966 | "version": "2.0.1", 967 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 968 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 969 | "dev": true, 970 | "requires": { 971 | "color-name": "~1.1.4" 972 | } 973 | }, 974 | "color-name": { 975 | "version": "1.1.4", 976 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 977 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 978 | "dev": true 979 | }, 980 | "has-flag": { 981 | "version": "4.0.0", 982 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 983 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 984 | "dev": true 985 | }, 986 | "supports-color": { 987 | "version": "7.2.0", 988 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 989 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 990 | "dev": true, 991 | "requires": { 992 | "has-flag": "^4.0.0" 993 | } 994 | } 995 | } 996 | }, 997 | "require-directory": { 998 | "version": "2.1.1", 999 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1000 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 1001 | "dev": true 1002 | }, 1003 | "rev-hash": { 1004 | "version": "3.0.0", 1005 | "resolved": "https://registry.npmjs.org/rev-hash/-/rev-hash-3.0.0.tgz", 1006 | "integrity": "sha512-s+87HfEKAu95TaTxnbCobn0/BkbzR23LHSwVdYvr8mn5+PPjzy+hTWyh92b5oaLgig9TKPe5d6ZcubsVBtUrZg==", 1007 | "dev": true 1008 | }, 1009 | "safe-buffer": { 1010 | "version": "5.1.2", 1011 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1012 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1013 | "dev": true 1014 | }, 1015 | "serve": { 1016 | "version": "11.3.2", 1017 | "resolved": "https://registry.npmjs.org/serve/-/serve-11.3.2.tgz", 1018 | "integrity": "sha512-yKWQfI3xbj/f7X1lTBg91fXBP0FqjJ4TEi+ilES5yzH0iKJpN5LjNb1YzIfQg9Rqn4ECUS2SOf2+Kmepogoa5w==", 1019 | "dev": true, 1020 | "requires": { 1021 | "@zeit/schemas": "2.6.0", 1022 | "ajv": "6.5.3", 1023 | "arg": "2.0.0", 1024 | "boxen": "1.3.0", 1025 | "chalk": "2.4.1", 1026 | "clipboardy": "1.2.3", 1027 | "compression": "1.7.3", 1028 | "serve-handler": "6.1.3", 1029 | "update-check": "1.5.2" 1030 | } 1031 | }, 1032 | "serve-handler": { 1033 | "version": "6.1.3", 1034 | "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", 1035 | "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", 1036 | "dev": true, 1037 | "requires": { 1038 | "bytes": "3.0.0", 1039 | "content-disposition": "0.5.2", 1040 | "fast-url-parser": "1.1.3", 1041 | "mime-types": "2.1.18", 1042 | "minimatch": "3.0.4", 1043 | "path-is-inside": "1.0.2", 1044 | "path-to-regexp": "2.2.1", 1045 | "range-parser": "1.2.0" 1046 | }, 1047 | "dependencies": { 1048 | "mime-db": { 1049 | "version": "1.33.0", 1050 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 1051 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", 1052 | "dev": true 1053 | }, 1054 | "mime-types": { 1055 | "version": "2.1.18", 1056 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 1057 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 1058 | "dev": true, 1059 | "requires": { 1060 | "mime-db": "~1.33.0" 1061 | } 1062 | } 1063 | } 1064 | }, 1065 | "shebang-command": { 1066 | "version": "1.2.0", 1067 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1068 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1069 | "dev": true, 1070 | "requires": { 1071 | "shebang-regex": "^1.0.0" 1072 | } 1073 | }, 1074 | "shebang-regex": { 1075 | "version": "1.0.0", 1076 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1077 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 1078 | "dev": true 1079 | }, 1080 | "signal-exit": { 1081 | "version": "3.0.3", 1082 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 1083 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", 1084 | "dev": true 1085 | }, 1086 | "source-map": { 1087 | "version": "0.6.1", 1088 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1089 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1090 | "dev": true 1091 | }, 1092 | "source-map-support": { 1093 | "version": "0.5.19", 1094 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 1095 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 1096 | "dev": true, 1097 | "requires": { 1098 | "buffer-from": "^1.0.0", 1099 | "source-map": "^0.6.0" 1100 | } 1101 | }, 1102 | "split": { 1103 | "version": "0.3.3", 1104 | "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", 1105 | "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", 1106 | "dev": true, 1107 | "requires": { 1108 | "through": "2" 1109 | } 1110 | }, 1111 | "stream-splicer": { 1112 | "version": "1.3.2", 1113 | "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-1.3.2.tgz", 1114 | "integrity": "sha1-PARBvhW5v04iYnXm3IOWR0VUZmE=", 1115 | "dev": true, 1116 | "requires": { 1117 | "indexof": "0.0.1", 1118 | "inherits": "^2.0.1", 1119 | "isarray": "~0.0.1", 1120 | "readable-stream": "^1.1.13-1", 1121 | "readable-wrap": "^1.0.0", 1122 | "through2": "^1.0.0" 1123 | }, 1124 | "dependencies": { 1125 | "readable-stream": { 1126 | "version": "1.1.14", 1127 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 1128 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 1129 | "dev": true, 1130 | "requires": { 1131 | "core-util-is": "~1.0.0", 1132 | "inherits": "~2.0.1", 1133 | "isarray": "0.0.1", 1134 | "string_decoder": "~0.10.x" 1135 | } 1136 | }, 1137 | "through2": { 1138 | "version": "1.1.1", 1139 | "resolved": "https://registry.npmjs.org/through2/-/through2-1.1.1.tgz", 1140 | "integrity": "sha1-CEfLxESfNAVXTb3M2buEG4OsNUU=", 1141 | "dev": true, 1142 | "requires": { 1143 | "readable-stream": ">=1.1.13-1 <1.2.0-0", 1144 | "xtend": ">=4.0.0 <4.1.0-0" 1145 | } 1146 | } 1147 | } 1148 | }, 1149 | "string-width": { 1150 | "version": "2.1.1", 1151 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1152 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1153 | "dev": true, 1154 | "requires": { 1155 | "is-fullwidth-code-point": "^2.0.0", 1156 | "strip-ansi": "^4.0.0" 1157 | } 1158 | }, 1159 | "string_decoder": { 1160 | "version": "0.10.31", 1161 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1162 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 1163 | "dev": true 1164 | }, 1165 | "strip-ansi": { 1166 | "version": "4.0.0", 1167 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1168 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1169 | "dev": true, 1170 | "requires": { 1171 | "ansi-regex": "^3.0.0" 1172 | } 1173 | }, 1174 | "strip-eof": { 1175 | "version": "1.0.0", 1176 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 1177 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", 1178 | "dev": true 1179 | }, 1180 | "strip-json-comments": { 1181 | "version": "2.0.1", 1182 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1183 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1184 | "dev": true 1185 | }, 1186 | "supports-color": { 1187 | "version": "5.5.0", 1188 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1189 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1190 | "dev": true, 1191 | "requires": { 1192 | "has-flag": "^3.0.0" 1193 | } 1194 | }, 1195 | "term-size": { 1196 | "version": "1.2.0", 1197 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", 1198 | "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", 1199 | "dev": true, 1200 | "requires": { 1201 | "execa": "^0.7.0" 1202 | } 1203 | }, 1204 | "terser": { 1205 | "version": "5.7.1", 1206 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", 1207 | "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", 1208 | "dev": true, 1209 | "requires": { 1210 | "commander": "^2.20.0", 1211 | "source-map": "~0.7.2", 1212 | "source-map-support": "~0.5.19" 1213 | }, 1214 | "dependencies": { 1215 | "source-map": { 1216 | "version": "0.7.3", 1217 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", 1218 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", 1219 | "dev": true 1220 | } 1221 | } 1222 | }, 1223 | "through": { 1224 | "version": "2.3.8", 1225 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1226 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1227 | "dev": true 1228 | }, 1229 | "through2": { 1230 | "version": "0.6.5", 1231 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", 1232 | "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", 1233 | "dev": true, 1234 | "requires": { 1235 | "readable-stream": ">=1.0.33-1 <1.1.0-0", 1236 | "xtend": ">=4.0.0 <4.1.0-0" 1237 | } 1238 | }, 1239 | "trumpet": { 1240 | "version": "1.7.2", 1241 | "resolved": "https://registry.npmjs.org/trumpet/-/trumpet-1.7.2.tgz", 1242 | "integrity": "sha1-sCxp5GXRcfVeRJJL+bW90gl0yDA=", 1243 | "dev": true, 1244 | "requires": { 1245 | "duplexer2": "~0.0.2", 1246 | "html-select": "^2.3.5", 1247 | "html-tokenize": "^1.1.1", 1248 | "inherits": "^2.0.0", 1249 | "readable-stream": "^1.0.27-1", 1250 | "through2": "^1.0.0" 1251 | }, 1252 | "dependencies": { 1253 | "through2": { 1254 | "version": "1.1.1", 1255 | "resolved": "https://registry.npmjs.org/through2/-/through2-1.1.1.tgz", 1256 | "integrity": "sha1-CEfLxESfNAVXTb3M2buEG4OsNUU=", 1257 | "dev": true, 1258 | "requires": { 1259 | "readable-stream": ">=1.1.13-1 <1.2.0-0", 1260 | "xtend": ">=4.0.0 <4.1.0-0" 1261 | }, 1262 | "dependencies": { 1263 | "readable-stream": { 1264 | "version": "1.1.14", 1265 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 1266 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 1267 | "dev": true, 1268 | "requires": { 1269 | "core-util-is": "~1.0.0", 1270 | "inherits": "~2.0.1", 1271 | "isarray": "0.0.1", 1272 | "string_decoder": "~0.10.x" 1273 | } 1274 | } 1275 | } 1276 | } 1277 | } 1278 | }, 1279 | "tslib": { 1280 | "version": "1.13.0", 1281 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 1282 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", 1283 | "dev": true 1284 | }, 1285 | "update-check": { 1286 | "version": "1.5.2", 1287 | "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz", 1288 | "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==", 1289 | "dev": true, 1290 | "requires": { 1291 | "registry-auth-token": "3.3.2", 1292 | "registry-url": "3.1.0" 1293 | } 1294 | }, 1295 | "uri-js": { 1296 | "version": "4.2.2", 1297 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1298 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1299 | "dev": true, 1300 | "requires": { 1301 | "punycode": "^2.1.0" 1302 | } 1303 | }, 1304 | "vary": { 1305 | "version": "1.1.2", 1306 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1307 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 1308 | "dev": true 1309 | }, 1310 | "which": { 1311 | "version": "1.3.1", 1312 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1313 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1314 | "dev": true, 1315 | "requires": { 1316 | "isexe": "^2.0.0" 1317 | } 1318 | }, 1319 | "widest-line": { 1320 | "version": "2.0.1", 1321 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", 1322 | "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", 1323 | "dev": true, 1324 | "requires": { 1325 | "string-width": "^2.1.1" 1326 | } 1327 | }, 1328 | "wrap-ansi": { 1329 | "version": "7.0.0", 1330 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1331 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1332 | "dev": true, 1333 | "requires": { 1334 | "ansi-styles": "^4.0.0", 1335 | "string-width": "^4.1.0", 1336 | "strip-ansi": "^6.0.0" 1337 | }, 1338 | "dependencies": { 1339 | "ansi-regex": { 1340 | "version": "5.0.0", 1341 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 1342 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 1343 | "dev": true 1344 | }, 1345 | "ansi-styles": { 1346 | "version": "4.3.0", 1347 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1348 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1349 | "dev": true, 1350 | "requires": { 1351 | "color-convert": "^2.0.1" 1352 | } 1353 | }, 1354 | "color-convert": { 1355 | "version": "2.0.1", 1356 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1357 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1358 | "dev": true, 1359 | "requires": { 1360 | "color-name": "~1.1.4" 1361 | } 1362 | }, 1363 | "color-name": { 1364 | "version": "1.1.4", 1365 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1366 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1367 | "dev": true 1368 | }, 1369 | "is-fullwidth-code-point": { 1370 | "version": "3.0.0", 1371 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1372 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1373 | "dev": true 1374 | }, 1375 | "string-width": { 1376 | "version": "4.2.2", 1377 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", 1378 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", 1379 | "dev": true, 1380 | "requires": { 1381 | "emoji-regex": "^8.0.0", 1382 | "is-fullwidth-code-point": "^3.0.0", 1383 | "strip-ansi": "^6.0.0" 1384 | } 1385 | }, 1386 | "strip-ansi": { 1387 | "version": "6.0.0", 1388 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1389 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1390 | "dev": true, 1391 | "requires": { 1392 | "ansi-regex": "^5.0.0" 1393 | } 1394 | } 1395 | } 1396 | }, 1397 | "wrappy": { 1398 | "version": "1.0.2", 1399 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1400 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1401 | "dev": true 1402 | }, 1403 | "xtend": { 1404 | "version": "4.0.2", 1405 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1406 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 1407 | "dev": true 1408 | }, 1409 | "y18n": { 1410 | "version": "5.0.8", 1411 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1412 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1413 | "dev": true 1414 | }, 1415 | "yallist": { 1416 | "version": "2.1.2", 1417 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 1418 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 1419 | "dev": true 1420 | }, 1421 | "yargs": { 1422 | "version": "16.2.0", 1423 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1424 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1425 | "dev": true, 1426 | "requires": { 1427 | "cliui": "^7.0.2", 1428 | "escalade": "^3.1.1", 1429 | "get-caller-file": "^2.0.5", 1430 | "require-directory": "^2.1.1", 1431 | "string-width": "^4.2.0", 1432 | "y18n": "^5.0.5", 1433 | "yargs-parser": "^20.2.2" 1434 | }, 1435 | "dependencies": { 1436 | "ansi-regex": { 1437 | "version": "5.0.0", 1438 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 1439 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 1440 | "dev": true 1441 | }, 1442 | "is-fullwidth-code-point": { 1443 | "version": "3.0.0", 1444 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1445 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1446 | "dev": true 1447 | }, 1448 | "string-width": { 1449 | "version": "4.2.2", 1450 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", 1451 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", 1452 | "dev": true, 1453 | "requires": { 1454 | "emoji-regex": "^8.0.0", 1455 | "is-fullwidth-code-point": "^3.0.0", 1456 | "strip-ansi": "^6.0.0" 1457 | } 1458 | }, 1459 | "strip-ansi": { 1460 | "version": "6.0.0", 1461 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1462 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1463 | "dev": true, 1464 | "requires": { 1465 | "ansi-regex": "^5.0.0" 1466 | } 1467 | } 1468 | } 1469 | }, 1470 | "yargs-parser": { 1471 | "version": "20.2.9", 1472 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 1473 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 1474 | "dev": true 1475 | } 1476 | } 1477 | } 1478 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-vitals-leaderboard", 3 | "version": "1.2.3", 4 | "description": "The simplest way to understand how your site’s user experience compares to other major sites in your industry.", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "serve -p 3030", 8 | "inline": "html-inline --ignore-images --ignore-links --ignore-scripts -i ./index.html -o ./dist/index.html", 9 | "minify": "html-minifier-terser --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --remove-tag-whitespace --use-short-doctype --minify-css true --minify-js '{\"compress\":{\"drop_console\":\"true\"},\"mangle\":{\"toplevel\":\"true\"}}' --output ./dist/index.html ./dist/index.html", 10 | "copy": "rm -rf dist && mkdir -p dist && cp -rf ./src ./dist/src", 11 | "minify-js": "for filename in ./dist/src/js/*.js; do terser \"${filename}\" --compress --mangle --output \"${filename}\"; done", 12 | "dist": "npm run copy && npm run inline && npm run minify && npm run minify-js && node revision-hash.js" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/pazguille/web-vitals-leaderboard.git" 17 | }, 18 | "author": "Guille Paz ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/pazguille/web-vitals-leaderboard/issues" 22 | }, 23 | "homepage": "https://vitals-leaderboard.pazguill.me", 24 | "devDependencies": { 25 | "html-inline": "^1.2.0", 26 | "html-minifier-terser": "^5.1.1", 27 | "replace-in-file": "^6.2.0", 28 | "rev-hash": "^3.0.0", 29 | "serve": "^11.3.2", 30 | "terser": "^5.7.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /revision-hash.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const revisionHash = require('rev-hash'); 3 | const replace = require('replace-in-file'); 4 | 5 | const map = {}; 6 | const dir = './dist/src/js'; 7 | const files = fs.readdirSync(dir); 8 | 9 | files.forEach((file) => { 10 | if (file.endsWith('.js')) { 11 | const hash = revisionHash(fs.readFileSync(`${dir}/${file}`)); 12 | map[file] = hash; 13 | fs.renameSync( 14 | `${dir}/${file}`, 15 | `${dir}/${file.replace('.js', `.${hash}.js`)}` 16 | ); 17 | } 18 | }); 19 | 20 | replace.sync({ 21 | files: [ 22 | `${dir}/*.js`, 23 | './dist/index.html', 24 | ], 25 | from: /[A-Za-z]+\.js/gi, 26 | to: (match) => { 27 | if (map[match]) { 28 | return match.replace('.js', `.${map[match]}.js`); 29 | } 30 | return match; 31 | }, 32 | }); 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pazguille/web-vitals-leaderboard/c0d63e2772eb3a090b612597691000cd9923de2d/src/assets/favicon.png -------------------------------------------------------------------------------- /src/assets/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pazguille/web-vitals-leaderboard/c0d63e2772eb3a090b612597691000cd9923de2d/src/assets/loading.gif -------------------------------------------------------------------------------- /src/js/ErrorMessage.js: -------------------------------------------------------------------------------- 1 | import store from './store.js'; 2 | 3 | export default class ErrorMessage { 4 | constructor() { 5 | this.$node = document.querySelector('#error-message'); 6 | } 7 | 8 | toggleVisibility() { 9 | const { leaderboardError, leaderboardLoaded } = store.getState(); 10 | if (leaderboardError) { 11 | this.$node.removeAttribute('hidden'); 12 | } else if (!leaderboardLoaded) { 13 | this.$node.setAttribute('hidden', 'hidden'); 14 | } 15 | } 16 | 17 | init() { 18 | store.subscribe(this.toggleVisibility.bind(this)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/js/Filter.js: -------------------------------------------------------------------------------- 1 | import store from './store.js'; 2 | 3 | export default class Filter { 4 | constructor() { 5 | this.$node = document.querySelector('#filters'); 6 | this.$device = document.querySelector('#device-filter'); 7 | } 8 | 9 | toggleVisibility() { 10 | const { leaderboardLoaded, running, leaderboardError } = store.getState(); 11 | if (leaderboardLoaded && running) { 12 | this.$node.setAttribute('hidden', 'hidden'); 13 | return; 14 | } 15 | 16 | if (leaderboardLoaded && !leaderboardError) { 17 | this.$node.removeAttribute('hidden'); 18 | return; 19 | } 20 | 21 | if (leaderboardLoaded === false) { 22 | this.$node.setAttribute('hidden', 'hidden'); 23 | return; 24 | } 25 | } 26 | 27 | addEvents() { 28 | this.$device.addEventListener('change', function() { 29 | store.dispatch({ 30 | type: 'CHANGE_DEVICE', 31 | value: this.value, 32 | }); 33 | }); 34 | } 35 | 36 | init() { 37 | this.addEvents(); 38 | store.subscribe(this.toggleVisibility.bind(this)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/js/Leaderboard.js: -------------------------------------------------------------------------------- 1 | import store from './store.js'; 2 | import { resultTemplate, skeletonTemplate } from './templates.js'; 3 | 4 | export default class LeaderboardResults { 5 | constructor() { 6 | this.$node = document.querySelector('table'); 7 | this.$results = document.querySelector('#content'); 8 | } 9 | 10 | sortBy(metric) { 11 | return (a, b) => { 12 | if ( a.metrics === null) { 13 | return 1; 14 | } 15 | if (b.metrics === null) { 16 | return -1; 17 | } 18 | if (a.metrics[metric].value < b.metrics[metric].value) { 19 | return -1; 20 | } 21 | if (a.metrics[metric].value > b.metrics[metric].value) { 22 | return 1; 23 | } 24 | return 0; 25 | } 26 | } 27 | 28 | fetchData(data) { 29 | if (data.urls) { 30 | data.urls = data.urls.map((url) => encodeURIComponent(url)).join(','); 31 | } else if (data.origins) { 32 | data.origins = data.origins.map((url) => encodeURIComponent(url)).join(','); 33 | } 34 | const query = Object.keys(data).map(key => key + '=' + data[key]).join('&'); 35 | return fetch(`https://crux.pazguille.me/api/web-vitals?${query}`) 36 | .then(res => res.json()) 37 | .then((results) => { 38 | if (results.code) { 39 | store.dispatch({ type: 'LEADERBOARD_LOADED', error: true }); 40 | return; 41 | } 42 | store.dispatch({ type: 'LEADERBOARD_LOADED', results }); 43 | }) 44 | .catch(() => { 45 | store.dispatch({ type: 'LEADERBOARD_LOADED', error: true }); 46 | }) 47 | } 48 | 49 | toggleVisibility() { 50 | const { running, from, urls, filters, leaderboardLoaded, results } = store.getState(); 51 | 52 | if (running) { 53 | this.fetchData({ 54 | [from]: urls, 55 | device: filters.device, 56 | }); 57 | this.$node.setAttribute('hidden', 'hidden'); 58 | return; 59 | } 60 | 61 | if (leaderboardLoaded && results) { 62 | const html = results 63 | .sort(this.sortBy('LCP')) 64 | .map((result, index) => resultTemplate(result, index+=1)); 65 | this.$node.removeAttribute('hidden'); 66 | this.$results.innerHTML = ''; 67 | this.$results.insertAdjacentHTML('beforeend', html.join('')); 68 | this.$node.removeAttribute('hidden'); 69 | return; 70 | } 71 | 72 | if (leaderboardLoaded === false && urls.length > 0) { 73 | this.$node.removeAttribute('hidden'); 74 | this.$results.insertAdjacentHTML('beforeend', skeletonTemplate(urls[urls.length-1])); 75 | return; 76 | } 77 | 78 | if (leaderboardLoaded === false && urls.length === 0) { 79 | this.$results.innerHTML = ''; 80 | this.$node.setAttribute('hidden', 'hidden'); 81 | window.history.replaceState({}, '', `${location.pathname}`); 82 | return; 83 | } 84 | } 85 | 86 | init() { 87 | store.subscribe(this.toggleVisibility.bind(this)) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/js/LeaderboardControl.js: -------------------------------------------------------------------------------- 1 | import store from './store.js'; 2 | 3 | export default class LeaderboardControl { 4 | constructor() { 5 | this.$node = document.querySelector('form'); 6 | this.$from = document.querySelector('#from'); 7 | this.$input = document.querySelector('#url'); 8 | this.$run = document.querySelector('#run'); 9 | this.$reset = document.querySelector('#reset'); 10 | } 11 | 12 | toggleDisabled() { 13 | const { leaderboardLoaded, urls } = store.getState(); 14 | if (leaderboardLoaded === false && urls.length >= 1) { 15 | this.$run.removeAttribute('disabled'); 16 | this.$reset.removeAttribute('disabled'); 17 | return; 18 | } 19 | 20 | if (leaderboardLoaded === false) { 21 | this.$run.setAttribute('disabled', 'disabled'); 22 | this.$reset.setAttribute('disabled', 'disabled'); 23 | return; 24 | } 25 | } 26 | 27 | addEvents() { 28 | this.$node.addEventListener('submit', (eve) => { 29 | eve.preventDefault(); 30 | 31 | if (eve.submitter.id === 'add') { 32 | const { leaderboardLoaded } = store.getState(); 33 | 34 | if (leaderboardLoaded) { 35 | store.dispatch({ type: 'RESET' }); 36 | } 37 | 38 | store.dispatch({ 39 | type: 'ADD_URL', 40 | value: this.$input.value, 41 | }); 42 | 43 | } else if (eve.submitter.id === 'run') { 44 | store.dispatch({ type: 'LEADERBOARD_UPDATE' }); 45 | 46 | const { from, urls } = store.getState(); 47 | const encodedUrls = urls.map((url) => encodeURIComponent(url)).join(','); 48 | const params = new URLSearchParams(`from=${from}&competitors=${encodedUrls}`); 49 | window.history.replaceState({}, '', `${location.pathname}?${params}`); 50 | 51 | ga('send', { 52 | hitType: 'event', 53 | eventCategory: 'Leaderboard', 54 | eventAction: 'run', 55 | eventLabel: urls.join(','), 56 | }); 57 | } 58 | this.$input.value = ''; 59 | }); 60 | 61 | this.$reset.addEventListener('click', () => store.dispatch({ type: 'RESET' })); 62 | this.$from.addEventListener('change', function () { 63 | store.dispatch({ type: 'CHANGE_FROM', value: this.value }); 64 | }); 65 | } 66 | 67 | init() { 68 | this.addEvents(); 69 | store.subscribe(this.toggleDisabled.bind(this)) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/js/Loading.js: -------------------------------------------------------------------------------- 1 | import store from './store.js'; 2 | 3 | export default class Loading { 4 | constructor() { 5 | this.$node = document.querySelector('#loading'); 6 | } 7 | 8 | toggleVisibility() { 9 | const { running, leaderboardLoaded } = store.getState(); 10 | if (running) { 11 | this.$node.removeAttribute('hidden'); 12 | } else if (leaderboardLoaded) { 13 | this.$node.setAttribute('hidden', 'hidden'); 14 | } 15 | } 16 | 17 | init() { 18 | store.subscribe(this.toggleVisibility.bind(this)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/js/app.js: -------------------------------------------------------------------------------- 1 | import store from './store.js'; 2 | import LeaderboardResults from './Leaderboard.js'; 3 | import Loading from './Loading.js'; 4 | import LeaderboardControl from './LeaderboardControl.js'; 5 | import Filter from './Filter.js'; 6 | import ErrorMessage from './ErrorMessage.js'; 7 | 8 | const demoUrls = [ 9 | 'https://www.cnn.com', 10 | 'https://www.nytimes.com', 11 | 'https://www.foxnews.com', 12 | 'https://www.theguardian.com/international', 13 | 'https://www.washingtonpost.com', 14 | ]; 15 | 16 | export default function bootApp() { 17 | new LeaderboardResults().init(); 18 | new Loading().init(); 19 | 20 | requestIdleCallback(() => { 21 | new LeaderboardControl().init(); 22 | new Filter().init(); 23 | new ErrorMessage().init(); 24 | }); 25 | 26 | const url = new URL(window.location.href); 27 | const params = new URLSearchParams(url.search); 28 | 29 | if (params.has('competitors')) { 30 | store.dispatch({ 31 | type: 'LEADERBOARD_UPDATE', 32 | urls: params.get('competitors').split(','), 33 | from: params.get('from'), 34 | }); 35 | const { urls } = store.getState(); 36 | requestIdleCallback(() => { 37 | ga('send', { 38 | hitType: 'event', 39 | eventCategory: 'Leaderboard', 40 | eventAction: 'share', 41 | eventLabel: urls.join(','), 42 | }); 43 | }); 44 | } else { 45 | store.dispatch({ 46 | type: 'LEADERBOARD_UPDATE', 47 | urls: demoUrls, 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/js/store.js: -------------------------------------------------------------------------------- 1 | import unistore from 'https://unpkg.com/unistore@3.5.2/dist/unistore.es.js'; 2 | 3 | const initialState = { 4 | urls: [], 5 | from: 'urls', 6 | running: false, 7 | results: null, 8 | leaderboardLoaded: false, 9 | leaderboardError: false, 10 | filters: { 11 | device: 'PHONE', 12 | } 13 | }; 14 | 15 | function leaderboard(state = {}, action) { 16 | const newState = { ...state }; 17 | 18 | switch (action.type) { 19 | case 'LEADERBOARD_UPDATE': 20 | newState.running = true; 21 | 22 | if (action.urls) { 23 | newState.urls = action.urls; 24 | } 25 | if (action.from) { 26 | newState.from = action.from; 27 | } 28 | return newState; 29 | 30 | case 'LEADERBOARD_LOADED': 31 | newState.running = false; 32 | newState.leaderboardLoaded = true; 33 | newState.results = action.results || null; 34 | newState.leaderboardError = action.error || false; 35 | return newState; 36 | 37 | case 'CHANGE_DEVICE': 38 | newState.running = true; 39 | newState.results = null; 40 | newState.filters.device = action.value; 41 | return newState; 42 | 43 | case 'CHANGE_FROM': 44 | return { 45 | ...initialState, 46 | from: action.value, 47 | }; 48 | 49 | case 'ADD_URL': 50 | const value = Array.isArray(action.value) ? action.value : [action.value]; 51 | newState.urls = [...state.urls, ...value]; 52 | newState.running = false; 53 | newState.results = null; 54 | newState.leaderboardLoaded = false; 55 | return newState; 56 | 57 | case 'REMOVE_URL': 58 | newState.urls = state.urls.filter(url => url !== action.value); 59 | newState.running = false; 60 | newState.results = null; 61 | newState.leaderboardLoaded = false; 62 | return newState; 63 | 64 | case 'RESET': 65 | return initialState; 66 | 67 | default: 68 | return newState; 69 | } 70 | } 71 | 72 | const store = unistore(initialState); 73 | 74 | // Create a dispatch method like Redux 75 | store.dispatch = store.action(leaderboard); 76 | 77 | export default store; 78 | -------------------------------------------------------------------------------- /src/js/templates.js: -------------------------------------------------------------------------------- 1 | const positions = ['🥇', '🥈', '🥉']; 2 | const healthy = { 3 | FCP: (value) => value <= 1.8 ? 'fast' : (value >= 3 ? 'fail' : 'average'), 4 | LCP: (value) => value <= 2.5 ? 'fast' : (value >= 4 ? 'fail' : 'average'), 5 | INP: (value) => value <= 200 ? 'fast' : (value >= 500 ? 'fail' : 'average'), 6 | CLS: (value) => value <= 0.1 ? 'fast' : (value >= 0.25 ? 'fail' : 'average'), 7 | }; 8 | 9 | function getSiteInfo(url) { 10 | const { host } = new URL(url); 11 | return ` 12 | 13 |
14 | ${host.split('.')[1].toUpperCase()} 15 | ${url} 16 |
17 | ` 18 | } 19 | 20 | export function resultTemplate(site, position) { 21 | const url = site.origin || site.url; 22 | 23 | if (site.metrics === null) { 24 | return noDataTemplate(url, site.error); 25 | } 26 | 27 | const fcp = site.metrics.FCP && (site.metrics.FCP.value/1000).toFixed(2) || '-'; 28 | const lcp = site.metrics.LCP && (site.metrics.LCP.value/1000).toFixed(2) || '-'; 29 | const cls = site.metrics.CLS && site.metrics.CLS.value || '-'; 30 | const inp = site.metrics.INP && site.metrics.INP.value || '-'; 31 | 32 | return (` 33 | 34 | ${positions[position-1] || position} 35 | ${getSiteInfo(url)} 36 | ${fcp}s 37 | ${lcp}s 38 | ${cls} 39 | ${inp}ms 40 | 41 | `); 42 | } 43 | 44 | export function skeletonTemplate(url) { 45 | return (` 46 | 47 | - 48 | ${getSiteInfo(url)} 49 | - 50 | - 51 | - 52 | - 53 | - 54 | 55 | `); 56 | } 57 | 58 | export function noDataTemplate(url, message) { 59 | return (` 60 | 61 | - 62 | ${getSiteInfo(url)} 63 | ${message} 64 | 65 | `); 66 | } 67 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | table-layout: fixed; 4 | border-collapse: collapse; 5 | margin: 0 auto; 6 | text-align: center; 7 | min-height: 100vh; 8 | color: #333333; 9 | font-size: 16px; 10 | } 11 | 12 | abbr { 13 | text-decoration: none; 14 | } 15 | 16 | a { 17 | color: #616161; 18 | } 19 | 20 | h1 { 21 | margin: 0; 22 | font-size: 64px; 23 | line-height: 1em; 24 | margin: 25px 0; 25 | font-weight: 500; 26 | } 27 | 28 | h1 span { 29 | color: #6ACDC0; 30 | } 31 | 32 | h2 { 33 | font-size: 24px; 34 | font-weight: 300; 35 | max-width: 700px; 36 | margin: 20px auto 40px; 37 | padding: 0 10px; 38 | } 39 | 40 | input, 41 | select, 42 | button { 43 | display: inline-block; 44 | vertical-align: middle; 45 | border: 1px solid transparent; 46 | } 47 | 48 | select { 49 | appearance: none; 50 | border: none; 51 | padding: 0.6em 1.2em; 52 | color: #333333; 53 | font-size: 16px; 54 | text-transform: uppercase; 55 | border-radius: 3px; 56 | background-color: transparent; 57 | background-image: url("data:image/svg+xml;utf8,"); 58 | background-size: 12px; 59 | background-position: 95% .9em; 60 | background-repeat: no-repeat; 61 | } 62 | 63 | button { 64 | font-size: 16px; 65 | text-transform: uppercase; 66 | cursor: pointer; 67 | border-radius: 3px; 68 | height: auto; 69 | line-height: 23px; 70 | text-decoration: none; 71 | white-space: nowrap; 72 | box-sizing: border-box; 73 | padding: 0.5em 1em; 74 | background-color: #322241; 75 | border-color: #664584; 76 | color: white; 77 | } 78 | 79 | button[type="reset"] { 80 | background-color: transparent; 81 | border-color: #664584; 82 | } 83 | 84 | button[disabled] { 85 | background-color: #251931; 86 | border-color: #322241; 87 | color: #C1A9D8; 88 | cursor: not-allowed; 89 | } 90 | 91 | input[type="url"] { 92 | border-left-color: #D4D4D4; 93 | color: #333333; 94 | font-size: 18px; 95 | height: 58px; 96 | text-align: 58px; 97 | padding: 0 20px; 98 | } 99 | 100 | :focus { 101 | outline-color: #664584; 102 | } 103 | 104 | button:focus, 105 | input:focus, 106 | select:focus { 107 | outline: 0; 108 | box-shadow: 0 0 10px 5px #664584; 109 | } 110 | 111 | .heroo { 112 | background-color: #2B2B52; 113 | color: lightgray; 114 | padding: 20px 0px 150px; 115 | } 116 | 117 | .heroo a { 118 | color: lightgray; 119 | } 120 | 121 | .heroo h3 { 122 | font-weight: 400; 123 | } 124 | 125 | .primary-controls { 126 | background-color: white; 127 | box-shadow: 0px 3px 24px rgba(0, 0, 0, 0.36); 128 | border-radius: 32px; 129 | max-width: 700px; 130 | padding: 5px 0; 131 | margin: 0 auto; 132 | } 133 | 134 | .add-button { 135 | width: 58px; 136 | height: 58px; 137 | border-radius: 100%; 138 | border: none; 139 | padding: 0; 140 | position: relative; 141 | right: -3px; 142 | } 143 | 144 | .results { 145 | position: relative; 146 | top: -120px; 147 | padding: 10px 0 30px; 148 | 149 | box-shadow: 0px 4px 28px rgba(0, 0, 0, 0.12); 150 | border-radius: 6px; 151 | background: white; 152 | max-width: 95%; 153 | margin: 0 auto; 154 | 155 | min-height: 170px; 156 | z-index: 1; 157 | } 158 | 159 | .loading { 160 | position: absolute; 161 | top: 0; 162 | left: 50%; 163 | margin-left: -100px; 164 | z-index: -1; 165 | } 166 | 167 | .filters { 168 | margin: 15px 0; 169 | } 170 | 171 | table { 172 | border-spacing: 0; 173 | border-collapse: collapse; 174 | margin: 30px auto 10px; 175 | width: 90%; 176 | } 177 | 178 | tbody { 179 | text-align: left; 180 | } 181 | 182 | tbody tr { 183 | background-color: #ffffff; 184 | } 185 | 186 | tbody tr:nth-child(even) { 187 | background-color: #f6f6f6; 188 | } 189 | 190 | th { 191 | font-size: 12px; 192 | text-transform: uppercase; 193 | padding: 5px 0 15px; 194 | } 195 | 196 | td { 197 | padding: 1rem 1rem 1rem 2rem; 198 | } 199 | 200 | .logo { 201 | border-radius: 4px; 202 | float: left; 203 | display: block; 204 | } 205 | 206 | .site { 207 | margin-left: 40px; 208 | min-width: 300px; 209 | } 210 | 211 | .site strong { 212 | margin: 0 0 3px; 213 | font-size: 12px; 214 | } 215 | 216 | .url { 217 | white-space: nowrap; 218 | max-width: 400px; 219 | text-overflow: ellipsis; 220 | overflow: hidden; 221 | display: block; 222 | font-weight: 400; 223 | } 224 | 225 | .ranking { 226 | text-align: center; 227 | } 228 | 229 | .rank-1 { 230 | font-size: 3em; 231 | } 232 | 233 | .rank-2 { 234 | font-size: 2.5em; 235 | } 236 | 237 | .rank-3 { 238 | font-size: 2em; 239 | } 240 | 241 | .fast, 242 | .average, 243 | .fail { 244 | font-weight: 500; 245 | border-style: inset; 246 | text-align: right; 247 | font-size: 15px; 248 | border: 3px solid white !important; 249 | } 250 | 251 | .fast { 252 | background-color: #6ACDC0; 253 | border-color: #6ACDC0; 254 | } 255 | .average { 256 | background-color: #FFF292; 257 | border-color: #FFF292; 258 | } 259 | .fail { 260 | background-color: #FF8484; 261 | border-color: #FF8484; 262 | } 263 | 264 | .data-not-found { 265 | text-align: center; 266 | text-transform: capitalize; 267 | } 268 | 269 | hr { 270 | width: 50%; 271 | margin: 20px auto; 272 | border: none; 273 | border-top: 1px solid #f5f5f5; 274 | } 275 | 276 | .faq { 277 | position: relative; 278 | top: -50px; 279 | padding: 10px 50px 30px; 280 | margin: 0 auto; 281 | background-color: #f9f9f9; 282 | } 283 | 284 | .faq h2 { 285 | text-align: center; 286 | margin-bottom: 15px; 287 | } 288 | 289 | .faq details { 290 | display: inline-block; 291 | text-align: left; 292 | width: 49%; 293 | vertical-align: top; 294 | margin: 20px 0; 295 | } 296 | 297 | .faq summary { 298 | font-size: 18px; 299 | font-weight: 300; 300 | margin: 10px 0; 301 | } 302 | 303 | .faq p { 304 | color: #616161; 305 | font-size: 14px; 306 | line-height: 1.6em; 307 | margin: 0; 308 | width: 75%; 309 | } 310 | 311 | footer { 312 | margin-top: 20px; 313 | font-size: 12px; 314 | } 315 | 316 | .copyright { 317 | font-size: 9px; 318 | position: absolute; 319 | top: 5px; 320 | right: 5px; 321 | } 322 | 323 | .copyright, 324 | .copyright a { 325 | color:lightgray; 326 | } 327 | 328 | /* 329 | * Media queries 330 | */ 331 | @media (min-width: 320px) and (max-width: 480px) { 332 | h1 { 333 | font-size: 36px; 334 | margin: auto; 335 | } 336 | 337 | h2 { 338 | font-size: 20px; 339 | } 340 | 341 | td { 342 | padding: .8rem; 343 | } 344 | 345 | table th:nth-child(1), 346 | .ranking, 347 | .site { 348 | display: none; 349 | } 350 | 351 | .faq details, 352 | .faq p { 353 | width: 100%; 354 | } 355 | 356 | input[type="url"] { 357 | width: 170px; 358 | } 359 | 360 | } 361 | --------------------------------------------------------------------------------