Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>",
Expand Down
12 changes: 12 additions & 0 deletions src/types/TagContract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @athenna/view
*
* (c) João Lenon <[email protected]>
*
* 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<EdgeTagContract, 'tagName'>
10 changes: 10 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @athenna/view
*
* (c) João Lenon <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

export * from '#src/types/TagContract'
54 changes: 52 additions & 2 deletions src/views/ViewImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', {}))
}

Expand Down Expand Up @@ -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.
Expand Down
66 changes: 66 additions & 0 deletions tests/unit/views/ViewImplTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'))
Expand Down
Loading