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); }