server.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import 'zone.js/dist/zone-node';
  2. import { ngExpressEngine } from '@nguniversal/express-engine';
  3. import * as express from 'express';
  4. import { join } from 'path';
  5. import { AppServerModule } from './src/main.server';
  6. import { APP_BASE_HREF } from '@angular/common';
  7. import { existsSync, readFileSync } from 'fs';
  8. // Simulation DOM (nécessaire pour certaines librairies côté serveur)
  9. const domino = require('domino');
  10. const distFolder = join(process.cwd(), 'dist/fatboar/browser');
  11. const indexHtml = existsSync(join(distFolder, 'index.html')) ? 'index.html' : 'index';
  12. const template = readFileSync(join(distFolder, indexHtml)).toString();
  13. // Simuler un DOM global pour les libs (Leaflet, Material, etc.)
  14. const win = domino.createWindow(template);
  15. global['window'] = win;
  16. global['document'] = win.document;
  17. global['navigator'] = win.navigator;
  18. global['CSS'] = null;
  19. export function app() {
  20. const server = express();
  21. // Moteur de rendu Angular Universal
  22. server.engine('html', ngExpressEngine({
  23. bootstrap: AppServerModule,
  24. }));
  25. server.set('view engine', 'html');
  26. server.set('views', distFolder);
  27. // Fichiers statiques (CSS, JS, images…)
  28. server.get('*.*', express.static(distFolder, { maxAge: '1y' }));
  29. // Routes SEO
  30. server.get('/sitemap.xml', (req, res) => {
  31. res.header('Content-Type', 'application/xml');
  32. res.sendFile(join(distFolder, 'sitemap.xml'));
  33. });
  34. server.get('/robots.txt', (req, res) => {
  35. res.sendFile(join(distFolder, 'robots.txt'));
  36. });
  37. // Toutes les autres routes => rendu SSR
  38. server.get('*', (req, res) => {
  39. res.render(indexHtml, {
  40. req,
  41. providers: [
  42. { provide: APP_BASE_HREF, useValue: req.baseUrl },
  43. { provide: 'CANONICAL_URL', useValue: `https://angular-dev.foodgame.fr${req.originalUrl}` }
  44. ]
  45. }, (err, html) => {
  46. if (err) {
  47. console.error(err);
  48. return res.status(500).send(err);
  49. }
  50. // Injection Preboot pour capturer les interactions avant bootstrap Angular
  51. const prebootScript = `
  52. <script src="https://unpkg.com/preboot"></script>
  53. <script>preboot.init({ appRoot: 'app-root' });</script>
  54. `;
  55. // Injection des CSS directement dans le SSR pour éviter le flash
  56. const styles = `
  57. <link rel="stylesheet" href="styles.css">
  58. <link rel="stylesheet" href="assets/theme.css">
  59. `;
  60. res.send(
  61. html
  62. .replace('</head>', `${styles}</head>`)
  63. .replace('</body>', `${prebootScript}</body>`)
  64. );
  65. });
  66. });
  67. return server;
  68. }
  69. function run() {
  70. const port = process.env.PORT || 4000;
  71. const server = app();
  72. server.listen(port, () => {
  73. console.log(`Node Express server listening on http://localhost:${port}`);
  74. });
  75. }
  76. // Exécuter le serveur si lancé directement
  77. declare const __non_webpack_require__: NodeRequire;
  78. const mainModule = __non_webpack_require__.main;
  79. const moduleFilename = mainModule && mainModule.filename || '';
  80. if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  81. run();
  82. }
  83. export * from './src/main.server';