From 2d078545ef3ebf6c1fde9cb7fd1ebe911248b12d Mon Sep 17 00:00:00 2001 From: jlenon7 Date: Thu, 23 Jan 2025 10:44:45 -0300 Subject: [PATCH] chore(npm): update dependencies --- package.json | 2 +- src/types/TagContract.ts | 12 ++++++ src/types/index.ts | 10 +++++ src/views/ViewImpl.ts | 54 +++++++++++++++++++++++++- tests/unit/views/ViewImplTest.ts | 66 ++++++++++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 src/types/TagContract.ts create mode 100644 src/types/index.ts diff --git a/package.json b/package.json index 91860cf..2fea29f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@athenna/view", - "version": "5.2.0", + "version": "5.3.0", "description": "The Athenna template engine. Built on top of Edge.js.", "license": "MIT", "author": "João Lenon ", diff --git a/src/types/TagContract.ts b/src/types/TagContract.ts new file mode 100644 index 0000000..321905f --- /dev/null +++ b/src/types/TagContract.ts @@ -0,0 +1,12 @@ +/** + * @athenna/view + * + * (c) João Lenon + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import type { TagContract as EdgeTagContract } from 'edge.js/types' + +export type TagContract = Omit diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..8d41dc3 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,10 @@ +/** + * @athenna/view + * + * (c) João Lenon + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +export * from '#src/types/TagContract' diff --git a/src/views/ViewImpl.ts b/src/views/ViewImpl.ts index c36d37d..7b1f91a 100644 --- a/src/views/ViewImpl.ts +++ b/src/views/ViewImpl.ts @@ -11,18 +11,20 @@ import { Edge } from 'edge.js' import { debug } from '#src/debug' import { Config } from '@athenna/config' import { resolve, isAbsolute } from 'node:path' -import { Is, File, Path } from '@athenna/common' +import type { TagContract } from '#src/types/TagContract' +import { Is, File, Path, Macroable } from '@athenna/common' import { EmptyComponentException } from '#src/exceptions/EmptyComponentException' import { NotFoundComponentException } from '#src/exceptions/NotFoundComponentException' import { AlreadyExistComponentException } from '#src/exceptions/AlreadyExistComponentException' -export class ViewImpl { +export class ViewImpl extends Macroable { /** * Edge instance that is handling all the views. */ public edge: Edge public constructor() { + super() this.edge = Edge.create(Config.get('view.edge', {})) } @@ -152,6 +154,54 @@ export class ViewImpl { return this } + /** + * Add a new tag to templates. Just like @component + * @if, etc. + * + * @example + * ```ts + * import type { TagContract } from '@athenna/view' + * + * const reverseTagOptions: TagContract = { + * block: false, + * seekable: true, + * compile(parser, buffer, token) { + * buffer.outputRaw('Hello from reverse tag') + * } + * } + * + * View.addTag('reverse', reverseTagOptions) + * + * const output = await View.renderRaw('@reverse()') // 'Hello from reverse tag' + * ``` + */ + public addTag(name: string, options: TagContract) { + this.edge.registerTag({ tagName: name, ...options }) + + return this + } + + /** + * Remove some tag from views registered using + * "addTag" method. + * + * @example + * ```ts + * View + * .addTag('reverse', { ... }) + * .removeTag('reverse') + * ``` + */ + public removeTag(name: string) { + if (!this.edge.tags[name]) { + return this + } + + delete this.edge.tags[name] + + return this + } + /** * Create a new view disk. View disks can be used * to register multiple views at the same time. diff --git a/tests/unit/views/ViewImplTest.ts b/tests/unit/views/ViewImplTest.ts index 83a69b6..3892ec7 100644 --- a/tests/unit/views/ViewImplTest.ts +++ b/tests/unit/views/ViewImplTest.ts @@ -194,6 +194,72 @@ export default class ViewImplTest { } } + @Test() + public async shouldBeAbleToAddTagsToBeUsedByAllViews({ assert }: Context) { + View.addTag('reverse', { + block: false, + seekable: true, + compile(_, buffer) { + buffer.outputRaw('Hello from reverse tag') + } + }) + + const content = await View.renderRaw('@reverse()') + + assert.deepEqual(content, 'Hello from reverse tag') + } + + @Test() + public async shouldBeAbleToRemoveGlobalTags({ assert }: Context) { + View.addTag('reverse', { + block: false, + seekable: true, + compile(_, buffer) { + buffer.outputRaw('Hello from reverse tag') + } + }) + + { + const content = await View.renderRaw('@reverse()') + + assert.deepEqual(content, 'Hello from reverse tag') + } + + View.removeTag('reverse') + + { + const content = await View.renderRaw('@reverse()') + + assert.deepEqual(content, '@reverse()') + } + } + + @Test() + public async shouldBeAbleToRemoveGlobalTagsThatDoesNotExist({ assert }: Context) { + View.addTag('reverse', { + block: false, + seekable: true, + compile(_, buffer) { + buffer.outputRaw('Hello from reverse tag') + } + }) + + { + const content = await View.renderRaw('@reverse()') + + assert.deepEqual(content, 'Hello from reverse tag') + } + + View.removeTag('reverse') + View.removeTag('not-found') + + { + const content = await View.renderRaw('@reverse()') + + assert.deepEqual(content, '@reverse()') + } + } + @Test() public async shouldBeAbleToCreateViewDisks({ assert }: Context) { View.createViewDisk('test', Path.fixtures('views/admin'))