diff --git a/.gitignore b/.gitignore index a735b0f0bb..5c9cf64a20 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,7 @@ prerender-static.html # stencil angular/css/ core/css/ -core/hydrated/ +core/hydrate/ core/loader/ core/www/ .stencil/ diff --git a/core/package.json b/core/package.json index 2dc8340627..2aaada22ff 100644 --- a/core/package.json +++ b/core/package.json @@ -26,6 +26,7 @@ "files": [ "dist/", "css/", + "hydrate/", "loader/" ], "dependencies": { @@ -42,6 +43,7 @@ "aws-sdk": "^2.320.0", "chromedriver": "^2.38.3", "clean-css-cli": "^4.1.11", + "domino": "^2.1.3", "fs-extra": "^8.0.1", "jest": "24.8.0", "jest-cli": "24.8.0", @@ -76,7 +78,7 @@ "lint.ts": "tslint --project .", "lint.ts.fix": "tslint --project . --fix", "prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next", - "prerender.e2e": "node scripts/testing/prerender-e2e.js", + "prerender.e2e": "node scripts/testing/prerender.js", "start": "npm run build.css && stencil build --dev --watch --serve", "test": "stencil test --spec --e2e", "test.spec": "stencil test --spec", diff --git a/core/scripts/testing/prerender.js b/core/scripts/testing/prerender.js new file mode 100644 index 0000000000..13d856a1ba --- /dev/null +++ b/core/scripts/testing/prerender.js @@ -0,0 +1,176 @@ +const fs = require('fs'); +const path = require('path'); +const hydrate = require('../../hydrate'); +const domino = require('domino'); + +let prerenderCount = 0; + +async function prerenderPage(srcIndexFilePath) { + const start = Date.now(); + + try { + await prerenderStatic(srcIndexFilePath); + await prerenderHydrated(srcIndexFilePath); + await prerenderDomino(srcIndexFilePath); + console.log(srcIndexFilePath, ` ${Date.now() - start}ms`); + + } catch (e) { + console.error(`Failed:`, srcIndexFilePath, ` ${Date.now() - start}ms`); + throw e; + } +} + +async function prerenderStatic(srcIndexFilePath) { + const dirPath = path.dirname(srcIndexFilePath); + const srcHtml = fs.readFileSync(srcIndexFilePath, 'utf-8'); + const staticFilePath = path.join(dirPath, 'prerender-static.html'); + + const results = await hydrate.renderToString(srcHtml, { + prettyHtml: true, + removeScripts: true + }); + if (results.diagnostics.some(d => d.type === 'error')) { + throw new Error('staticResults:\n' + results.diagnostics.map(d => d.messageText).join('\n')); + } + fs.writeFileSync(staticFilePath, results.html); + + const dstIndexFilePath = path.join(dirPath, 'prerender.html'); + const prerenderIndexHtml = buildPrerenderIndexHtml(results.title); + fs.writeFileSync(dstIndexFilePath, prerenderIndexHtml); + prerenderCount++; +} + +async function prerenderHydrated(srcIndexFilePath) { + const dirPath = path.dirname(srcIndexFilePath); + const srcHtml = fs.readFileSync(srcIndexFilePath, 'utf-8'); + const hydratedFilePath = path.join(dirPath, 'prerender-hydrated.html'); + const results = await hydrate.renderToString(srcHtml, { + prettyHtml: true + }); + if (results.diagnostics.some(d => d.type === 'error')) { + throw new Error('hydrateResults:\n' + staticResults.diagnostics.map(d => d.messageText).join('\n')); + } + fs.writeFileSync(hydratedFilePath, results.html); + prerenderCount++; +} + +async function prerenderDomino(srcIndexFilePath) { + const dirPath = path.dirname(srcIndexFilePath); + const srcHtml = fs.readFileSync(srcIndexFilePath, 'utf-8'); + const dominoFilePath = path.join(dirPath, 'prerender-domino.html'); + const dominoDoc = domino.createDocument(srcHtml, true); + const results = await hydrate.hydrateDocument(dominoDoc); + if (results.diagnostics.some(d => d.type === 'error')) { + throw new Error('dominoResults:\n' + staticResults.diagnostics.map(d => d.messageText).join('\n')); + } + const dominoHtml = dominoDoc.documentElement.outerHTML; + fs.writeFileSync(dominoFilePath, dominoHtml); + prerenderCount++; +} + +function buildPrerenderIndexHtml(title) { + return ` + + + + ${title} + + + +
+
+ Client + +
+
+ Static + +
+
+ Domino + +
+
+ Hydrated + +
+
+ + +`; +} + +async function prerenderDir(dirPath) { + const items = fs.readdirSync(dirPath); + + for (const item of items) { + const itemPath = path.join(dirPath, item); + const stat = fs.statSync(itemPath); + + if (stat.isDirectory() && item !== 'spec') { + await prerenderDir(itemPath); + + } else { + if (item === 'index.html' && dirPath.includes('test')) { + await prerenderPage(itemPath); + } + } + } +} + +async function run() { + const start = Date.now(); + try { + + let p = process.argv[2]; + if (p) { + const s = fs.statSync(p); + if (s.isDirectory()) { + await prerenderDir(p) + } else { + await prerenderPage(p); + } + + } else { + p = path.join(__dirname, '..', '..', 'src', 'components'); + await prerenderDir(p); + } + + } catch (e) { + console.error(e); + } + + const duration = Date.now() - start; + console.log(`time: ${duration}ms`); + if (prerenderCount > 1) { + console.log(`prerendered: ${prerenderCount}`); + console.log(`average: ${Math.round(duration / prerenderCount)}ms`); + } +} + +run(); diff --git a/core/stencil.config.ts b/core/stencil.config.ts index fa42e5a204..d3d3cbdab0 100644 --- a/core/stencil.config.ts +++ b/core/stencil.config.ts @@ -86,6 +86,9 @@ export const config: Config = { type: 'docs-json', file: '../docs/core.json' }, + { + type: 'dist-hydrate-script' + }, apiSpecGenerator({ file: 'api.txt' }), diff --git a/package.json b/package.json index cbe7bcc955..6820d788d7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,4 @@ { - "name": "@ionic/source", - "version": "0.0.1", - "description": "Ionic mono-repo root package.json, used mainly to execute build scripts. This package is not published to npm.", "private": true, "scripts": { "build": "node .scripts/build.js", @@ -19,5 +16,8 @@ "listr": "^0.14.0", "semver": "^5.5.0", "turbocolor": "^2.4.1" + }, + "engines": { + "node": ">= 10" } }