diff --git a/README.md b/README.md
index 095b970..3990aae 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ Eta is a lightweight and blazing fast embedded JS templating engine that works i
- Great error reporting
- ⚡️ Exports ES Modules as well as UMD
- 📝 Easy template syntax
-
+- Namespaces for easy tempalte include
## Get Started
_For more thorough documentation, visit [https://eta.js.org](https://eta.js.org)_
@@ -84,8 +84,33 @@ const eta = new Eta({ views: path.join(__dirname, "templates") });
const res = eta.render("./simple", { name: "Ben" });
console.log(res); // Hi Ben!
+
+
+
```
+Using namespaces
+```ts
+ import { Eta } from "eta";
+ const eta = new Eta({ namespaces: {
+ '@myroot' : path.join(__dirname, "templates"),
+ '@includes' : path.join(__dirname, "templates",'includes'),
+ '@components' : path.join(__dirname, "templates",'components')
+ } });
+
+ const res = eta.render("@myroot/sample", { name: "Ben" });
+ console.log(res); // Hi Ben!
+```
+Namespace usage within template
+
+```html
+<% include('@includes/header') %>
+
+
Welcome to the Index Page
+
Here's a widget:
+ <% include('@components/widget') %>
+
+```
## FAQs
diff --git a/src/config.ts b/src/config.ts
index bce8d48..44889ee 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -78,6 +78,29 @@ export interface EtaConfig {
/** Control template file extension defaults. Default `.eta` */
defaultExtension?: string;
+
+
+ /**
+ * Give namespaces to view paths with `@prefix`.
+ *
+ * Example:
+ * ```typescript
+ * const eta = new Eta({
+ * namespaces: {
+ * '@includes': path.join(__dirname, 'src', 'views', 'includes')
+ * }
+ * });
+ *
+ * // Usage in code:
+ * eta.render('@includes/header', { title: 'Home' });
+ * ```
+ *
+ * Within a template, you can use the namespace like this:
+ * ```html
+ * <% include('@includes/header') %>
+ * ```
+ */
+ namespaces?: { [key: string]: string };
}
/* END TYPES */
diff --git a/src/file-handling.ts b/src/file-handling.ts
index 4615bd2..af7f1b4 100644
--- a/src/file-handling.ts
+++ b/src/file-handling.ts
@@ -11,6 +11,9 @@ import type { Options } from "./config.ts";
export function readFile(this: EtaCore, path: string): string {
let res = "";
+ if(path.startsWith('@') && this.config.namespaces){
+ path = resolveNamespace.call(this,path) ?? path;
+ }
try {
res = fs.readFileSync(path, "utf8");
@@ -26,23 +29,58 @@ export function readFile(this: EtaCore, path: string): string {
return res;
}
+/**
+ * Funtion resolves namespace path provided on namespaces in config
+ */
+function resolveNamespace(this: EtaCore,templatePath : string) : string | null{
+ const defaultExtension = this.config.defaultExtension === undefined
+ ? ".eta"
+ : this.config.defaultExtension;
+ if(this.config.namespaces){
+ const entries = Object.keys(this.config.namespaces);
+ const namespace = entries.find(namespaceKey => {
+ return templatePath.startsWith(namespaceKey);
+ });
+ if(namespace){
+ templatePath = templatePath.replace(namespace,this.config.namespaces[namespace]);
+ templatePath += path.extname(templatePath) ? "" : defaultExtension;
+ return templatePath;
+ }
+ }
+ return null;
+}
+
+
+
+
+
export function resolvePath(
this: EtaCore,
templatePath: string,
options?: Partial,
): string {
let resolvedFilePath = "";
+ const defaultExtension = this.config.defaultExtension === undefined
+ ? ".eta"
+ : this.config.defaultExtension;
+ const baseFilePath = options && options.filepath;
+
+
+ // Check if tempalte path has a namespace constant at the begining
+ if(templatePath.startsWith('@') && this.config.namespaces){
+ resolvedFilePath = resolveNamespace.call(this,templatePath) ?? "";
+ return resolvedFilePath;
+ }
+
const views = this.config.views;
+
if (!views) {
throw new EtaFileResolutionError("Views directory is not defined");
}
- const baseFilePath = options && options.filepath;
- const defaultExtension = this.config.defaultExtension === undefined
- ? ".eta"
- : this.config.defaultExtension;
+
// how we index cached template paths
const cacheIndex = JSON.stringify({
diff --git a/src/render.ts b/src/render.ts
index b67354b..b480967 100644
--- a/src/render.ts
+++ b/src/render.ts
@@ -15,7 +15,7 @@ function handleCache(
? this.templatesAsync
: this.templatesSync;
- if (this.resolvePath && this.readFile && !template.startsWith("@")) {
+ if (this.resolvePath && this.readFile) {
const templatePath = options.filepath as string;
const cachedTemplate = templateStore.get(templatePath);
@@ -25,6 +25,7 @@ function handleCache(
} else {
const templateString = this.readFile(templatePath);
+
const templateFn = this.compile(templateString, options);
if (this.config.cache) templateStore.define(templatePath, templateFn);
@@ -54,7 +55,7 @@ export function render(
const options = { ...meta, async: false };
if (typeof template === "string") {
- if (this.resolvePath && this.readFile && !template.startsWith("@")) {
+ if (this.resolvePath && this.readFile) {
options.filepath = this.resolvePath(template, options);
}
@@ -78,7 +79,7 @@ export function renderAsync(
const options = { ...meta, async: true };
if (typeof template === "string") {
- if (this.resolvePath && this.readFile && !template.startsWith("@")) {
+ if (this.resolvePath && this.readFile) {
options.filepath = this.resolvePath(template, options);
}