mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
fix: Add filename parameter for EJS templates
This is required if we want to include partial templates
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import type { TemplateFunction } from 'ejs';
|
||||
import { compile, render } from 'ejs';
|
||||
import type { TemplateEngine, Template } from './TemplateEngine';
|
||||
import { readTemplate } from './TemplateEngine';
|
||||
import { getTemplateFilePath, readTemplate } from './TemplateEngine';
|
||||
import Dict = NodeJS.Dict;
|
||||
|
||||
/**
|
||||
@@ -16,13 +16,16 @@ export class EjsTemplateEngine<T extends Dict<any> = Dict<any>> implements Templ
|
||||
* @param template - The default template @range {json}
|
||||
*/
|
||||
public constructor(template?: Template) {
|
||||
// EJS requires the `filename` parameter to be able to include partial templates
|
||||
const filename = getTemplateFilePath(template);
|
||||
this.applyTemplate = readTemplate(template)
|
||||
.then((templateString: string): TemplateFunction => compile(templateString));
|
||||
.then((templateString: string): TemplateFunction => compile(templateString, { filename }));
|
||||
}
|
||||
|
||||
public async render(contents: T): Promise<string>;
|
||||
public async render<TCustom = T>(contents: TCustom, template: Template): Promise<string>;
|
||||
public async render<TCustom = T>(contents: TCustom, template?: Template): Promise<string> {
|
||||
return template ? render(await readTemplate(template), contents) : (await this.applyTemplate)(contents);
|
||||
const options = { ...contents, filename: getTemplateFilePath(template) };
|
||||
return template ? render(await readTemplate(template), options) : (await this.applyTemplate)(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,20 +36,32 @@ export interface TemplateEngine<T extends Dict<any> = Dict<any>> {
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/method-signature-style */
|
||||
|
||||
/**
|
||||
* Returns the absolute path to the template.
|
||||
* Returns undefined if the input does not contain a file path.
|
||||
*/
|
||||
export function getTemplateFilePath(template?: Template): string | undefined {
|
||||
// The template has been passed as a filename
|
||||
if (typeof template === 'string') {
|
||||
return getTemplateFilePath({ templateFile: template });
|
||||
}
|
||||
// The template has already been given as a string so no known path
|
||||
if (!template || 'templateString' in template) {
|
||||
return;
|
||||
}
|
||||
const { templateFile, templatePath } = template;
|
||||
const fullTemplatePath = templatePath ? joinFilePath(templatePath, templateFile) : templateFile;
|
||||
return resolveAssetPath(fullTemplatePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the template and returns it as a string.
|
||||
*/
|
||||
export async function readTemplate(template: Template = { templateString: '' }): Promise<string> {
|
||||
// The template has been passed as a filename
|
||||
if (typeof template === 'string') {
|
||||
return readTemplate({ templateFile: template });
|
||||
}
|
||||
// The template has already been given as a string
|
||||
if ('templateString' in template) {
|
||||
if (typeof template === 'object' && 'templateString' in template) {
|
||||
return template.templateString;
|
||||
}
|
||||
// The template needs to be read from disk
|
||||
const { templateFile, templatePath } = template;
|
||||
const fullTemplatePath = templatePath ? joinFilePath(templatePath, templateFile) : templateFile;
|
||||
return fsPromises.readFile(resolveAssetPath(fullTemplatePath), 'utf8');
|
||||
return fsPromises.readFile(getTemplateFilePath(template)!, 'utf8');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { EjsTemplateEngine } from '../../../../src/util/templates/EjsTemplateEngine';
|
||||
|
||||
jest.mock('../../../../src/util/templates/TemplateEngine', (): any => ({
|
||||
getTemplateFilePath: jest.fn((): string => `filename`),
|
||||
readTemplate: jest.fn(async({ templateString }): Promise<string> => `${templateString}: <%= detail %>`),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,40 +1,77 @@
|
||||
import { resolveAssetPath } from '../../../../src/util/PathUtil';
|
||||
import { readTemplate } from '../../../../src/util/templates/TemplateEngine';
|
||||
import { getTemplateFilePath, readTemplate } from '../../../../src/util/templates/TemplateEngine';
|
||||
import { mockFs } from '../../../util/Util';
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
describe('readTemplate', (): void => {
|
||||
const templateFile = 'template.xyz';
|
||||
const templatePath = 'other';
|
||||
describe('TemplateEngine', (): void => {
|
||||
describe('#getTemplateFilePath', (): void => {
|
||||
const templateFile = 'template.xyz';
|
||||
const templatePath = 'other';
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
const { data } = mockFs(resolveAssetPath(''));
|
||||
Object.assign(data, {
|
||||
'template.xyz': '{{template}}',
|
||||
other: {
|
||||
'template.xyz': '{{other}}',
|
||||
},
|
||||
beforeEach(async(): Promise<void> => {
|
||||
const { data } = mockFs(resolveAssetPath(''));
|
||||
Object.assign(data, {
|
||||
'template.xyz': '{{template}}',
|
||||
other: {
|
||||
'template.xyz': '{{other}}',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the undefined when no template is provided.', async(): Promise<void> => {
|
||||
expect(getTemplateFilePath()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns the input if it was a filename.', async(): Promise<void> => {
|
||||
expect(getTemplateFilePath(templateFile)).toBe(resolveAssetPath(templateFile));
|
||||
});
|
||||
|
||||
it('returns undefined for options with a string template.', async(): Promise<void> => {
|
||||
expect(getTemplateFilePath({ templateString: 'abc' })).toBeUndefined();
|
||||
});
|
||||
|
||||
it('accepts options with a filename.', async(): Promise<void> => {
|
||||
expect(getTemplateFilePath({ templateFile })).toBe(resolveAssetPath(templateFile));
|
||||
});
|
||||
|
||||
it('accepts options with a filename and a path.', async(): Promise<void> => {
|
||||
expect(getTemplateFilePath({ templateFile, templatePath })).toBe(resolveAssetPath('other/template.xyz'));
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the empty string when no template is provided.', async(): Promise<void> => {
|
||||
await expect(readTemplate()).resolves.toBe('');
|
||||
});
|
||||
describe('#readTemplate', (): void => {
|
||||
const templateFile = 'template.xyz';
|
||||
const templatePath = 'other';
|
||||
|
||||
it('accepts a filename.', async(): Promise<void> => {
|
||||
await expect(readTemplate(templateFile)).resolves.toBe('{{template}}');
|
||||
});
|
||||
beforeEach(async(): Promise<void> => {
|
||||
const { data } = mockFs(resolveAssetPath(''));
|
||||
Object.assign(data, {
|
||||
'template.xyz': '{{template}}',
|
||||
other: {
|
||||
'template.xyz': '{{other}}',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts options with a string template.', async(): Promise<void> => {
|
||||
await expect(readTemplate({ templateString: 'abc' })).resolves.toBe('abc');
|
||||
});
|
||||
it('returns the empty string when no template is provided.', async(): Promise<void> => {
|
||||
await expect(readTemplate()).resolves.toBe('');
|
||||
});
|
||||
|
||||
it('accepts options with a filename.', async(): Promise<void> => {
|
||||
await expect(readTemplate({ templateFile })).resolves.toBe('{{template}}');
|
||||
});
|
||||
it('accepts a filename.', async(): Promise<void> => {
|
||||
await expect(readTemplate(templateFile)).resolves.toBe('{{template}}');
|
||||
});
|
||||
|
||||
it('accepts options with a filename and a path.', async(): Promise<void> => {
|
||||
await expect(readTemplate({ templateFile, templatePath })).resolves.toBe('{{other}}');
|
||||
it('accepts options with a string template.', async(): Promise<void> => {
|
||||
await expect(readTemplate({ templateString: 'abc' })).resolves.toBe('abc');
|
||||
});
|
||||
|
||||
it('accepts options with a filename.', async(): Promise<void> => {
|
||||
await expect(readTemplate({ templateFile })).resolves.toBe('{{template}}');
|
||||
});
|
||||
|
||||
it('accepts options with a filename and a path.', async(): Promise<void> => {
|
||||
await expect(readTemplate({ templateFile, templatePath })).resolves.toBe('{{other}}');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user