FoalTS / foal

Full-featured Node.js framework, with no complexity. 🚀 Simple and easy to use, TypeScript-based and well-documented.
https://foalts.org/
MIT License
1.88k stars 137 forks source link

SSR with built-in templates not working #1211

Closed warren-gallagher closed 3 months ago

warren-gallagher commented 1 year ago

Version of FoalTS: 3.2

Getting Error

Error: ENOENT: no such file or directory, open '/Users/wgallagher/dev/aq/aq-touchpoint-demo/build/app/templates/aq-touchpoint.html'

The src files are in: aq-touchpoint-demo/src/app/templates

Getting an error whereby the template file cannot be found. It looks like the build is not copying the templates into the build directory. I've tried moving the directory under the controllers directory and adding the __dirname as well.

It would appear the template directory and its contents are not being copied into the build hierarchy.

@Get('/aq-touchpoint-verify')
 async aqTouchpointVerify(ctx: Context) {
    const httpResponse = await render('./templates/aq-touchpoint.html', {
      touchpointId: 'cd544334-dd07-4010-9285-3b78461b7dae',
      title: 'Verify - <aq-touchpoint/>'
    }, __dirname);
    httpResponse.setHeader('Access-Control-Allow-Origin', '*');
    httpResponse.setHeader('Content-Type', 'text/html');
    return httpResponse;
 }
LoicPoullain commented 1 year ago

The templates are not copied by default because the framework supposes that, usually, there is one templates directory at the root at the project.

If you want to add the templates inside the src you will need to install another library to copy them during the build

npm install cpx2  --save-dev
{
  "scripts": {
    "build": "foal rmdir build && cpx \"src/**/*.html\" build && tsc -p tsconfig.app.json",
    "dev": "npm run build && concurrently \"cpx \\\"src/**/*. html\\\" build -w\" \"tsc -p tsconfig.app.json -w\" \"supervisor -w ./build,./config -e js,json,yml, html --no-restart-on error ./build/index.js\"",
    "build:test": "foal rmdir build && cpx \"src/**/*. html\" build && tsc -p tsconfig.test.json",
    "test": "npm run build:test && concurrently \"cpx \\\"src/**/*. html\\\" build -w\" \"tsc -p tsconfig.test.json -w\" \"mocha --file ./build/test.js -w --watch-files build \\\"./build/**/*.spec.js\\\"\"",
    "build:e2e": "foal rmdir build && cpx \"src/**/*. html\" build && tsc -p tsconfig.e2e.json",
    "e2e": "npm run build:e2e && concurrently \"cpx \\\"src/**/*. html\\\" build -w\" \"tsc -p tsconfig.e2e.json -w\" \"mocha --file ./build/e2e.js -w --watch-files build \\\"./build/e2e/**/*.js\\\"\"",
    ...
  }
}
warren-gallagher commented 1 year ago

Thanks for the response. It might be useful to have this as part of the documentation at: https://foalts.org/docs/frontend/server-side-rendering#rendering-templates

I came up with a less elegant solution (not being aware of the cpx package). I like yours better. ;)

  1. Modify the scripts within package.json to copy all of the templates, but only when doing a build.
    "scripts": {
    "build": "foal rmdir build && tsc -p tsconfig.app.json && npm run copy-templates",
    "start": "node ./build/index.js",
    "dev": "npm run build && concurrently -r \"tsc -p tsconfig.app.json -w\" \"supervisor -w ./build,./config -e js,json,yml --no-restart-on error ./build/index.js\"",
    "build:test": "foal rmdir build && tsc -p tsconfig.test.json",
    "start:test": "mocha --file ./build/test.js \"./build/**/*.spec.js\"",
    "test": "npm run build:test && concurrently -r \"tsc -p tsconfig.test.json -w\" \"mocha --file ./build/test.js -w \\\"./build/**/*.spec.js\\\"\"",
    "build:e2e": "foal rmdir build && tsc -p tsconfig.e2e.json",
    "start:e2e": "mocha --timeout 4000 --file ./build/e2e.js \"./build/e2e/**/*.js\"",
    "e2e": "npm run build:e2e && concurrently -r \"tsc -p tsconfig.e2e.json -w\" \"mocha --file ./build/e2e.js -w \\\"./build/e2e/**/*.js\\\"\"",
    "lint": "eslint --ext ts src",
    "lint:fix": "eslint --ext ts --fix src",
    "makemigrations": "foal rmdir build && tsc -p tsconfig.app.json && npx typeorm migration:generate src/migrations/migration -d build/db -p && tsc -p tsconfig.app.json",
    "migrations": "npx typeorm migration:run -d build/db",
    "revertmigration": "npx typeorm migration:revert -d build/db",
    "copy-templates": "mkdir -p ./build/app/templates && cp -r ./src/app/templates/ ./build/app/templates/."
    },
  2. Create a controller base class that any other controller could inherit from that finds the templates in the source directory when running in "development" mode else, expect to find copied templates in the build directory:
    
    import { Env, render, HttpResponseBadRequest } from '@foal/core';
    import { constants } from 'fs';
    import { access } from 'fs/promises';

export abstract class AbstractRenderController {

protected async renderTemplate(templateName: string, locals: object = {} ) { const environment = Env.get('NODE_ENV') switch( environment ) { case 'development': { const packageRootPath = dirname.slice(0, dirname.indexOf('/build/app')) const path = ${packageRootPath}/src/app/templates/${templateName}; try { await access(path, constants.R_OK);

      return (await render(path, locals)).setHeader('Access-Control-Allow-Origin', '*');
    }
    catch( e ) {
      return new HttpResponseBadRequest({reason: `Can not read template ${path} - ${JSON.stringify(e,null,2)}`});
    }
    break;
  }
  default:{
    return await render(`templates/${templateName}`, locals, __dirname)
  }
}

} }