├── .gitignore ├── README.md ├── headless-browser ├── README.md ├── hydrate.ts ├── lib │ ├── hydrate.js │ └── serialize.js ├── package-lock.json ├── package.json ├── prerender.ts ├── public │ ├── components │ │ ├── color-text.js │ │ ├── my-hello.js │ │ └── real-counter.js │ └── pages │ │ ├── index.html │ │ └── index.ssr.html ├── render.ts ├── serialize.ts ├── server.ts └── tsconfig.json ├── package-lock.json ├── package.json ├── skate-ssr ├── README.md ├── package-lock.json ├── package.json ├── prerender.ts ├── public │ ├── components │ │ ├── color-text.js │ │ ├── color-text_comp.js │ │ ├── my-hello.js │ │ ├── my-hello_comp.js │ │ ├── real-counter.js │ │ └── real-counter_comp.js │ └── pages │ │ ├── index.html │ │ └── index.ssr.html ├── render.ts ├── server.ts ├── tsconfig.json └── utils.ts ├── stringify.ts └── test.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wc-prerender 2 | My master thesis project regarding pre-rendering of Web Components 3 | 4 | ## Abstract 5 | Web Components are getting more popular and the community came across some disadvantages like first load performance. Server-side rendering (SSR) could improve this. The idea of server-side rendering is nothing new but it has been said that Web Components can not be rendered on the server due to the lack of a declarative shadow root representation. 6 | This project evaluates a solution to pre-render Web Components and compares its performance to other possible implementations. 7 | 8 | ## Goal 9 | 10 | The key intention, which might be obsolete these days, is that functionality should be usable without JavaScript. As of today, about 95% of all websites use JavaScript which makes the fallback for non-JavaScript websites less important. Nonetheless, this means that Web Components should work without JavaScript to some extent. Although, the definition of "work" here should be narrowed down to the content representation only which is hidden when a Web Component is used while JavaScript is disabled. Another aspect is to minimize the time until the First Contentful Paint(FCP) happens in the browser. This means reducing the workload a browser has to do upfront. Which includes interpreting and displaying a component. The time needed to show a custom component will always be greater compared to a vanilla HTML element, due to their nature and how they are being parsed by the browser. 11 | 12 | Therefore, the goal is to pre-render Web Components to provide a vanilla HTML alternative until the actual component is ready to display and exchange it afterwards. 13 | 14 | 15 | ## Installation 16 | The code was developed using Node.js v10.15.3 and npm v6.9.0. 17 | To install and test the source code, clone the repository and execute `npm install` in each directory to make sure all dependencies are being installed correctly. 18 | 19 | ## Running 20 | 21 | The command `npm run render` triggers a single rendering which takes an example HTML file and creates a new, serialized version of it in the "public/pages" directory. 22 | A server can be started using `npm start`. This will serve a local server at http://localhost:3001/ssr for the headless browser or http://localhost:3002/ssr for the DOM API. The route `/avg` keeps track and displays the current average measurement. 23 | 24 | ### Appendix 25 | Stencil Approach 26 | https://github.com/ionic-team/stencil/blob/143e07ea58a67c9bc16209c1c2a5d32a13f50b60/src/mock-doc/serialize-node.ts 27 | -------------------------------------------------------------------------------- /headless-browser/README.md: -------------------------------------------------------------------------------- 1 | # Headless Browser 2 | This utilizes a headless browser(puppeteer) to pre-render the web component. 3 | 4 | [Article](https://medium.com/@treshugart/%C3%A5server-side-rendering-web-components-e5df705f3f48) 5 | 6 | ## Run 7 | Enter `npm run render` in your terminal. 8 | 9 | ## Serve 10 | Enter `npm start` in your terminal to startup a server. 11 | -------------------------------------------------------------------------------- /headless-browser/hydrate.ts: -------------------------------------------------------------------------------- 1 | function hydrate() { 2 | // @ts-ignore 3 | const elements = [...document.querySelectorAll('[data-ssr="serialized"]')]; 4 | 5 | for (const el of elements) { 6 | const lightDom = el.querySelector('[type="ssr-light-dom"]'); 7 | const dataElement = el.querySelector('[type="ssr-data"]'); 8 | const dataContent = dataElement && dataElement.innerText; 9 | const data = dataContent && JSON.parse(dataContent); 10 | 11 | // replace the whole light DOM 12 | el.innerHTML = lightDom.innerHTML; 13 | 14 | //re-apply attributes 15 | data && Object.keys(data).forEach(key => { 16 | el[key] = data[key]; 17 | }); 18 | 19 | el.setAttribute('data-ssr', 'hydrated'); 20 | } 21 | } 22 | 23 | export const rehydrate = ``; 24 | -------------------------------------------------------------------------------- /headless-browser/lib/hydrate.js: -------------------------------------------------------------------------------- 1 | function hydrate() { 2 | [...document.querySelectorAll('[data-ssr="serialized"]')] 3 | .forEach(el => { 4 | const lightDom = el.querySelector('[type="ssr-light-dom"]') 5 | const lightDomContent = lightDom && lightDom.content 6 | const dataElement = el.querySelector('[type="ssr-data"]') 7 | const dataContent = dataElement && dataElement.innerText 8 | const data = dataContent && JSON.parse(dataContent) 9 | 10 | el.childNodes.forEach(node => { 11 | if (node !== lightDom) { 12 | node.parentElement.removeChild(node) 13 | } 14 | }) 15 | 16 | lightDomContent && lightDomContent.childNodes.forEach(node => el.appendChild(node)) 17 | lightDom && lightDom.parentElement.removeChild(lightDom) 18 | dataElement && dataElement.parentElement && dataElement.parentElement.removeChild(dataElement) 19 | data && Object.keys(data).forEach(key => { 20 | el[key] = data[key] 21 | }) 22 | 23 | el.setAttribute('data-ssr', 'hydrated') 24 | } 25 | ) 26 | } 27 | 28 | module.exports = `` -------------------------------------------------------------------------------- /headless-browser/lib/serialize.js: -------------------------------------------------------------------------------- 1 | module.exports = function serialize (rootNode) { 2 | function serializeNode (node) { 3 | try { 4 | const lightDomNodes = node.childNodes 5 | const lightDomHtml = node.innerHTML 6 | const templateDom = document.createElement('template') 7 | const scriptData = document.createElement('script') 8 | const slot = node.shadowRoot.querySelector('slot') 9 | const attributesProperties = node.getAttributeNames() 10 | .filter(name => name !== 'data-ssr') 11 | .reduce((obj, name) => { 12 | return {...obj, [name]: node[name] } 13 | }, {}) 14 | 15 | templateDom.setAttribute('type', 'ssr-light-dom') 16 | 17 | templateDom.innerHTML = lightDomHtml 18 | 19 | scriptData.setAttribute('type', 'ssr-data') 20 | 21 | scriptData.innerHTML = JSON.stringify(attributesProperties) 22 | 23 | // move light nodes into shadowDom 24 | lightDomNodes.forEach(lightNode => slot.parentNode.insertBefore(lightNode, slot)) 25 | 26 | // move shadowDom into root node 27 | node.shadowRoot.childNodes.forEach(shadowNode => node.appendChild(shadowNode)) 28 | 29 | // remove slot element 30 | if (slot) { 31 | slot.parentNode.removeChild(slot) 32 | } 33 | 34 | // serialize custom element child nodes 35 | serialize(node) 36 | 37 | // add original lightDom as template 38 | if (templateDom.innerHTML !== '') { 39 | node.appendChild(templateDom) 40 | } 41 | 42 | if (scriptData.innerHTML !== '{}') { 43 | node.appendChild(scriptData) 44 | } 45 | 46 | node.setAttribute('data-ssr', 'serialized') 47 | } catch (err) { 48 | console.log('error:', err) 49 | } 50 | } 51 | 52 | [...rootNode.querySelectorAll('*')] 53 | .filter(element => /-/.test(element.nodeName)) 54 | .forEach(serializeNode) 55 | } 56 | -------------------------------------------------------------------------------- /headless-browser/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wc-prerender", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "12.12.7", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.7.tgz", 10 | "integrity": "sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==" 11 | }, 12 | "@types/puppeteer": { 13 | "version": "2.0.0", 14 | "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-2.0.0.tgz", 15 | "integrity": "sha512-QPHXIcaPcijMbvizoM7PRL97Rm+aM8J2DmgTz2tt79b15PqbyeaCppYonvPLHQ/Q5ea92BUHDpv4bsqtiTy8kQ==", 16 | "requires": { 17 | "@types/node": "*" 18 | } 19 | }, 20 | "accepts": { 21 | "version": "1.3.7", 22 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 23 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 24 | "dev": true, 25 | "requires": { 26 | "mime-types": "~2.1.24", 27 | "negotiator": "0.6.2" 28 | } 29 | }, 30 | "agent-base": { 31 | "version": "4.3.0", 32 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", 33 | "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", 34 | "dev": true, 35 | "requires": { 36 | "es6-promisify": "^5.0.0" 37 | } 38 | }, 39 | "array-flatten": { 40 | "version": "1.1.1", 41 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 42 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", 43 | "dev": true 44 | }, 45 | "async-limiter": { 46 | "version": "1.0.1", 47 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 48 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", 49 | "dev": true 50 | }, 51 | "balanced-match": { 52 | "version": "1.0.0", 53 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 54 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 55 | "dev": true 56 | }, 57 | "body-parser": { 58 | "version": "1.19.0", 59 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 60 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 61 | "dev": true, 62 | "requires": { 63 | "bytes": "3.1.0", 64 | "content-type": "~1.0.4", 65 | "debug": "2.6.9", 66 | "depd": "~1.1.2", 67 | "http-errors": "1.7.2", 68 | "iconv-lite": "0.4.24", 69 | "on-finished": "~2.3.0", 70 | "qs": "6.7.0", 71 | "raw-body": "2.4.0", 72 | "type-is": "~1.6.17" 73 | } 74 | }, 75 | "brace-expansion": { 76 | "version": "1.1.11", 77 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 78 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 79 | "dev": true, 80 | "requires": { 81 | "balanced-match": "^1.0.0", 82 | "concat-map": "0.0.1" 83 | } 84 | }, 85 | "buffer-from": { 86 | "version": "1.1.1", 87 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 88 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 89 | "dev": true 90 | }, 91 | "bytes": { 92 | "version": "3.1.0", 93 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 94 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", 95 | "dev": true 96 | }, 97 | "concat-map": { 98 | "version": "0.0.1", 99 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 100 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 101 | "dev": true 102 | }, 103 | "concat-stream": { 104 | "version": "1.6.2", 105 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 106 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 107 | "dev": true, 108 | "requires": { 109 | "buffer-from": "^1.0.0", 110 | "inherits": "^2.0.3", 111 | "readable-stream": "^2.2.2", 112 | "typedarray": "^0.0.6" 113 | } 114 | }, 115 | "content-disposition": { 116 | "version": "0.5.3", 117 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 118 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 119 | "dev": true, 120 | "requires": { 121 | "safe-buffer": "5.1.2" 122 | } 123 | }, 124 | "content-type": { 125 | "version": "1.0.4", 126 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 127 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 128 | "dev": true 129 | }, 130 | "cookie": { 131 | "version": "0.4.0", 132 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 133 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", 134 | "dev": true 135 | }, 136 | "cookie-signature": { 137 | "version": "1.0.6", 138 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 139 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", 140 | "dev": true 141 | }, 142 | "core-util-is": { 143 | "version": "1.0.2", 144 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 145 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 146 | "dev": true 147 | }, 148 | "debug": { 149 | "version": "2.6.9", 150 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 151 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 152 | "dev": true, 153 | "requires": { 154 | "ms": "2.0.0" 155 | } 156 | }, 157 | "depd": { 158 | "version": "1.1.2", 159 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 160 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 161 | "dev": true 162 | }, 163 | "destroy": { 164 | "version": "1.0.4", 165 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 166 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 167 | "dev": true 168 | }, 169 | "ee-first": { 170 | "version": "1.1.1", 171 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 172 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 173 | "dev": true 174 | }, 175 | "encodeurl": { 176 | "version": "1.0.2", 177 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 178 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 179 | "dev": true 180 | }, 181 | "es6-promise": { 182 | "version": "4.2.8", 183 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", 184 | "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", 185 | "dev": true 186 | }, 187 | "es6-promisify": { 188 | "version": "5.0.0", 189 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 190 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 191 | "dev": true, 192 | "requires": { 193 | "es6-promise": "^4.0.3" 194 | } 195 | }, 196 | "escape-html": { 197 | "version": "1.0.3", 198 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 199 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 200 | "dev": true 201 | }, 202 | "etag": { 203 | "version": "1.8.1", 204 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 205 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 206 | "dev": true 207 | }, 208 | "express": { 209 | "version": "4.17.1", 210 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 211 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 212 | "dev": true, 213 | "requires": { 214 | "accepts": "~1.3.7", 215 | "array-flatten": "1.1.1", 216 | "body-parser": "1.19.0", 217 | "content-disposition": "0.5.3", 218 | "content-type": "~1.0.4", 219 | "cookie": "0.4.0", 220 | "cookie-signature": "1.0.6", 221 | "debug": "2.6.9", 222 | "depd": "~1.1.2", 223 | "encodeurl": "~1.0.2", 224 | "escape-html": "~1.0.3", 225 | "etag": "~1.8.1", 226 | "finalhandler": "~1.1.2", 227 | "fresh": "0.5.2", 228 | "merge-descriptors": "1.0.1", 229 | "methods": "~1.1.2", 230 | "on-finished": "~2.3.0", 231 | "parseurl": "~1.3.3", 232 | "path-to-regexp": "0.1.7", 233 | "proxy-addr": "~2.0.5", 234 | "qs": "6.7.0", 235 | "range-parser": "~1.2.1", 236 | "safe-buffer": "5.1.2", 237 | "send": "0.17.1", 238 | "serve-static": "1.14.1", 239 | "setprototypeof": "1.1.1", 240 | "statuses": "~1.5.0", 241 | "type-is": "~1.6.18", 242 | "utils-merge": "1.0.1", 243 | "vary": "~1.1.2" 244 | } 245 | }, 246 | "extract-zip": { 247 | "version": "1.6.7", 248 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", 249 | "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", 250 | "dev": true, 251 | "requires": { 252 | "concat-stream": "1.6.2", 253 | "debug": "2.6.9", 254 | "mkdirp": "0.5.1", 255 | "yauzl": "2.4.1" 256 | } 257 | }, 258 | "fd-slicer": { 259 | "version": "1.0.1", 260 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", 261 | "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", 262 | "dev": true, 263 | "requires": { 264 | "pend": "~1.2.0" 265 | } 266 | }, 267 | "finalhandler": { 268 | "version": "1.1.2", 269 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 270 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 271 | "dev": true, 272 | "requires": { 273 | "debug": "2.6.9", 274 | "encodeurl": "~1.0.2", 275 | "escape-html": "~1.0.3", 276 | "on-finished": "~2.3.0", 277 | "parseurl": "~1.3.3", 278 | "statuses": "~1.5.0", 279 | "unpipe": "~1.0.0" 280 | } 281 | }, 282 | "forwarded": { 283 | "version": "0.1.2", 284 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 285 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", 286 | "dev": true 287 | }, 288 | "fresh": { 289 | "version": "0.5.2", 290 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 291 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 292 | "dev": true 293 | }, 294 | "fs.realpath": { 295 | "version": "1.0.0", 296 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 297 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 298 | "dev": true 299 | }, 300 | "glob": { 301 | "version": "7.1.6", 302 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 303 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 304 | "dev": true, 305 | "requires": { 306 | "fs.realpath": "^1.0.0", 307 | "inflight": "^1.0.4", 308 | "inherits": "2", 309 | "minimatch": "^3.0.4", 310 | "once": "^1.3.0", 311 | "path-is-absolute": "^1.0.0" 312 | } 313 | }, 314 | "http-errors": { 315 | "version": "1.7.2", 316 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 317 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 318 | "dev": true, 319 | "requires": { 320 | "depd": "~1.1.2", 321 | "inherits": "2.0.3", 322 | "setprototypeof": "1.1.1", 323 | "statuses": ">= 1.5.0 < 2", 324 | "toidentifier": "1.0.0" 325 | } 326 | }, 327 | "https-proxy-agent": { 328 | "version": "3.0.1", 329 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", 330 | "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", 331 | "dev": true, 332 | "requires": { 333 | "agent-base": "^4.3.0", 334 | "debug": "^3.1.0" 335 | }, 336 | "dependencies": { 337 | "debug": { 338 | "version": "3.2.6", 339 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 340 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 341 | "dev": true, 342 | "requires": { 343 | "ms": "^2.1.1" 344 | } 345 | }, 346 | "ms": { 347 | "version": "2.1.2", 348 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 349 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 350 | "dev": true 351 | } 352 | } 353 | }, 354 | "iconv-lite": { 355 | "version": "0.4.24", 356 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 357 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 358 | "dev": true, 359 | "requires": { 360 | "safer-buffer": ">= 2.1.2 < 3" 361 | } 362 | }, 363 | "inflight": { 364 | "version": "1.0.6", 365 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 366 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 367 | "dev": true, 368 | "requires": { 369 | "once": "^1.3.0", 370 | "wrappy": "1" 371 | } 372 | }, 373 | "inherits": { 374 | "version": "2.0.3", 375 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 376 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 377 | "dev": true 378 | }, 379 | "ipaddr.js": { 380 | "version": "1.9.0", 381 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 382 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", 383 | "dev": true 384 | }, 385 | "isarray": { 386 | "version": "1.0.0", 387 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 388 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 389 | "dev": true 390 | }, 391 | "media-typer": { 392 | "version": "0.3.0", 393 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 394 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 395 | "dev": true 396 | }, 397 | "merge-descriptors": { 398 | "version": "1.0.1", 399 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 400 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", 401 | "dev": true 402 | }, 403 | "methods": { 404 | "version": "1.1.2", 405 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 406 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 407 | "dev": true 408 | }, 409 | "mime": { 410 | "version": "1.6.0", 411 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 412 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 413 | "dev": true 414 | }, 415 | "mime-db": { 416 | "version": "1.40.0", 417 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 418 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", 419 | "dev": true 420 | }, 421 | "mime-types": { 422 | "version": "2.1.24", 423 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 424 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 425 | "dev": true, 426 | "requires": { 427 | "mime-db": "1.40.0" 428 | } 429 | }, 430 | "minimatch": { 431 | "version": "3.0.4", 432 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 433 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 434 | "dev": true, 435 | "requires": { 436 | "brace-expansion": "^1.1.7" 437 | } 438 | }, 439 | "minimist": { 440 | "version": "0.0.8", 441 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 442 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 443 | "dev": true 444 | }, 445 | "mkdirp": { 446 | "version": "0.5.1", 447 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 448 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 449 | "dev": true, 450 | "requires": { 451 | "minimist": "0.0.8" 452 | } 453 | }, 454 | "ms": { 455 | "version": "2.0.0", 456 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 457 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 458 | "dev": true 459 | }, 460 | "negotiator": { 461 | "version": "0.6.2", 462 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 463 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 464 | "dev": true 465 | }, 466 | "on-finished": { 467 | "version": "2.3.0", 468 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 469 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 470 | "dev": true, 471 | "requires": { 472 | "ee-first": "1.1.1" 473 | } 474 | }, 475 | "once": { 476 | "version": "1.4.0", 477 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 478 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 479 | "dev": true, 480 | "requires": { 481 | "wrappy": "1" 482 | } 483 | }, 484 | "parseurl": { 485 | "version": "1.3.3", 486 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 487 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 488 | "dev": true 489 | }, 490 | "path-is-absolute": { 491 | "version": "1.0.1", 492 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 493 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 494 | "dev": true 495 | }, 496 | "path-to-regexp": { 497 | "version": "0.1.7", 498 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 499 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", 500 | "dev": true 501 | }, 502 | "pend": { 503 | "version": "1.2.0", 504 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 505 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", 506 | "dev": true 507 | }, 508 | "process-nextick-args": { 509 | "version": "2.0.1", 510 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 511 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 512 | "dev": true 513 | }, 514 | "progress": { 515 | "version": "2.0.3", 516 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 517 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 518 | "dev": true 519 | }, 520 | "proxy-addr": { 521 | "version": "2.0.5", 522 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 523 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 524 | "dev": true, 525 | "requires": { 526 | "forwarded": "~0.1.2", 527 | "ipaddr.js": "1.9.0" 528 | } 529 | }, 530 | "proxy-from-env": { 531 | "version": "1.0.0", 532 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", 533 | "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", 534 | "dev": true 535 | }, 536 | "puppeteer": { 537 | "version": "2.0.0", 538 | "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-2.0.0.tgz", 539 | "integrity": "sha512-t3MmTWzQxPRP71teU6l0jX47PHXlc4Z52sQv4LJQSZLq1ttkKS2yGM3gaI57uQwZkNaoGd0+HPPMELZkcyhlqA==", 540 | "dev": true, 541 | "requires": { 542 | "debug": "^4.1.0", 543 | "extract-zip": "^1.6.6", 544 | "https-proxy-agent": "^3.0.0", 545 | "mime": "^2.0.3", 546 | "progress": "^2.0.1", 547 | "proxy-from-env": "^1.0.0", 548 | "rimraf": "^2.6.1", 549 | "ws": "^6.1.0" 550 | }, 551 | "dependencies": { 552 | "debug": { 553 | "version": "4.1.1", 554 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 555 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 556 | "dev": true, 557 | "requires": { 558 | "ms": "^2.1.1" 559 | } 560 | }, 561 | "mime": { 562 | "version": "2.4.4", 563 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", 564 | "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", 565 | "dev": true 566 | }, 567 | "ms": { 568 | "version": "2.1.2", 569 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 570 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 571 | "dev": true 572 | } 573 | } 574 | }, 575 | "qs": { 576 | "version": "6.7.0", 577 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 578 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", 579 | "dev": true 580 | }, 581 | "range-parser": { 582 | "version": "1.2.1", 583 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 584 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 585 | "dev": true 586 | }, 587 | "raw-body": { 588 | "version": "2.4.0", 589 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 590 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 591 | "dev": true, 592 | "requires": { 593 | "bytes": "3.1.0", 594 | "http-errors": "1.7.2", 595 | "iconv-lite": "0.4.24", 596 | "unpipe": "1.0.0" 597 | } 598 | }, 599 | "readable-stream": { 600 | "version": "2.3.6", 601 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 602 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 603 | "dev": true, 604 | "requires": { 605 | "core-util-is": "~1.0.0", 606 | "inherits": "~2.0.3", 607 | "isarray": "~1.0.0", 608 | "process-nextick-args": "~2.0.0", 609 | "safe-buffer": "~5.1.1", 610 | "string_decoder": "~1.1.1", 611 | "util-deprecate": "~1.0.1" 612 | } 613 | }, 614 | "rimraf": { 615 | "version": "2.7.1", 616 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 617 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 618 | "dev": true, 619 | "requires": { 620 | "glob": "^7.1.3" 621 | } 622 | }, 623 | "safe-buffer": { 624 | "version": "5.1.2", 625 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 626 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 627 | "dev": true 628 | }, 629 | "safer-buffer": { 630 | "version": "2.1.2", 631 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 632 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 633 | "dev": true 634 | }, 635 | "send": { 636 | "version": "0.17.1", 637 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 638 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 639 | "dev": true, 640 | "requires": { 641 | "debug": "2.6.9", 642 | "depd": "~1.1.2", 643 | "destroy": "~1.0.4", 644 | "encodeurl": "~1.0.2", 645 | "escape-html": "~1.0.3", 646 | "etag": "~1.8.1", 647 | "fresh": "0.5.2", 648 | "http-errors": "~1.7.2", 649 | "mime": "1.6.0", 650 | "ms": "2.1.1", 651 | "on-finished": "~2.3.0", 652 | "range-parser": "~1.2.1", 653 | "statuses": "~1.5.0" 654 | }, 655 | "dependencies": { 656 | "ms": { 657 | "version": "2.1.1", 658 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 659 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 660 | "dev": true 661 | } 662 | } 663 | }, 664 | "serve-static": { 665 | "version": "1.14.1", 666 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 667 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 668 | "dev": true, 669 | "requires": { 670 | "encodeurl": "~1.0.2", 671 | "escape-html": "~1.0.3", 672 | "parseurl": "~1.3.3", 673 | "send": "0.17.1" 674 | } 675 | }, 676 | "setprototypeof": { 677 | "version": "1.1.1", 678 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 679 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", 680 | "dev": true 681 | }, 682 | "statuses": { 683 | "version": "1.5.0", 684 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 685 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 686 | "dev": true 687 | }, 688 | "string_decoder": { 689 | "version": "1.1.1", 690 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 691 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 692 | "dev": true, 693 | "requires": { 694 | "safe-buffer": "~5.1.0" 695 | } 696 | }, 697 | "toidentifier": { 698 | "version": "1.0.0", 699 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 700 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", 701 | "dev": true 702 | }, 703 | "type-is": { 704 | "version": "1.6.18", 705 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 706 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 707 | "dev": true, 708 | "requires": { 709 | "media-typer": "0.3.0", 710 | "mime-types": "~2.1.24" 711 | } 712 | }, 713 | "typedarray": { 714 | "version": "0.0.6", 715 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 716 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 717 | "dev": true 718 | }, 719 | "unpipe": { 720 | "version": "1.0.0", 721 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 722 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 723 | "dev": true 724 | }, 725 | "util-deprecate": { 726 | "version": "1.0.2", 727 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 728 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 729 | "dev": true 730 | }, 731 | "utils-merge": { 732 | "version": "1.0.1", 733 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 734 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 735 | "dev": true 736 | }, 737 | "vary": { 738 | "version": "1.1.2", 739 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 740 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 741 | "dev": true 742 | }, 743 | "wrappy": { 744 | "version": "1.0.2", 745 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 746 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 747 | "dev": true 748 | }, 749 | "ws": { 750 | "version": "6.2.1", 751 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", 752 | "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", 753 | "dev": true, 754 | "requires": { 755 | "async-limiter": "~1.0.0" 756 | } 757 | }, 758 | "yauzl": { 759 | "version": "2.4.1", 760 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", 761 | "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", 762 | "dev": true, 763 | "requires": { 764 | "fd-slicer": "~1.0.1" 765 | } 766 | } 767 | } 768 | } 769 | -------------------------------------------------------------------------------- /headless-browser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wc-prerender", 3 | "version": "1.0.0", 4 | "description": "My master thesis project regarding pre-rendering of Web Components", 5 | "author": "Andreas Hahn", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "ts-node server.ts", 9 | "render": "ts-node prerender.ts" 10 | }, 11 | "dependencies": { 12 | "@types/puppeteer": "^2.0.0" 13 | }, 14 | "devDependencies": { 15 | "express": "^4.17.1", 16 | "puppeteer": "^2.0.0", 17 | "ts-node": "^8.6.2" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/drdreo/wc-prerender.git" 22 | }, 23 | "keywords": [ 24 | "webcomponents", 25 | "prerendering" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /headless-browser/prerender.ts: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | import { render, launched, stopBrowser } from './render'; 5 | import { rehydrate } from './hydrate'; 6 | 7 | const file = '/public/pages/index.html'; 8 | 9 | // need to wait for the browser to launch 10 | launched 11 | .then(() => { 12 | render(file) 13 | .then((pageContent: string) => { 14 | console.log('[Headless Browser] Pre-rendering finished!'); 15 | fs.writeFile( 16 | path.join(__dirname, file.replace('.html', '.ssr.html')), 17 | // add re-hydration script to the end of
18 | pageContent.replace('', `${rehydrate} 11 | 12 |`),
19 | { encoding: 'utf8' },
20 | async (err) => {
21 | if (err) console.log(err);
22 | },
23 | );
24 |
25 | stopBrowser();
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/headless-browser/public/components/color-text.js:
--------------------------------------------------------------------------------
1 | class ColorText extends HTMLElement {
2 | connectedCallback() {
3 | this.attachShadow({mode: "open"});
4 | this.shadowRoot.innerHTML = `
5 |
4 | 5 |
6 | 7 | 8 | 9 | 10 |
18 |