1 | declare var __ASSETS_PATH__: any;
|
2 | declare var __RESOLVED_ASSETS_PATH__: any;
|
3 | declare var __ISOMORPHIC_ID__: any;
|
4 | declare var __DATALAYER_ID__: any;
|
5 | declare var __ISOFFLINE__: any;
|
6 |
|
7 | // this must be imported to allow async-functions within an AWS lambda environment
|
8 | // see: https://github.com/babel/babel/issues/5085
|
9 | import "@babel/polyfill";
|
10 |
|
11 | import React, { ReactNode} from "react";
|
12 | import ReactDOMServer from "react-dom/server";
|
13 | import express from "express";
|
14 | import serverless from "serverless-http";
|
15 | import ConnectSequence from 'connect-sequence';
|
16 |
|
17 | // make the styled-components work with server-side rendering
|
18 | import { ServerStyleSheet } from 'styled-components'; // <-- importing ServerStyleSheet
|
19 | import { matchPath } from 'react-router';
|
20 | import helmet from 'react-helmet';
|
21 | import {createServerApp} from "./routed-app";
|
22 | import {getBasename} from '../libs/iso-libs';
|
23 | import {serviceAttachDataLayer} from "./attach-data-layer";
|
24 | import { serviceAttachStorage } from '../components/attach-storage';
|
25 | import { listFiles } from '../storage/storage-libs';
|
26 | import { findComponentRecursively } from '../libs';
|
27 | import {isStorage} from "../storage/storage-component";
|
28 | import { serviceAttachService } from '../components/attach-service';
|
29 | import { callService } from './service-libs';
|
30 |
|
31 | import Types from '../types';
|
32 | import { extractObject, INFRASTRUCTURE_MODES, loadConfigurationFromModule } from '../libs/loader';
|
33 | import { connectWithDataLayer } from './datalayer-integration';
|
34 |
|
35 | import {
|
36 | graphql,
|
37 | GraphQLSchema,
|
38 | GraphQLObjectType,
|
39 | GraphQLString,
|
40 | GraphQLNonNull
|
41 | } from 'graphql';
|
42 |
|
43 | import { getClientFilename } from '../libs/server-libs';
|
44 |
|
45 | //import {loadIsoConfigFromComponent, applyCustomComponents} from "../isolib";
|
46 | //import { applyAppClientModules } from '../types/client-app-config';
|
47 |
|
48 | const createServer = (assetsDir, resolvedAssetsPath, isomorphicId, isOffline) => {
|
49 |
|
50 |
|
51 | // express is the web-framework that lets us configure the endpoints
|
52 | const app = express();
|
53 |
|
54 | // in production, API-Gateway proxy redirects the request to S3
|
55 | // serve static files - the async components of the server - only used of localhost
|
56 | app.use('/'+assetsDir, express.static(resolvedAssetsPath));
|
57 |
|
58 |
|
59 | // load the IsomorphicComponent
|
60 | // we must load it directly from the module here, to enable the aliad of the config_file_path
|
61 | const isoConfig = loadConfigurationFromModule(require('__CONFIG_FILE_PATH__'), INFRASTRUCTURE_MODES.RUNTIME);
|
62 |
|
63 |
|
64 | // let's extract it from the root configuration
|
65 | const isoApp = extractObject(
|
66 | isoConfig,
|
67 | Types.INFRASTRUCTURE_TYPE_CONFIGURATION,
|
68 | isomorphicId
|
69 | )
|
70 | // connect the middlewares
|
71 | isoApp.middlewares.map(mw => app.use(mw.callback));
|
72 |
|
73 |
|
74 | // let's extract it from the root configuration
|
75 | const dataLayer = extractObject(
|
76 | isoConfig,
|
77 | Types.INFRASTRUCTURE_TYPE_COMPONENT,
|
78 | __DATALAYER_ID__
|
79 | );
|
80 |
|
81 | if (dataLayer) {
|
82 |
|
83 | //console.log ("Datalayer Active: ", dataLayer.id)
|
84 |
|
85 | if (isOffline) {
|
86 | //console.log("setOffline!")
|
87 | dataLayer.setOffline(true);
|
88 |
|
89 |
|
90 | } else {
|
91 | //console.log("NOT offline!")
|
92 |
|
93 | const cors = require('cors');
|
94 |
|
95 | const corsOptions = {
|
96 | origin(origin, callback) {
|
97 | callback(null, true);
|
98 | },
|
99 | credentials: true
|
100 | };
|
101 | app.use(cors(corsOptions));
|
102 |
|
103 | // TODO only allow the domains of the app (S3, APIs)
|
104 | var allowCrossDomain = function(req, res, next) {
|
105 | res.header('Access-Control-Allow-Origin', '*');
|
106 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
|
107 | //res.header('Access-Control-Allow-Headers', 'Content-Type,token');
|
108 | next();
|
109 | }
|
110 | app.use(allowCrossDomain);
|
111 |
|
112 | }
|
113 |
|
114 | app.use('/query', async (req, res, next) => {
|
115 | //console.log("query-endpoint / offline: ", isOffline);
|
116 | const parsedBody = JSON.parse(req.body);
|
117 | //console.log(parsedBody)
|
118 |
|
119 | await graphql(dataLayer.getSchema(false), parsedBody.query).then(
|
120 | result_type => {
|
121 | const entryQueryName = Object.keys(result_type.data)[0];
|
122 |
|
123 | // when the query resolves, we get back
|
124 | //console.log("pre-resolve | found entry: ", entryQueryName)
|
125 |
|
126 | new ConnectSequence(req, res, next)
|
127 | .append(...dataLayer.entries.filter(entry => entry.providesQuery(entryQueryName)).map(entry=> entry.middleware.callback))
|
128 | .append(async (req, res, next) => {
|
129 |
|
130 | //console.log("DL-mw: req: ");
|
131 | //const parsedBody = JSON.parse(req.body);
|
132 | //console.log("parsedBody: ", parsedBody);
|
133 |
|
134 | // now we let the schema resolve with data
|
135 | await graphql(dataLayer.getSchema(true), parsedBody.query).then(
|
136 | result => res.status(200).set({
|
137 | "Access-Control-Allow-Origin" : "*", // Required for CORS support to work
|
138 | "Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
|
139 | }).send(JSON.stringify(result)),
|
140 | err => res.set({
|
141 | "Access-Control-Allow-Origin" : "*", // Required for CORS support to work
|
142 | "Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
|
143 | }).status(500).send(err)
|
144 | );
|
145 | })
|
146 | .run();
|
147 | },
|
148 | err => res.set({
|
149 | "Access-Control-Allow-Origin" : "*", // Required for CORS support to work
|
150 | "Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
|
151 | }).status(500).send(err)
|
152 | );
|
153 | });
|
154 |
|
155 | };
|
156 |
|
157 | // let's extract it from the root configuration
|
158 | const storages = findComponentRecursively(isoConfig, c => isStorage(c));
|
159 |
|
160 | const reqListFiles = (
|
161 | storageId: string,
|
162 | prefix: string,
|
163 | listMode: string,
|
164 | data: any,
|
165 | onComplete: (data: any) => void,
|
166 | onError: (err: string) => void
|
167 | ) => {
|
168 |
|
169 | //console.log("listFiles function: ", storageId);
|
170 | //console.log("found storages: ", storages);
|
171 |
|
172 | const storage = storages.find(storage => storage.id == storageId);
|
173 | if (storage) {
|
174 | listFiles(storageId, prefix, listMode, data, onComplete, onError, storage, isOffline);
|
175 | } else {
|
176 | onError("could not find storage with id "+ storageId);
|
177 | }
|
178 | };
|
179 |
|
180 | const reqCallService = (
|
181 | id: string,
|
182 | args: any,
|
183 | onResult: (result: any) => void,
|
184 | onError: (error: any) => void,
|
185 | config: any,
|
186 | isOffline: Boolean = false
|
187 | ) => callService(id, args,onResult,onError,isoConfig,isOffline);
|
188 |
|
189 |
|
190 | // flattens the callbacks
|
191 | const unpackMiddlewares = (middlewares) => {
|
192 | // always returns the list of callbacks
|
193 | const cbList = (mw) => Array.isArray(mw.callback) ? mw.callback : [mw.callback];
|
194 | return middlewares.reduce(
|
195 | (res,mw) => res.concat(...cbList(mw)),
|
196 | // attach the callService-function
|
197 | [serviceAttachService(reqCallService)].concat(
|
198 | // when we have a dataLayer, let's attach it to the request
|
199 | dataLayer ? [serviceAttachDataLayer(dataLayer)] : [],
|
200 |
|
201 | //when we have a storage, attach the listFiles-function
|
202 | storages ? [ serviceAttachStorage(reqListFiles)] : []
|
203 | )
|
204 | );
|
205 | };
|
206 |
|
207 |
|
208 | // split the clientApps here and define a function for each of the clientApps, with the right middleware
|
209 | isoApp.services.map(service => {
|
210 |
|
211 |
|
212 |
|
213 | //console.log("found service: ", service);
|
214 |
|
215 | if (service.method.toUpperCase() == "GET") {
|
216 | app.get(service.path, ...unpackMiddlewares(service.middlewares));
|
217 |
|
218 | } else if (service.method.toUpperCase() == "POST") {
|
219 | app.post(service.path, ...unpackMiddlewares(service.middlewares));
|
220 |
|
221 | } else if (service.method.toUpperCase() == "PUT") {
|
222 | app.put(service.path, ...unpackMiddlewares(service.middlewares));
|
223 |
|
224 | } else if (service.method.toUpperCase() == "DELETE") {
|
225 | app.delete(service.path, ...unpackMiddlewares(service.middlewares));
|
226 |
|
227 | }
|
228 |
|
229 | return service;
|
230 | });
|
231 |
|
232 | //console.log("webApps: ",isoApp.webApps.length, " -> ", isoApp.webApps);
|
233 |
|
234 | // split the clientApps here and define a function for each of the clientApps, with the right middleware
|
235 | isoApp.webApps
|
236 | //.filter(clientApp => clientApp.middlewares !== undefined)
|
237 | .map(clientApp => {
|
238 |
|
239 | const serveMiddleware = (req, res, next) => serve(req, res, next, clientApp, assetsDir, isoConfig, isOffline);
|
240 | const routes = clientApp.routes.filter(route => route.middlewares !== undefined && route.middlewares.length > 0);
|
241 |
|
242 | if (clientApp.method.toUpperCase() == "GET") {
|
243 |
|
244 | app.get(clientApp.path, ...unpackMiddlewares(clientApp.middlewares));
|
245 | routes.forEach(route => app.get(route.path, ...unpackMiddlewares(route.middlewares)));
|
246 | app.get(clientApp.path, serveMiddleware);
|
247 |
|
248 | } else if (clientApp.method.toUpperCase() == "POST") {
|
249 |
|
250 | app.post(clientApp.path, ...unpackMiddlewares(clientApp.middlewares));
|
251 | routes.forEach(route => app.post(route.path, ...unpackMiddlewares(route.middlewares)));
|
252 | app.post(clientApp.path, serveMiddleware);
|
253 |
|
254 | } else if (clientApp.method.toUpperCase() == "PUT") {
|
255 |
|
256 | app.put(clientApp.path, ...unpackMiddlewares(clientApp.middlewares));
|
257 | routes.forEach(route => app.put(route.path, ...unpackMiddlewares(route.middlewares)));
|
258 | app.put(clientApp.path, serveMiddleware);
|
259 |
|
260 | } else if (clientApp.method.toUpperCase() == "DELETE") {
|
261 |
|
262 | app.delete(clientApp.path, ...unpackMiddlewares(clientApp.middlewares));
|
263 | routes.forEach(route => app.delete(route.path, ...unpackMiddlewares(route.middlewares)));
|
264 | app.delete(clientApp.path, serveMiddleware);
|
265 |
|
266 | }
|
267 |
|
268 | return clientApp;
|
269 | });
|
270 |
|
271 |
|
272 | return app;
|
273 | };
|
274 |
|
275 |
|
276 | async function serve (req, res, next, clientApp, assetsDir, isoConfig, isOffline) {
|
277 |
|
278 | //console.log("serve - isOffline: ", isOffline)
|
279 |
|
280 | //TODO use try catch depending on the environment
|
281 | //try {
|
282 |
|
283 |
|
284 | //context is used by react router, empty by default
|
285 | let context: any = {};
|
286 |
|
287 |
|
288 | const basename = getBasename();
|
289 |
|
290 | // creating the stylesheet
|
291 | const sheet = new ServerStyleSheet();
|
292 |
|
293 | const parsedUrl = req.url.indexOf("?") >= 0 ? req.url.substring(0, req.url.indexOf("?")) : req.url;
|
294 | //console.log("parsedUrl: ", parsedUrl);
|
295 |
|
296 |
|
297 | ////////// TODO refactor
|
298 | var foundPath = undefined;
|
299 |
|
300 |
|
301 | // match request url to our React Router paths and grab the path-params
|
302 | let matchResult = clientApp.routes.find(
|
303 | ({ path, exact }) => {
|
304 | foundPath = matchPath(parsedUrl,
|
305 | {
|
306 | path: path,
|
307 | exact: exact,
|
308 | strict: false
|
309 | }
|
310 | )
|
311 | return foundPath
|
312 | }) || {};
|
313 | let { path } = matchResult;
|
314 |
|
315 | //console.log("found: ", foundPath);
|
316 | //console.log("server: path params: ", foundPath ? foundPath.params : "---");
|
317 | //console.log("url: ", req.url);
|
318 |
|
319 | const routePath = foundPath ? (
|
320 | foundPath.path.indexOf("/:") > 0 ?
|
321 | foundPath.path.substring(0, foundPath.path.indexOf("/:")) :
|
322 | foundPath.path
|
323 | ) : "";
|
324 |
|
325 | //console.log("routePath: ", routePath);
|
326 | ////////// END OF REFACTORING required
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 | //console.log("app data layer id: ", clientApp.dataLayerId);
|
335 |
|
336 | const serverState = {};
|
337 | const setServerValue = (id, value, isInitial=false) => {
|
338 | if (!serverState[id] || !isInitial) {
|
339 | serverState[id] = value;
|
340 | //console.log("set >>", id, "<< -> ", value);
|
341 | }
|
342 |
|
343 |
|
344 | }
|
345 |
|
346 | const getIsomorphicState = () => {
|
347 | //console.log("serverState: ", serverState);
|
348 | return `window.__ISOMORPHICSTATE__ = ${JSON.stringify(serverState)}`
|
349 | };
|
350 |
|
351 | const fConnectWithDataLayer = clientApp.dataLayerId !== undefined ?
|
352 | connectWithDataLayer(clientApp.dataLayerId, req, isOffline) :
|
353 | async function (app) {
|
354 | //console.log("default dummy data layer")
|
355 | return {connectedApp: app, getState: () => ""};
|
356 | };
|
357 |
|
358 | /*const serverApp = await new Promise((resolve, reject) => {
|
359 |
|
360 | });*/
|
361 |
|
362 | var renderList = [];
|
363 | const addToRenderList = (fRenderSsr, hashValue) => {
|
364 | //console.log("add to Render: ", hashValue);
|
365 | renderList = renderList.concat([{fRenderSsr: fRenderSsr, hashValue: hashValue}]);
|
366 | };
|
367 |
|
368 | const completeSSR = (htmlData, getState, renderListResults) => {
|
369 |
|
370 | // getting all the tags from the sheet
|
371 | const styles = sheet.getStyleTags();
|
372 |
|
373 | //render helmet data aka meta data in <head></head>
|
374 | const helmetData = helmet.renderStatic();
|
375 |
|
376 | const fRender = clientApp.renderHtmlPage ? clientApp.renderHtmlPage : renderHtmlPage;
|
377 |
|
378 | // render a page with the state and return it in the response
|
379 | res.status(200).send(
|
380 | fRender(htmlData, styles, getState(), getIsomorphicState(), `window.__RENDERLISTSTATE__ = ${JSON.stringify(renderListResults)}`, helmetData, basename, req.url, clientApp, assetsDir)
|
381 | ).end();
|
382 | };
|
383 |
|
384 | async function renderApp (apolloState, oldServerState, oldStorageState, htmlData, getApolloState, newStorageState) {
|
385 |
|
386 | const tempApolloState = getApolloState();
|
387 | const tempServerState = Object.assign({}, serverState);
|
388 |
|
389 | /*console.log("APOLLO STATE BEFORE", apolloState, "\nAPOLLO STATE AFTER ", tempApolloState);
|
390 | console.log("SERVER STATE BEFORE", oldServerState, "\nSERVER STATE AFTER ", serverState);
|
391 | console.log("STORAGE STATE BEFORE", oldStorageState, "\nSTORAGE STATE AFTER ", newStorageState);
|
392 | */
|
393 |
|
394 | if (JSON.stringify(apolloState) !== JSON.stringify(tempApolloState) ||
|
395 | JSON.stringify(oldServerState) !== JSON.stringify(serverState) ||
|
396 | (oldStorageState && oldStorageState.length !== newStorageState.length)
|
397 | ) {
|
398 | //console.log("--------- need to render --------");
|
399 |
|
400 | // create the app and connect it with the DataAbstractionLayer
|
401 | await fConnectWithDataLayer(
|
402 | createServerApp(
|
403 | clientApp.routes,
|
404 | clientApp.redirects,
|
405 | basename,
|
406 | req.url,
|
407 | context,
|
408 | req,
|
409 | require('infrastructure-components').getAuthCallback(isoConfig, clientApp.authenticationId),
|
410 | setServerValue,
|
411 | addToRenderList,
|
412 | isoConfig,
|
413 | isOffline,
|
414 | newStorageState,
|
415 | serverState
|
416 | )
|
417 | ).then(async ({connectedApp, getState}) => {
|
418 |
|
419 | //console.log("renderList before: ", renderList)
|
420 | // collect the styles from the connected appsheet.collectStyles(
|
421 | const newHtmlData = ReactDOMServer.renderToString(connectedApp);
|
422 |
|
423 | const addToStorage = await Promise.all(
|
424 | renderList.filter((item, index, arr)=> {
|
425 | const c = arr.map(item=> item.hashValue);
|
426 | return index === c.indexOf(item.hashValue)
|
427 | }).map(({fRenderSsr, hashValue}) => new Promise((resolve, reject) => {
|
428 | return fRenderSsr(
|
429 | ({data, files})=>{
|
430 | //console.log("resolved: ", files);
|
431 | resolve({
|
432 | hashValue: hashValue,
|
433 | data: data,
|
434 | files: files,
|
435 | })
|
436 | }, err=>{
|
437 | //console.log("rejected: ", err);
|
438 | reject(err);
|
439 | }
|
440 | )
|
441 | }))
|
442 | );
|
443 |
|
444 | //console.log("renderList after: ", renderList)
|
445 | renderList.length = 0;
|
446 |
|
447 | //console.log("addToStorage: ", addToStorage);
|
448 |
|
449 | //completeSSR(newHtmlData, getState, storageState);
|
450 | await renderApp (tempApolloState, tempServerState, newStorageState,
|
451 | newHtmlData, getState, newStorageState.concat(addToStorage));
|
452 | });
|
453 |
|
454 | } else {
|
455 | //console.log("----- no difference, return ------");
|
456 | completeSSR(htmlData, getApolloState, newStorageState);
|
457 | };
|
458 | };
|
459 |
|
460 | await renderApp ({}, serverState, undefined, "", ()=> "", []);
|
461 |
|
462 | /*
|
463 | // create the app and connect it with the DataAbstractionLayer
|
464 | await fConnectWithDataLayer(
|
465 | createServerApp(
|
466 | clientApp.routes,
|
467 | clientApp.redirects,
|
468 | basename,
|
469 | req.url,
|
470 | context,
|
471 | req,
|
472 | require('infrastructure-components').getAuthCallback(isoConfig, clientApp.authenticationId),
|
473 | setServerValue,
|
474 | addToRenderList,
|
475 | isoConfig,
|
476 | isOffline,
|
477 | undefined,
|
478 | undefined
|
479 | )
|
480 | ).then(async ({connectedApp, getState}) => {
|
481 |
|
482 | //console.log("resolved... renderList: ", renderList);
|
483 |
|
484 | // collect the styles from the connected appsheet.collectStyles(
|
485 | const htmlData = ReactDOMServer.renderToString(connectedApp);
|
486 | //console.log(htmlData);
|
487 |
|
488 | const renderListResults = await Promise.all(
|
489 | renderList.filter((item, index, arr)=> {
|
490 | const c = arr.map(item=> item.hashValue);
|
491 | return index === c.indexOf(item.hashValue)
|
492 | }).map(({fRenderSsr, hashValue}) => new Promise((resolve, reject) => {
|
493 | return fRenderSsr(
|
494 | (data, files)=>{
|
495 | //console.log("resolved: ", files);
|
496 | resolve({
|
497 | hashValue: hashValue,
|
498 | data: data,
|
499 | files: files,
|
500 | })
|
501 | }, err=>{
|
502 | //console.log("rejected: ", err);
|
503 | reject(err);
|
504 | }
|
505 | )
|
506 | }))
|
507 | );
|
508 |
|
509 |
|
510 | if (renderListResults && renderListResults.length > 0) {
|
511 | //console.log("need to rerender! got renderListResults: ", renderListResults);
|
512 |
|
513 | await renderApp ({}, serverState, renderListResults, htmlData, getState);
|
514 |
|
515 | } else {
|
516 | completeSSR(htmlData, getState, renderListResults);
|
517 | }
|
518 |
|
519 | });*/
|
520 |
|
521 | }
|
522 |
|
523 |
|
524 | /**
|
525 | *
|
526 | * This functions puts together the whole Html
|
527 | *
|
528 | * - style as collected from the styled-components
|
529 | * - head-meta data: from helmet
|
530 | * - data state: from the DAL
|
531 | * - basename: to let the client know
|
532 | *
|
533 | * The html loads the script from the path where we find the assets. This is part of the config, thus load it
|
534 | * using `require('../config').pathToAssets(process.env.STAGE_PATH)`
|
535 | *
|
536 | * The script loading the app.bundle.js uses the window location in order to find out whether there is a slash
|
537 | *
|
538 | * //TODO the app.bundle.js depends on the name "app". Paramterize this!
|
539 | *
|
540 | * //TODO: apply the same base style as the client does
|
541 | *
|
542 | * when we are in a sub-route, e.g. when we have path parameters, we need to add ../ to the path to the assets
|
543 | *
|
544 | * Routing to the Assets
|
545 | *
|
546 | * entered url | basename==/ | basename==/dev
|
547 | * ---------------------------------------------
|
548 | * (none) / /dev
|
549 | * / / /dev
|
550 | * (dev)/ / /
|
551 | * (dev)/route / /
|
552 | * (dev)/route/ ../ ../
|
553 | * (dev)/route/:var ../ ../
|
554 | * TODO what happens with more than one path parameter?
|
555 | *
|
556 | *
|
557 | * @param host the host of the request
|
558 | * @param html
|
559 | * @param styles
|
560 | * @param preloadedState the state in form of a script
|
561 | * @param helmet
|
562 | */
|
563 | function renderHtmlPage(html, styles, preloadedState, isomorphicState, renderListResults, helmet, basename, routePath, clientApp, assetsDir) {
|
564 | //<link rel="icon" href="/assets/favicon.ico" type="image/ico" />
|
565 | //console.log(preloadedState);
|
566 | const path = require('path');
|
567 |
|
568 | const calcBase = () => {
|
569 | return path.join(basename, routePath);
|
570 | }
|
571 |
|
572 |
|
573 |
|
574 | //For each"/" in the entered path after the basename, we need to add "../" to the assets-path
|
575 | //when there is a basename, it must be added
|
576 |
|
577 | //console.log("calcBase: ", calcBase());
|
578 |
|
579 | return `<!doctype html>
|
580 | <html>
|
581 | <head>
|
582 | <meta charset="utf-8" />
|
583 | ${helmet.title.toString()}
|
584 | ${helmet.meta.toString()}
|
585 | ${helmet.link.toString()}
|
586 | <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
587 | ${styles}
|
588 | <style>
|
589 | body {
|
590 | display: block;
|
591 | margin: 0px;
|
592 | }
|
593 | </style>
|
594 | </head>
|
595 | <body>
|
596 | <div id="root">${html.trim()}</div>
|
597 | <script>
|
598 | ${preloadedState}
|
599 | ${isomorphicState}
|
600 | ${renderListResults}
|
601 | window.__BASENAME__ = "${basename}";
|
602 | </script>
|
603 | <script>
|
604 | var loadscript = document.createElement('script');
|
605 | function getPath() {
|
606 | const basePath = ${basename !== "/" ? "window.location.pathname.startsWith(\""+basename+"\") ? \"\": \"/\" " : "\"\"" };
|
607 | const routePath= "${routePath !== "/" ? routePath : ""}";
|
608 | const pre = window.location.pathname.startsWith(basePath+routePath+"/") ? ".." : "";
|
609 | return pre+"${path.join(basename, assetsDir, getClientFilename(clientApp.id))}";
|
610 |
|
611 | }
|
612 |
|
613 | loadscript.setAttribute('src',getPath());
|
614 | document.body.appendChild(loadscript);
|
615 | </script>
|
616 |
|
617 | </body>
|
618 | </html>`
|
619 | }
|
620 |
|
621 |
|
622 | // we're exporting the handler-function as default, must match the sls-config!
|
623 | //export default (assetsDir, resolvedAssetsPath) => serverless(createServer(assetsDir, resolvedAssetsPath));
|
624 |
|
625 | /*
|
626 | const serverIndexPath = path.join(serverPath, "index.js");
|
627 | fs.writeFileSync(serverIndexPath, `const lib = require ('./server');
|
628 | const server = lib.default('${ssrConfig.assetsPath}', '${resolveAssetsPath(ssrConfig)}');
|
629 | exports.default = server;*/
|
630 |
|
631 | // these variables are replaced during compilation
|
632 | export default serverless(createServer(__ASSETS_PATH__, __RESOLVED_ASSETS_PATH__, __ISOMORPHIC_ID__, __ISOFFLINE__)); |
\ | No newline at end of file |