fix: 改用基于 Prerender 库的 SSR 方式

This commit is contained in:
Zhicheng WANG 2019-01-06 08:25:02 +08:00
commit 3dec4da2be
12 changed files with 164 additions and 97 deletions

View File

@ -9,6 +9,7 @@ addons:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
chrome: stable
branches:
except:
- g3
@ -21,5 +22,6 @@ cache:
before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.10.1
- export PATH="$HOME/.yarn/bin:$PATH"
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
script:
- "./aio/deploy-cn.sh"

View File

@ -9,7 +9,16 @@ commitMessage=$(git log --oneline -n 1)
cd `dirname $0`
yarn build
ts-node ./tools/translator/bin/ssr.ts
yarn start &
node ./tools/translator/ssr/ssr-server.js &
sleep 3;
ts-node ./tools/translator/ssr/ssr.ts
killall -9 node
if [[ ! -d "./ng-docs.github.io" ]]
then

View File

@ -68,7 +68,9 @@
"post~~build": "yarn build-404-page",
"~~build-ie-polyfills": "webpack-cli src/ie-polyfills.js -o src/generated/ie-polyfills.min.js --mode production",
"~~http-server": "http-server",
"~~minify-lunr": "uglifyjs node_modules/lunr/lunr.js -c -m -o src/generated/lunr.min.js --source-map"
"~~minify-lunr": "uglifyjs node_modules/lunr/lunr.js -c -m -o src/generated/lunr.min.js --source-map",
"ssr-server": "node ./tools/translator/ssr/ssr-server.js",
"ssr": "ts-node ./tools/translator/ssr/ssr.ts"
},
"engines": {
"node": ">=10.9.0 <11.0.0",
@ -93,6 +95,8 @@
"classlist.js": "^1.1.20150312",
"core-js": "^2.4.1",
"klaw-sync": "^6.0.0",
"node-fetch": "^2.3.0",
"prerender": "^5.4.5",
"rxjs": "^6.3.0",
"tslib": "^1.9.0",
"web-animations-js": "^2.2.5",

View File

@ -132,7 +132,7 @@ export const svgIconProviders = [
@NgModule({
imports: [
BrowserModule,
BrowserModule.withServerTransition({appId: 'ng-docs'}),
BrowserAnimationsModule,
CustomElementsModule,
HttpClientModule,

View File

@ -8,5 +8,7 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
document.addEventListener('DOMContentLoaded', () => {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
});

View File

@ -1,12 +0,0 @@
<aio-shell ng-version="6.0.0"
class="mode-stable sidenav-open page-guide-template-syntax folder-guide view-SideNav aio-notification-hide ">
<ul role="navigation"><!---->
<li class="ng-star-inserted"><a class="nav-link" href="/features" title="特性">特性</a></li>
<li class="ng-star-inserted"><a class="nav-link" href="/docs" title="文档">文档</a></li>
<li class="ng-star-inserted"><a class="nav-link" href="/resources" title="资源">资源</a></li>
<li class="ng-star-inserted"><a class="nav-link" href="/events" title="会议">会议</a></li>
<li class="ng-star-inserted"><a class="nav-link" href="https://blog.angular.io/" title="博客">博客</a></li>
<li class="ng-star-inserted"><a class="nav-link" href="/translations/cn/home" title="关于中文版">关于中文版</a></li>
<li class="ng-star-inserted"><a class="nav-link" href="https://ng-china.org" title="****2018 ngChina @ 杭州!****">****2018 ngChina @ 杭州!****</a></li>
</ul>
</aio-shell>

File diff suppressed because one or more lines are too long

View File

@ -1,68 +0,0 @@
import * as fs from 'fs';
import * as mkdirp from 'mkdirp';
import * as path from 'path';
import * as klawSync from 'klaw-sync';
import { minify } from 'html-minifier';
const rootElementPattern = /<aio-shell\b[\s\S]*<\/aio-shell>/;
const indexTemplate = fs.readFileSync('./dist/index.html', 'utf-8');
const aioShellTemplate = fs.readFileSync(__dirname + '/../assets/aio-shell-template.html');
const pageTemplate = indexTemplate.replace(rootElementPattern, `${aioShellTemplate}`);
const minifyOptions = {
collapseWhitespace: true,
ignoreCustomFragments: [/<code>[\s\S]*?<\/code>/],
minifyCSS: true,
minifyJS: true,
removeComments: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
};
function composePage(url) {
console.log(`pre-rendering ${url}...`);
const { title, contents } = JSON.parse(fs.readFileSync(`./dist/generated/docs/${url}.json`, 'utf-8'));
if (!contents) {
return;
}
const ssrContent = contents.replace(/href="(?!http)(.{2,}?)"/gi, 'href="/$1"');
const pageContent = pageTemplate
.replace('<title>Angular Docs</title>', `<title>${title} - Angular 官方文档</title>`)
.replace('<section class="sidenav-content" role="content" id="docs"></section>',
`<section class="sidenav-content" role="content" id="docs">${ssrContent}</section>`);
mkdirp.sync(path.dirname(`./dist/${url}`));
fs.writeFileSync(`./dist/${url}.html`, minify(pageContent, minifyOptions), 'utf-8');
}
function buildGuidePages(): void {
const navigation = fs.readFileSync('./content/navigation.json', 'utf-8');
(navigation.match(/"url": "(.*?)"/g) || [])
.map((entry) => entry.replace(/^"url": "(.*?)".*$/, '$1'))
.filter(url => url.slice(0, 4) !== 'http')
.forEach(url => composePage(url));
}
function buildApiPages(): void {
const apiList = klawSync('./dist/generated/docs/api', { nodir: true })
.map(file => file.path.replace(/^.*generated\/docs(\/.*).json$/, '$1'));
apiList.forEach(url => composePage(url));
const links = apiList.map(url => `<a href="${url}">${url}</a>`).join('\n');
const apiListContent = fs.readFileSync('./dist/api.html', 'utf-8')
.replace(rootElementPattern, `<aio-shell><h3>API List</h3>${links}</aio-shell>`);
fs.writeFileSync(`./dist/api.html`, minify(apiListContent, minifyOptions), 'utf-8');
}
function buildIndexPage(): void {
const indexTemplate = fs.readFileSync('./dist/index.html', 'utf-8');
const aioShellIndex = fs.readFileSync(__dirname + '/../assets/aio-shell-index.html');
const content = indexTemplate.replace(rootElementPattern, `${aioShellIndex}`);
fs.writeFileSync(`./dist/index.html`, minify(content, minifyOptions), 'utf-8');
}
buildGuidePages();
buildApiPages();
buildIndexPage();

View File

@ -0,0 +1,3 @@
const prerender = require('prerender');
const server = prerender();
server.start();

View File

@ -0,0 +1,55 @@
import * as fs from 'fs';
import * as klawSync from 'klaw-sync';
import { concat, defer, Observable } from 'rxjs';
import * as fetch from 'node-fetch';
import { fromPromise } from 'rxjs/internal-compatibility';
import { map } from 'rxjs/operators';
import * as path from 'path';
import { minify } from 'html-minifier';
import mkdirp = require('mkdirp');
const minifyOptions = {
collapseWhitespace: true,
ignoreCustomFragments: [/<code>[\s\S]*?<\/code>/],
minifyCSS: true,
minifyJS: true,
removeComments: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
};
function renderPage(url: string): Observable<void> {
return defer(() => fromPromise(fetch(`http://localhost:3000/render?url=http://localhost:4200/${url}`).then(resp => resp.text()))).pipe(
map(content => {
const filename = path.join('dist', `${url}.html`);
mkdirp.sync(path.dirname(filename));
fs.writeFileSync(filename, minify(content, minifyOptions), 'utf-8');
console.log(`rendered ${url}...`);
}),
);
}
function getGuideUrls(): string[] {
const navigation = fs.readFileSync('./content/navigation.json', 'utf-8');
return (navigation.match(/"url": "(.*?)"/g) || [])
.map((entry) => entry.replace(/^"url": "(.*?)".*$/, '$1'))
.filter(url => url.slice(0, 4) !== 'http');
}
function getApiUrls(): string[] {
return klawSync('./dist/generated/docs/api', { nodir: true })
.map(file => file.path.replace(/^.*generated\/docs\/(.*).json$/, '$1'));
}
const urls = [...getGuideUrls(), ...getApiUrls(), 'index.html'];
const tasks = urls.map(url => renderPage(url));
concat(...tasks).subscribe((url) => {
}, () => {
}, () => {
process.exit(0);
});
// 不自动退出
setInterval(function () {
}, 24 * 60 * 60 * 1000);

View File

@ -0,0 +1,28 @@
{
"compileOnSave": true,
"compilerOptions": {
"strict": true,
"noImplicitAny": false,
"outDir": "dist",
"baseUrl": "src",
"module": "commonjs",
"sourceMap": true,
"declaration": true,
"moduleResolution": "node",
"noUnusedLocals": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"types": [
"node"
],
"lib": [
"es2017",
"dom"
]
},
"exclude": [
"**/*.spec.ts"
]
}

View File

@ -1300,7 +1300,7 @@ body-parser@1.18.2, body-parser@^1.16.1:
raw-body "2.3.2"
type-is "~1.6.15"
body-parser@1.18.3, body-parser@^1.18.3:
body-parser@1.18.3, body-parser@^1.18.3, body-parser@~1.18.3:
version "1.18.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4"
dependencies:
@ -1867,6 +1867,14 @@ chrome-launcher@^0.10.5:
mkdirp "0.5.1"
rimraf "^2.6.1"
chrome-remote-interface@~0.25.7:
version "0.25.7"
resolved "https://registry.yarnpkg.com/chrome-remote-interface/-/chrome-remote-interface-0.25.7.tgz#827e85fbef3cc561a9ef2404eb7eee355968c5bc"
integrity sha512-6zI6LbR2IiGmduFZededaerEr9hHXabxT/L+fRrdq65a0CfyLMzpq0BKuZiqN0Upqcacsb6q2POj7fmobwBsEA==
dependencies:
commander "2.11.x"
ws "3.3.x"
chrome-trace-event@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48"
@ -2098,6 +2106,10 @@ comma-separated-tokens@^1.0.0, comma-separated-tokens@^1.0.1:
dependencies:
trim "0.0.1"
commander@2.11.x, commander@^2.8.1, commander@~2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
commander@2.15.1, commander@^2.12.1, commander@^2.9.0:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
@ -2106,10 +2118,6 @@ commander@2.17.x, commander@~2.17.1:
version "2.17.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
commander@^2.8.1, commander@~2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
commander@~2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
@ -2153,7 +2161,7 @@ compress-commons@^1.2.0:
normalize-path "^2.0.0"
readable-stream "^2.0.0"
compressible@^2.0.12:
compressible@^2.0.12, compressible@~2.0.14:
version "2.0.15"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.15.tgz#857a9ab0a7e5a07d8d837ed43fe2defff64fe212"
dependencies:
@ -2195,6 +2203,19 @@ compression@^1.7.0:
safe-buffer "5.1.1"
vary "~1.1.2"
compression@~1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db"
integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==
dependencies:
accepts "~1.3.5"
bytes "3.0.0"
compressible "~2.0.14"
debug "2.6.9"
on-headers "~1.0.1"
safe-buffer "5.1.2"
vary "~1.1.2"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@ -3529,7 +3550,7 @@ express@^4.16.2:
utils-merge "1.0.1"
vary "~1.1.2"
express@^4.16.3:
express@^4.16.3, express@~4.16.3:
version "4.16.4"
resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
dependencies:
@ -4648,7 +4669,7 @@ hawk@~6.0.2:
hoek "4.x.x"
sntp "2.x.x"
he@1.1.1, he@1.1.x:
he@1.1.1, he@1.1.x, he@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
@ -6937,6 +6958,11 @@ node-fetch-npm@^2.0.2:
json-parse-better-errors "^1.0.0"
safe-buffer "^5.1.1"
node-fetch@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5"
integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==
node-forge@0.7.1, node-forge@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300"
@ -7874,6 +7900,18 @@ prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
prerender@^5.4.5:
version "5.4.5"
resolved "https://registry.yarnpkg.com/prerender/-/prerender-5.4.5.tgz#1ba1a1136a27362840fb0c2f97d3acfec35ecf0f"
integrity sha512-oA/tZpCcWkpUj7KLtBJfb46OZN5cQLgqnSLtG6VsQgpPglv/deP0UCNR3ZKoNrrJaxcruGhfwhPL3c8gx1ncuw==
dependencies:
body-parser "~1.18.3"
chrome-remote-interface "~0.25.7"
compression "~1.7.3"
express "~4.16.3"
he "~1.1.1"
valid-url "~1.0.9"
preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
@ -10669,7 +10707,7 @@ v8flags@^3.0.0:
dependencies:
homedir-polyfill "^1.0.1"
valid-url@^1:
valid-url@^1, valid-url@~1.0.9:
version "1.0.9"
resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200"
@ -11163,6 +11201,15 @@ ws@3.3.2:
safe-buffer "~5.1.0"
ultron "~1.1.0"
ws@3.3.x:
version "3.3.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==
dependencies:
async-limiter "~1.0.0"
safe-buffer "~5.1.0"
ultron "~1.1.0"
ws@^1.0.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.4.tgz#57f40d036832e5f5055662a397c4de76ed66bf61"