for my project Angular 9 I must install Server-side rendering (SSR), I followed official tutorial https://angular.io/guide/universal. At the beginning I have the problem with the window is not define. So I decided to install SSR with domino and I followed this tutorial enter link description here , but I have a problem when the program build my project : elements is not define. (const elements = stripe.elements() Cannot read property elements of undefined).
Below my server.ts code
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '#nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { APP_BASE_HREF } from '#angular/common';
import { existsSync } from 'fs';
import * as core from 'express-serve-static-core';
const domino = require('domino');
const fs = require('fs');
const path = require('path');
// Use the browser index.html as template for the mock window
const template = fs
.readFileSync(path.join(join(process.cwd(), 'dist/captn-boat-angular/browser'), 'index.html'))
.toString();
// Shim for the global window and document objects.
const window = domino.createWindow(template);
global['window'] = window;
global['document'] = window.document;
global ['navigator']=window.navigator;
global ['screen']=window.screen;
global['Event'] = null;
global['window'] = window;
global['document'] = window.document;
global['branch'] = null;
global['object'] = window.object;
global['localStorage'] = window.localStorage;
global['navigator'] = window.navigator ;
global['elements']=window.elements;
global['elements']=null;
global['Event'] = null;
global['KeyboardEvent'] = null;
global['stripe']=window.stripe;
window.screen = { deviceXDPI: 0, logicalXDPI: 0 };
global['MouseEvent'] = window.MouseEvent;
declare interface Window {
Stripe: any; // Or you can define a type for that in this file as well
stripe:null;
elements:null;
}
import { AppServerModule } from './src/main.server';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): core.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/captn-boat-angular/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run() {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
And then the error : elements of undefined
Thank you for your answer.
Do you use stripe in every place on your application ? I'd use it in the payment section of the related module and would connect it to lazy loading. Thus you could use one of the importer of stripe ?
like here :ngx-stripe installation
Related
I am testing a nodejs express API with a PG db wrapped with Prisma ORM
I configured a testing singleton instance as described by Prisma docs
Since the API is implemented in CommonJS and not TS, I had to make some changes as described in this beautiful page.
Here is a synthesis of what I did, will try to make it short, so it's easier to read
orgs.js (A GET route served by the mock server later on ...)
const Router = require('express-promise-router')
const router = new Router()
const PrismaPool = require('../db/PrismaPool');
module.exports = router
router.get('/assessments', async (req, res) => {
try{
const prisma = PrismaPool.getInstance();
const data = await prisma.org.findUnique({
select:{
assessments:true,
},
where: {
id: res.locals.orgId,
},
})
res.send(data)
}
catch(err){
handleError(err, "[GET]/orgs/assessments", 400, req, res)
}
})
PrismaPool.js (A wrapper to access the unique prisma client instance)
const prisma = require('./PrismaClientInstance').default
class PrismaPool {
constructor() {
throw new Error('Use PrismaPool.getInstance()');
}
static getInstance() {
return prisma
}
}
module.exports = PrismaPool;
PrismaClientInstance.js The unique instance of PrismaClient class. This is the tricky part stitching between the CommonJS world and the TS world.
'use strict';
exports.__esModule = true;
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
exports['default'] = prisma;
All this configuration works GREAT at runtime, now, when wrapping it with JEST in unit tests, things go south quickly ...
mock_server.js (a simplified server to expose the orgs API above)
const http = require('http');
const express = require('express');
var orgsRouter = require('../orgs');
const app = express();
app.use('/orgs', orgsRouter);
const port = 3011
app.set('port', port);
const server = http.createServer(app);
function onError(error) {
// herror handling
}
function onListening() {
// some debug messages
}
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
module.exports = server
PrismaSingletonForTesting.ts (A jest deep mock of the PrismaClient instance)
import { PrismaClient } from '#prisma/client'
import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended'
import prisma from './PrismaClientInstance'
jest.mock('./PrismaClientInstance', () => ({
__esModule: true,
default: mockDeep<PrismaClient>()
}))
beforeEach(() => {
mockReset(prismaMock)
})
export const prismaMock = prisma as unknown as DeepMockProxy<PrismaClient>
orgs.test.js (The tests of the orgs API)
const Request = require("request")
const { prismaMock } = require('../../db/PrismaSingletonForTesting')
const TEST_ORGID = 1
describe('egrest server', () => {
let server
beforeAll(() => {
server = require('./test_server')
})
afterAll(() => {
server.close()
})
describe('assessments', () => {
let data = {}
beforeAll(() => {
const testorg = {
id: TEST_ORGID,
name: 'jestest',
admin:33,
avail_tests: 1234
}
prismaMock.org.findUnique.mockResolvedValue(testorg)
})
it(`read remaining assessments for org ${TEST_ORGID}`, (done) => {
Request.get("http://localhost:3011/orgs/assessments", (error, response, body) => {
data.status = response.statusCode
data.body = body
data.error = error
console.dir(body)
done()
})
})
})
})
I also configured .jest.config with the required line setupFilesAfterEnv: ['./db/PrismaSingletonForTesting.ts']
When I run this test, I get data=undefined in orgs.js, even-though I mocked prisma.org.findUnique by doing prismaMock.org.findUnique.mockResolvedValue(testorg) as described by prisma docs.
Any help would be appreciated.
currently, it's working using hash routing, I want to remove hash for SEO reasons so it is possible?
it creates routes like
https://mainDomain/program ← program build run
https://mainDomain/program/#detail ← program routing
I want https://mainDomain/program/#detail to https://mainDomain/program/detail
if I use BrowserRouter it shows no such directory
this is my build deploy structure in AWS ↓
might be this Helpful for you. you can create one Node server which can serve your Project builds.
let path = require("path");
let fsp = require("fs/promises");
let express = require("express");
let isProduction = process.env.NODE_ENV === "production";
async function createServer() {
let app = express();
/**
* #type {import("vite").ViteDevServer}
*/
let vite;
if (!isProduction) {
vite = await require("vite").createServer({
root: process.cwd(),
server: { middlewareMode: "ssr" },
});
app.use(vite.middlewares);
} else {
app.use(require("compression")());
app.use(express.static(path.join(__dirname, "dist")));
}
app.use("*", async (req, res) => {
let url = req.originalUrl;
// Use a separate HTML file for the "Inbox" app.
let appDirectory = url.startsWith("/inbox") ? "inbox" : "";
let htmlFileToLoad;
if (isProduction) {
htmlFileToLoad = path.join("dist", appDirectory, "index.html");
} else {
htmlFileToLoad = path.join(appDirectory, "index.html");
}
try {
let html = await fsp.readFile(
path.join(__dirname, htmlFileToLoad),
"utf8"
);
if (!isProduction) {
html = await vite.transformIndexHtml(req.url, html);
}
res.setHeader("Content-Type", "text/html");
return res.status(200).end(html);
} catch (error) {
if (!isProduction) vite.ssrFixStacktrace(error);
console.log(error.stack);
return res.status(500).end(error.stack);
}
});
return app;
}
createServer().then((app) => {
app.listen(3000, () => {
console.log("HTTP server is running at http://localhost:3000");
});
});
for extra information, you can referlink.
I am getting firebase (messaging/unsupported-browser) error when I am trying to run my project with server side rendering. Without server side rendering firebase messaging working fine but when I am trying to run using server side rendering I am getting browser unsupported error. Server side rendering also working fine when I am not using firebase messaging library in my project. I am trying to resolve to this issue for last three days but cannot find any proper solution.
Note: Issue only with SSR otherwise messaging working fine
Error
firebase-messaging-sw.js
server.ts
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '#nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '#angular/common';
import { existsSync, readFileSync } from 'fs';
import 'localstorage-polyfill'
import { createWindow } from 'domino';
const scripts = readFileSync('dist/index.html').toString();
const window = createWindow(scripts) as any;
(global as any).window = window;
(global as any).self = window;
(global as any).document = window.document;
(global as any).Event = window.Event;
(global as any).KeyboardEvent = window.KeyboardEvent;
(global as any).MouseEvent = window.MouseEvent;
(global as any).navigator = window.navigator;
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env['PORT'] || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
I want to use koa-views with Koa and Koa-Router with Next.js. In previous projects, I had no issues with express but in this project, I have to use Koa. Using its router, I want to render a page: /some/page/:id. Following the same Nextjs way:
router.get('/some/page/:id', async (ctx, next) => {
const actualPage = '/some/page/id' // id.js (not actual name 😝)
await ctx.render(actualPage, {/* could pass object */})
});
That would work if I was using express. With Koa:
const Koa = require('koa');
const views = require('koa-views');
// const render = require('koa-views-render'); <-- I what's this?
[..] // Making things short here
const server = new Koa();
const router = new Router();
// My issue, I'm seeing tutorials using other engines: .ejs etc
// I'm not using any, I only have .js files
server.use(views(__dirname + "/pages", { extension: 'js' }));
Using the same router.get... function as above, I get:
Error: Engine not found for the ".js" file extension
When I go to /some/page/123, I'd expect it to render the file /pages/some/page/id.js. How?
It turns out I do not need any extra modules to achieve this 🙀
Create a function called, ie, routes then pass app and router as a param
const routes = (router, app) => {
router.get('/some/page/:id', async (ctx) => {
const { id } = ctx.params
const actualPage = '/some/page/id'
// Render the page
await app.render(ctx.req, ctx.res, actualPage, {foo: 'Bar'})
}
}
module.exports = routes
Inside your server.js file:
// const routes = require('./routes);
// const app = next({ dev }); // import other modules for this section
// app.prepare().then(() => {
// const router = new Router();
// [..]
// routes(router, app)
// })
The commented out section is a slim down version to make a point in where things should be.
This is a weird bug so I must be missing something obvious, but here it is.
I'm trying to set up a Koa server to serve several Koa apps depending on the vhost name. The entry point is server.js :
const _ = require("lodash");
const compose = require("koa-compose");
const Koa = require("koa");
const server = module.exports = new Koa();
const app1 = require("./apps/app1");
const app2 = require("./apps/app2");
console.log(app1 instanceof Koa); // false (!)
console.log(app1); // `{ subdomainOffset: 2, proxy: false, env: 'development' }`
const vhostApps = [
{ vhost: "localhost", app: composer(app1) }, // composer fails because app1 is not a Koa instance
{ vhost: "app1.mydomain.com", app: composer(app1) },
{ vhost: "app2.mydomain.com", app: composer(app2) }
];
server.use(async function(ctx, next) {
const app = _(vhostApps).find({ vhost: ctx.hostname }).app;
return await app ? app.apply(this, [ctx, next]) : next();
});
if (!module.parent) server.listen(process.env.PORT || 80);
function composer(app) {
const middleware = app instanceof Koa ? app.middleware : app;
return compose(middleware);
}
Then, there's ./apps/app1/index.js, the entry point of one app:
const Koa = require("koa");
const serve = require("koa-static");
const views = require("koa-views");
const router = require("./routes");
const app = new Koa();
app.use(serve(__dirname + "/assets"));
app.use(views(__dirname + "/views", { map: { html: "lodash" } }));
app.use(router.routes());
console.log(app instanceof Koa); // true (OK)
console.log(app); // `{ subdomainOffset: 2, proxy: false, env: 'development' }`
module.exports = app;
In this module, app is an instance of Koa (thus it has a middleware property, of type array).
But seen from server.js, the value imported from app1 is not the expected Koa instance, even though the logged values of app and app1 are the same ({ subdomainOffset: 2, proxy: false, env: 'development' }
).
What am I doing wrong?
Finally I think found what the problem was, and, as it happens, the essential piece of info was missing in my question.
The ./apps/app1 folder has its own node_modules, with its own copy of Koa. Therefore, server.js and apps/app1/index.js each have their own, different, Koa.
So I suppose that in the line: const middleware = app instanceof Koa ? app.middleware : app;, app instanceof Koa will always return false for that reason.
One solution is simply to remove Koa from the app's node_modules, so Koa is inherited from the outer folder. (At first sight, it has some drawbacks for me because I would like to the apps to be standalone).
But I think I'll just skip the instanceof Koa test and have const middleware = app.middleware; instead (I borrowed the original line from https://github.com/koajs/examples/blob/master/vhost/app.js#L14).