Tech: Angular Cli, Angular version 7, Angular Google Maps, Firebase Functions.
Issue: I'm trying to serve my angular universal app but getting an error for angular google maps when building.
Error I get:
/Users/test/Public/Leisure/leisure-app/functions/node_modules/@agm/core/services/managers/info-window-manager.js:1
(function (exports, require, module, __filename, __dirname) { import {
Observable } from 'rxjs';
^^^^^^
SyntaxError: Unexpected token import
at createScript (vm.js:80:10)
at Object.runInThisContext (vm.js:139:10)
Seems that Angular Universal doesnt like third party libraries.
The resource I followed: https://hackernoon.com/deploying-angular-universal-v6-with-firebase-c86381ddd445
My App Module:
import { AgmCoreModule } from '@agm/core';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFireDatabase } from '@angular/fire/database';
import { AngularFireModule } from '@angular/fire';
import { ServiceWorkerModule } from '@angular/service-worker';
import { AngularFireStorageModule } from '@angular/fire/storage';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { BrowserModule } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { GooglePlaceModule } from 'ngx-google-places-autocomplete';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NgModule, NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { NgxErrorsModule } from '@hackages/ngxerrors';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { TruncateModule } from 'ng2-truncate';
import { MetaModule } from '@ngx-meta/core';
import { PreventDoubleSubmitModule } from 'ngx-prevent-double-submission';
// Core App
import { AppComponent } from './app.component';
import { CoreInterceptor } from './interceptor';
import { environment } from '../environments/environment';
// Modules
import { SharedModule } from './shared/shared.module';
import { CoreModule } from './services/core.module';
import { LayoutModule } from './layouts/layout.module';
// Sections
import { COMPONENTS } from './components/index';
import { ROUTES } from './app.routes';
// Guards
import { AuthGuard } from './guards/auth.guard';
import { CreditGuard } from './guards/credit.guard';
@NgModule({
declarations: [
AppComponent,
COMPONENTS
],
imports: [
CommonModule,
FormsModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFireAuthModule,
AngularFirestoreModule,
AngularFireStorageModule,
// This is the Angular google maps module causing issue on build
AgmCoreModule.forRoot({
apiKey: environment.googleApiKey,
libraries: ['places']
}),
GooglePlaceModule,
LayoutModule,
BrowserModule.withServerTransition({ appId: 'test123' }),
PreventDoubleSubmitModule.forRoot(),
TruncateModule,
MetaModule.forRoot(),
HttpClientModule,
NgxErrorsModule,
ReactiveFormsModule,
RouterModule.forRoot(ROUTES),
CoreModule,
SharedModule,
ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: CoreInterceptor, multi: true },
AuthGuard,
CreditGuard,
AngularFireDatabase
],
exports: [ RouterModule ],
schemas: [ NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Command I run: serve: npm run build && firebase serve --only functions
Latest Error (for npm run serve:ssr):
Error code from serve:ssr:
e.Lb=function(a,b,c){a=this.ka.J[String(a)];if(!a)return!0;a=a.concat();for(var d=!0,f=0;f<a.length;++f){var g=a[f];if(g&&!g.Sa&&g.capture==b){var k=g.listener,p=g.Ob||g.src;g.Eb&&this.Le(g);d=!1!==k.call(p,c)&&d;}}return d&&0!=c.Be};e.jb=function(a,b,c,d){return this.ka.jb(String(a),b,c,d)};var ub=h.JSON.stringify;function vb(a,b){this.Sf=100;this.ef=a;this.ug=b;this.Zb=0;this.Pb=null;}vb.prototype.get=function(){if(0<this.Zb){this.Zb--;var a=this.Pb;this.Pb=a.next;a.next=null;}else a=this.ef();return a};vb.prototype.put=function(a){this.ug(a);this.Zb<this.Sf&&(this.Zb++, a.next=this.Pb, this.Pb=a);};function I(){this.lc=this.Va=null;}var xb=new vb(function(){return new wb},function(a){a.reset();});I.prototype.add=function(a,b){var c=this.Af();c.set(a,b);this.lc?this.lc.next=c:this.Va=c;this.lc=c;};I.prototype.remove=function(){var a=null;this.Va&&(a=this.Va, this.Va=this.Va.next, this.Va||(this.lc=null), a.next=null);return a};I.prototype.wg=function(a){xb.put(a);};I.prototype.Af=function(){return xb.get()};function wb(){this.next=this.scope=this.Gc=null;}
wb.prototype.set=function(a,b){this.Gc=a;this.scope=b;this.next=null;};wb.prototype.reset=function(){this.next=this.scope=this.Gc=null;};function yb(a){h.setTimeout(function(){throw a;},0);}var zb;
The new Angular Component pearl-lullaby (v9. 0.0-rc. 0) introduces the second official @angular/component component, a Google Maps component.
Key Benefits for SEO A primary benefit for using Angular Universal is that it improves web crawler support for enhanced Search Engine Optimization (SEO). With traditional client-side rendered SPAs, anything that is not in that shell of an . html is all rendered by the JavaScript.
Angular Universal executes on the server, generating static application pages that later get bootstrapped on the client. This means that the application generally renders more quickly, giving users a chance to view the application layout before it becomes fully interactive.
TL;DR:
Source code and DEMO
The issue here is that @agm/core
package is compiled with es2015 module. As a result, it contains import
and export
in js code.
To remedy this you have two main options:
You can use either babel or typescript to compile that package. Then you need to make sure you provided compiled version in your functions dependencies
functions/package.json
"dependencies": {
...
"@agm/core": "file:./@agm/core"
},
Here I use local dependency but you can also use your own published version.
Another method would be compile directly in node_modules and publish the whole node_modules(I would avoid this):
firebase.json
{
"functions": {
"ignore": []
}
}
Babel
Install dependencies in root directory.
npm i -D @babel/cli @babel/core @babel/preset-env
Use the following script to compile:
package.json
"postbuild": "babel node_modules/@agm/core -d functions/@agm/core --presets @babel/preset-env && node ./copy-package-json.js"
where
copy-package-json.js
const fs = require('fs-extra');
const { join } = require('path');
(async() => {
await fs.copy(join(process.cwd(), 'node_modules/@agm/core/package.json'),
join(process.cwd(), 'functions/@agm/core/package.json'));
})();
Typescript
tsconfig.agm.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./functions/@agm/core",
"types": [],
"module": "commonjs"
},
"include": [
"node_modules/@agm/core"
]
}
package.json
"postbuild": "tsc -p tsconfig.agm.json --allowJs && node ./copy-package-json.js"
This is what Angular universal tutorial uses so I prefer this solution.
Also follow this quide
Simple steps:
1. Install global dependencies
I have installed:
2. Create a new Angular project
ng new angular-agm
3. Add Angular universal
ng add @nguniversal/express-engine --clientProject angular-agm
4. Update server.ts
Export the express app, then remove the call to listen and change index to index2.
import 'zone.js/dist/zone-node';
import {enableProdMode} from '@angular/core';
// Express Engine
import {ngExpressEngine} from '@nguniversal/express-engine';
// Import module map for lazy loading
import {provideModuleMap} from '@nguniversal/module-map-ngfactory-loader';
import * as express from 'express';
import {join} from 'path';
import * as path from 'path';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Express server
export const app = express();
// const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist/browser');
const index = require('fs')
.readFileSync(path.resolve(DIST_FOLDER, 'index2.html'), 'utf8')
.toString();
const domino = require('domino');
const win = domino.createWindow(index);
global['window'] = win;
global['document'] = win.document;
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = 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('index2', { req });
});
// Start up the Node server
/*app.listen(PORT, () => {
console.log(`Node Express server listening on http://localhost:${PORT}`);
});*/
5. Build
npm run build:ssr
where build:ssr
"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server && node ./tools/copy-artifacts.js",
copy-artifacts.js
const fs = require('fs-extra');
const { join } = require('path');
(async() => {
const src = join(process.cwd(), 'dist');
const copy = join(process.cwd(), 'functions/dist');
await fs.rename(join(src, 'browser/index.html'), join(src, 'browser/index2.html'));
await fs.remove(copy);
await fs.copy(src, copy);
})();
6. Update functions/index.js to use built version of express app
const functions = require('firebase-functions');
const { app } = require('./dist/server');
exports.ssr = functions.https.onRequest(app);
7. Configure firebase.json
{
"hosting": {
"public": "dist/browser",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "ssr"
}
]
}
}
The source code can be found on Github
See also demo https://angular-agm.firebaseapp.com/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With