Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

Commit 2066f18

Browse files
committed
feat(@nguniversal/builders): add static page generator builder using Angular SSR Clover
With this change we create a builder that can be used to generate static pages (prerendering) using the new Universal approach (Clover).
1 parent 03c51f4 commit 2066f18

File tree

19 files changed

+469
-50
lines changed

19 files changed

+469
-50
lines changed

integration/clover/angular.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@
5252
]
5353
}
5454
}
55+
},
56+
"build-static": {
57+
"builder": "@nguniversal/builders:static-generator",
58+
"options": {
59+
"browserTarget": "clover:build:production",
60+
"routes": [
61+
"/",
62+
"pokemon/pikachu",
63+
"pokemon/charmander",
64+
"pokemon/squirtle",
65+
"pokemon/bulbasaur"
66+
]
67+
}
5568
}
5669
}
5770
}

integration/clover/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
"scripts": {
1010
"ng": "ng",
1111
"test": "yarn build && node test.js",
12-
"build": "ng build --configuration production"
12+
"build": "ng run clover:build-static"
1313
},
1414
"private": true,
15+
"resolutions": {
16+
"@nguniversal/common": "file:../../dist/modules-dist/common"
17+
},
1518
"dependencies": {
1619
"@angular/animations": "file:../../node_modules/@angular/animations",
1720
"@angular/common": "file:../../node_modules/@angular/common",
@@ -23,13 +26,15 @@
2326
"@angular/platform-server": "file:../../node_modules/@angular/platform-server",
2427
"@angular/router": "file:../../node_modules/@angular/router",
2528
"@nguniversal/common": "file:../../dist/modules-dist/common",
29+
"angular-in-memory-web-api": "^0.11.0",
2630
"rxjs": "file:../../node_modules/rxjs",
2731
"zone.js": "file:../../node_modules/zone.js"
2832
},
2933
"devDependencies": {
3034
"@angular-devkit/build-angular": "file:../../node_modules/@angular-devkit/build-angular",
3135
"@angular/cli": "file:../../node_modules/@angular/cli",
3236
"@angular/compiler-cli": "file:../../node_modules/@angular/compiler-cli",
37+
"@nguniversal/builders": "file:../../dist/modules-dist/builders",
3338
"@types/node": "file:../../node_modules/@types/node",
3439
"typescript": "file:../../node_modules/typescript"
3540
}

integration/clover/src/app/app-routing.module.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { NgModule } from '@angular/core';
22
import { Routes, RouterModule } from '@angular/router';
3+
import { PokedexComponent } from './pokedex.component';
4+
import { HomepageComponent } from './homepage.component';
35

4-
const routes: Routes = [];
6+
const routes: Routes = [
7+
{ path: '', component: HomepageComponent },
8+
{ path: '**', component: PokedexComponent },
9+
];
510

611
@NgModule({
712
imports: [RouterModule.forRoot(routes)],
Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,8 @@
1-
import { Component, Inject } from '@angular/core';
2-
import { DOCUMENT } from '@angular/common';
1+
import { Component } from '@angular/core';
32

43
@Component({
54
selector: 'app-root',
6-
template: `
7-
<div>Hello {{ title }}!</div>
8-
<span class="href-check">{{ href }}</span>
9-
`,
10-
styles: [
11-
`
12-
div {
13-
font-weight: bold;
14-
}
15-
`,
16-
],
5+
template: '<router-outlet></router-outlet>',
6+
styleUrls: [],
177
})
18-
export class AppComponent {
19-
title = 'world';
20-
href: string;
21-
22-
constructor(@Inject(DOCUMENT) doc: Document) {
23-
this.href = doc.location.href;
24-
}
25-
}
8+
export class AppComponent {}

integration/clover/src/app/app.module.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
1+
import { HttpClientModule } from '@angular/common/http';
12
import { NgModule } from '@angular/core';
23
import { BrowserModule } from '@angular/platform-browser';
34
import { RendererModule, TransferHttpCacheModule } from '@nguniversal/common/clover';
5+
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
6+
import { PokemonService } from './pokemon.service';
47

58
import { AppRoutingModule } from './app-routing.module';
69
import { AppComponent } from './app.component';
10+
import { HomepageComponent } from './homepage.component';
11+
import { PokedexComponent } from './pokedex.component';
712

813
@NgModule({
9-
declarations: [AppComponent],
14+
declarations: [AppComponent, PokedexComponent, HomepageComponent],
1015
imports: [
11-
BrowserModule.withServerTransition({ appId: 'hlw' }),
16+
BrowserModule.withServerTransition({ appId: 'serverApp' }),
17+
AppRoutingModule,
18+
HttpClientModule,
19+
20+
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
21+
// and returns simulated server responses.
22+
23+
HttpClientInMemoryWebApiModule.forRoot(PokemonService, { dataEncapsulation: false }),
24+
1225
RendererModule.forRoot(),
1326
TransferHttpCacheModule,
14-
AppRoutingModule,
1527
],
1628
providers: [],
1729
bootstrap: [AppComponent],
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { NgModule } from '@angular/core';
2+
import { ServerModule } from '@angular/platform-server';
3+
4+
import { AppModule } from './app.module';
5+
import { AppComponent } from './app.component';
6+
7+
@NgModule({
8+
imports: [AppModule, ServerModule],
9+
bootstrap: [AppComponent],
10+
})
11+
export class AppServerModule {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-homepage',
5+
template: ` <p>Welcome to {{ title }}!</p> `,
6+
styles: [],
7+
})
8+
export class HomepageComponent {
9+
title = 'Pokemon';
10+
constructor() {}
11+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Component } from '@angular/core';
2+
import { HttpClient } from '@angular/common/http';
3+
import { Observable } from 'rxjs';
4+
import { Router } from '@angular/router';
5+
6+
@Component({
7+
selector: 'app-pokedex',
8+
template: `
9+
<div *ngIf="pokemonObservable | async as pokemon">
10+
<h1>{{ pokemon.name }}</h1>
11+
<img src="{{ pokemon.img }}" />
12+
</div>
13+
`,
14+
styleUrls: [],
15+
})
16+
export class PokedexComponent {
17+
pokemonObservable: Observable<any>;
18+
constructor(private http: HttpClient, private router: Router) {
19+
// This request will use the In-memory Db in PokemonService.
20+
this.pokemonObservable = http.get<any>('api' + router.url);
21+
}
22+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Injectable } from '@angular/core';
2+
import { InMemoryDbService } from 'angular-in-memory-web-api';
3+
4+
@Injectable({
5+
providedIn: 'root',
6+
})
7+
export class PokemonService implements InMemoryDbService {
8+
createDb() {
9+
const pokemon = [
10+
{
11+
id: 'pikachu',
12+
name: 'pikachu',
13+
img: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png',
14+
},
15+
{
16+
id: 'bulbasaur',
17+
name: 'bulbasaur',
18+
img: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png',
19+
},
20+
{
21+
id: 'charmander',
22+
name: 'charmander',
23+
img: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/4.png',
24+
},
25+
{
26+
id: 'squirtle',
27+
name: 'squirtle',
28+
img: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/7.png',
29+
},
30+
];
31+
32+
return { pokemon };
33+
}
34+
}

integration/clover/test.js

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,24 @@
1-
const { Engine } = require('@nguniversal/common/clover/server');
21
const { join } = require('path');
3-
const { format } = require('url');
2+
const glob = require('glob');
3+
const { readFileSync } = require('fs');
44

55
const DIST = join(__dirname, 'dist/clover/browser');
6+
const pages = glob.sync('**/index.html', {
7+
cwd: DIST,
8+
});
69

7-
const ssr = new Engine();
8-
ssr
9-
.render({
10-
publicPath: DIST,
11-
url: format({
12-
protocol: 'http',
13-
host: 'localhost:8000',
14-
pathname: '',
15-
}),
16-
})
17-
.then((html) => {
18-
if (html.includes('Hello world')) {
19-
console.log(`Response contained "Hello world"`);
20-
} else {
21-
console.log(html);
22-
throw new Error(`Response didn't include "Hello world"`);
23-
}
24-
})
25-
.catch((err) => {
26-
console.error(err);
27-
process.exit(1);
28-
});
10+
console.log({
11+
pages,
12+
});
13+
14+
const expectedNumberOfPages = 5;
15+
if (pages.length !== expectedNumberOfPages) {
16+
throw new Error(`Expected to have ${expectedNumberOfPages} index pages, but got ${pages.length}`);
17+
}
18+
19+
for (const page of pages) {
20+
const content = readFileSync(join(DIST, page), 'utf8');
21+
if (!content.includes('ng-clover')) {
22+
throw new Error(`Page ${page} didn't contain ng-clover marker.`);
23+
}
24+
}

0 commit comments

Comments
 (0)