Skip to content

Commit

Permalink
Merge pull request #40 from mhmo91/#37--performance-optimization
Browse files Browse the repository at this point in the history
setting up angular universal for ssr
  • Loading branch information
Muhammad Hasan committed Sep 24, 2019
2 parents 2258f48 + 05540ab commit a66c666
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 8 deletions.
23 changes: 22 additions & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/dahabguide",
"outputPath": "dist/browser",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
Expand Down Expand Up @@ -125,6 +125,27 @@
"devServerTarget": "dahabguide:serve:production"
}
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/server",
"main": "src/main.server.ts",
"tsConfig": "tsconfig.server.json"
},
"configurations": {
"production": {
"fileReplacements": [{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}],
"sourceMap": false,
"optimization": {
"scripts": false,
"styles": true
}
}
}
}
}
}
Expand Down
44 changes: 41 additions & 3 deletions firebase.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,52 @@
{
"hosting": {
"public": "dist/dahabguide",
"public": "dist/browser",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [{
"source": "**",
"destination": "/index.html"
}]
"function": "ssr"
}],
"headers": [{
"source": "**/*.@(jpg|jpeg|gif|png|svg)",
"headers": [{
"key": "Cache-Control",
"value": "max-age=86400"
},
{
"key": "Content-Encoding",
"value": "gzip"
}
]
},
{
"source": "**/*.@(js|css)",
"headers": [{
"key": "Cache-Control",
"value": "max-age=31536000"
},
{
"key": "Content-Encoding",
"value": "gzip"
}
]
},
{
"source": "**/*-sw.js",
"headers": [{
"key": "Cache-Control",
"value": "no-cache"
}]
}
]
},
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
}
18 changes: 16 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"analyze": "webpack-bundle-analyzer dist/dahabguide/stats-es2015.json"
"analyze": "webpack-bundle-analyzer dist/dahabguide/stats-es2015.json",
"compile:server": "webpack --config webpack.server.config.js --progress --colors",
"serve:ssr": "node dist/server",
"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
"build:client-and-server-bundles": "ng build --prod && ng run dahabguide:server:production --bundleDependencies none"
},
"private": true,
"dependencies": {
Expand All @@ -23,10 +27,14 @@
"@angular/material": "^8.2.0",
"@angular/platform-browser": "~8.2.4",
"@angular/platform-browser-dynamic": "~8.2.4",
"@angular/platform-server": "~8.2.4",
"@angular/pwa": "^0.803.5",
"@angular/router": "~8.2.4",
"@angular/service-worker": "~8.2.4",
"@nguniversal/express-engine": "^8.1.1",
"@nguniversal/module-map-ngfactory-loader": "8.1.1",
"bootstrap": "^4.3.1",
"express": "^4.15.2",
"firebase": "^6.6.0",
"hammerjs": "^2.0.8",
"rxjs": "~6.4.0",
Expand All @@ -43,6 +51,7 @@
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"bufferutil": "^4.0.1",
"codelyzer": "^5.1.1",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
Expand All @@ -52,9 +61,14 @@
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-loader": "^5.2.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.5.3",
"webpack-bundle-analyzer": "^3.5.1"
"utf-8-validate": "^5.0.2",
"webpack-bundle-analyzer": "^3.5.1",
"webpack-cli": "^3.1.0",
"ws": "^7.1.2",
"xhr2": "^0.2.0"
}
}
61 changes: 61 additions & 0 deletions server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* *** NOTE ON IMPORTING FROM ANGULAR AND NGUNIVERSAL IN THIS FILE ***
*
* If your application uses third-party dependencies, you'll need to
* either use Webpack or the Angular CLI's `bundleDependencies` feature
* in order to adequately package them for use on the server without a
* node_modules directory.
*
* However, due to the nature of the CLI's `bundleDependencies`, importing
* Angular in this file will create a different instance of Angular than
* the version in the compiled application code. This leads to unavoidable
* conflicts. Therefore, please do not explicitly import from @angular or
* @nguniversal in this file. You can export any needed resources
* from your application's main.server.ts file, as seen below with the
* import for `ngExpressEngine`.
*/

(global as any).WebSocket = require('ws');
(global as any).XMLHttpRequest = require('xhr2');

import 'zone.js/dist/zone-node';

import * as express from 'express';
import { join } from 'path';

// Express server
const app = express();

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist/browser');

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap } = require('./dist/server/main');

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));

app.set('view engine', 'html');
app.set('views', DIST_FOLDER);

// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express.static(DIST_FOLDER, {
maxAge: '1y'
}));

// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('index', { req });
});

// Start up the Node server
app.listen(PORT, () => {
console.log(`Node Express server listening on http://localhost:${PORT}`);
});
2 changes: 1 addition & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ServiceWorkerModule } from '@angular/service-worker';
AppComponent
],
imports: [
BrowserModule,
BrowserModule.withServerTransition({ appId: 'serverApp' }),
AppRoutingModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFirestoreModule,
Expand Down
16 changes: 16 additions & 0 deletions src/app/app.server.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';

@NgModule({
imports: [
AppModule,
ServerModule,
ModuleMapLoaderModule,
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
12 changes: 12 additions & 0 deletions src/main.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { enableProdMode } from '@angular/core';

import { environment } from './environments/environment';

if (environment.production) {
enableProdMode();
}

export { AppServerModule } from './app/app.server.module';
export { ngExpressEngine } from '@nguniversal/express-engine';
export { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';

4 changes: 3 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ if (environment.production) {
enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
document.addEventListener('DOMContentLoaded', () => {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
});
13 changes: 13 additions & 0 deletions tsconfig.server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "./out-tsc/app-server",
"module": "commonjs"
},
"files": [
"src/main.server.ts"
],
"angularCompilerOptions": {
"entryModule": "./src/app/app.server.module#AppServerModule"
}
}
56 changes: 56 additions & 0 deletions webpack.server.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Work around for https://github.com/angular/angular-cli/issues/7200

const path = require('path');
const webpack = require('webpack');

module.exports = {
mode: 'none',
entry: {
// This is our Express server for Dynamic universal
server: './server.ts'
},
externals: {
'./dist/server/main': 'require("./server/main")'
},
target: 'node',
resolve: {
extensions: ['.ts', '.js']
},
optimization: {
minimize: false
},
output: {
// Puts the output at the root of the dist folder
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
module: {
noParse: /polyfills-.*\.js/,
rules: [{
test: /\.ts$/,
loader: 'ts-loader'
},
{
// Mark files inside `@angular/core` as using SystemJS style dynamic imports.
// Removing this will cause deprecation warnings to appear.
test: /(\\|\/)@angular(\\|\/)core(\\|\/).+\.js$/,
parser: {
system: true
},
},
]
},
plugins: [
new webpack.ContextReplacementPlugin(
// fixes WARNING Critical dependency: the request of a dependency is an expression
/(.+)?angular(\\|\/)core(.+)?/,
path.join(__dirname, 'src'), // location of your src
{} // a map of your routes
),
new webpack.ContextReplacementPlugin(
// fixes WARNING Critical dependency: the request of a dependency is an expression
/(.+)?express(\\|\/)(.+)?/,
path.join(__dirname, 'src'), {}
)
]
};

0 comments on commit a66c666

Please sign in to comment.