Skip to content

Commit

Permalink
feat: add supercharged plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Harminder Virk authored and Harminder Virk committed Aug 13, 2023
1 parent 3b25406 commit 936b3f0
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 7 deletions.
12 changes: 11 additions & 1 deletion src/edge/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Loader } from '../loader.js'
import * as Tags from '../tags/main.js'
import { Compiler } from '../compiler.js'
import { Template } from '../template.js'
import { edgeGlobals } from './globals.js'
import { Processor } from '../processor.js'
import { EdgeRenderer } from './renderer.js'
import type {
Expand All @@ -20,6 +21,7 @@ import type {
LoaderTemplate,
LoaderContract,
} from '../types.js'
import { pluginSuperCharged } from '../plugins/supercharged.js'

/**
* Exposes the API to render templates, register custom tags and globals
Expand Down Expand Up @@ -77,7 +79,7 @@ export class Edge {
/**
* Globals are shared with all rendered templates
*/
globals: { [key: string]: any } = {}
globals: { [key: string]: any } = edgeGlobals

/**
* List of registered tags. Adding new tags will only impact
Expand All @@ -98,9 +100,17 @@ export class Edge {
async: true,
})

/**
* Registering bundled set of tags
*/
Object.keys(Tags).forEach((name) => {
this.registerTag(Tags[name as keyof typeof Tags])
})

/**
* Registering super charged plugin
*/
this.use(pluginSuperCharged, { recurring: !options.cache })
}

/**
Expand Down
17 changes: 11 additions & 6 deletions src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import { fileURLToPath } from 'node:url'
import string from '@poppinss/utils/string'
import { join, isAbsolute } from 'node:path'
import { readFileSync, readdirSync } from 'node:fs'
import type { LoaderContract, LoaderTemplate } from './types.js'
import { existsSync, readFileSync, readdirSync } from 'node:fs'
import type { ComponentsTree, LoaderContract, LoaderTemplate } from './types.js'

/**
* The job of a loader is to load the template from a given path.
Expand Down Expand Up @@ -50,16 +50,21 @@ export class Loader implements LoaderContract {
/**
* Returns a list of components for a given disk
*/
#getDiskComponents(diskName: string): { componentName: string; tagName: string }[] {
#getDiskComponents(diskName: string): ComponentsTree[0]['components'] {
const componentsDirName = 'components'
const diskBasePath = this.#mountedDirs.get(diskName)!
const files = readdirSync(join(diskBasePath, 'components'), {
if (!existsSync(join(diskBasePath, componentsDirName))) {
return []
}

const files = readdirSync(join(diskBasePath, componentsDirName), {
recursive: true,
encoding: 'utf8',
}).filter((file) => file.endsWith('.edge'))

return files.map((file) => {
const fileName = file.replace(/\.edge$/, '')
const componentPath = `components/${fileName}`
const componentPath = `${componentsDirName}/${fileName}`
const tagName = fileName
.split('/')
.filter((segment, index) => {
Expand Down Expand Up @@ -299,7 +304,7 @@ export class Loader implements LoaderContract {
* The return path is same the path you will pass to the `@component`
* tag.
*/
listComponents(): { diskName: string; components: string[] }[] {
listComponents(): ComponentsTree {
const diskNames = [...this.#mountedDirs.keys()]
return diskNames.map((diskName) => {
return {
Expand Down
94 changes: 94 additions & 0 deletions src/plugins/supercharged.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* edge.js
*
* (c) EdgeJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { Edge } from '../edge/main.js'
import { PluginFn } from '../types.js'

/**
* Hooks into the compiler phase of Edge and converts
* tags to components.
*
* The components are discovered from the components directory
* inside every registered disk.
*/
class SuperChargedComponents {
#edge: Edge
#components: Record<string, string> = {}

constructor(edge: Edge) {
this.#edge = edge
this.#claimTags()
this.#transformTags()
}

/**
* Refreshes the list of components
*/
refreshComponents() {
this.#components = this.#edge.loader
.listComponents()
.reduce<Record<string, string>>((result, { components }) => {
components.forEach((component) => {
result[component.tagName] = component.componentName
})
return result
}, {})
}

/**
* Registers hook to claim self processing of tags that
* are references to components
*/
#claimTags() {
this.#edge.compiler.claimTag((name) => {
if (this.#components[name]) {
return { seekable: true, block: true }
}
return null
})

this.#edge.asyncCompiler.claimTag((name) => {
if (this.#components[name]) {
return { seekable: true, block: true }
}
return null
})
}

/**
* Transforms tags to component calls
*/
#transformTags() {
this.#edge.processor.process('tag', ({ tag }) => {
const component = this.#components[tag.properties.name]
if (!component) {
return
}

tag.properties.name = 'component'
if (tag.properties.jsArg.trim() === '') {
tag.properties.jsArg = `'${component}'`
} else {
tag.properties.jsArg = `'${component}',${tag.properties.jsArg}`
}
})
}
}

/**
* The superCharged plugin converts components stored within the
* components directory of all the disk to Edge tags.
*/
let superCharged: SuperChargedComponents
export const pluginSuperCharged: PluginFn<{ recurring: boolean }> = (edge, firstRun) => {
if (firstRun) {
superCharged = new SuperChargedComponents(edge)
}
superCharged.refreshComponents()
}
13 changes: 13 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ export type LoaderTemplate = {
template: string
}

export type ComponentsTree = {
diskName: string
components: {
componentName: string
tagName: string
}[]
}[]

/**
* Loader contract that every loader must adheres to.
*/
Expand Down Expand Up @@ -64,6 +72,11 @@ export interface LoaderContract {
* Remove the pre-registered template
*/
remove(templatePath: string): void

/**
* Returns a list of components for all the registered disks
*/
listComponents(): ComponentsTree
}

/**
Expand Down
36 changes: 36 additions & 0 deletions tests/edge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,42 @@ test.group('Edge', () => {
const output = await edge.render('hello::foo')
assert.equal(output.trim(), 'Hello bar')
})

test('render components as tags', async ({ assert, fs }) => {
const edge = new Edge()
await fs.create('foo.edge', '@!foo({ username })')
await fs.create('components/foo.edge', 'Hello {{ username }}')

edge.mount(fs.basePath)
const output = await edge.render('foo', { username: 'virk' })
assert.equal(output.trim(), 'Hello virk')
})

test('refresh components list on each render', async ({ assert, fs }) => {
const edge = new Edge()
await fs.create('foo.edge', '@!foo({ username })')
edge.mount(fs.basePath)

const output = await edge.render('foo', { username: 'virk' })
assert.equal(output.trim(), '@!foo({ username })')

await fs.create('components/foo.edge', 'Hello {{ username }}')
const output1 = await edge.render('foo', { username: 'virk' })
assert.equal(output1.trim(), 'Hello virk')
})

test('do not refresh list when cache mode is enabled', async ({ assert, fs }) => {
const edge = new Edge({ cache: true })
await fs.create('foo.edge', '@!foo({ username })')
edge.mount(fs.basePath)

const output = await edge.render('foo', { username: 'virk' })
assert.equal(output.trim(), '@!foo({ username })')

await fs.create('components/foo.edge', 'Hello {{ username }}')
const output1 = await edge.render('foo', { username: 'virk' })
assert.equal(output1.trim(), '@!foo({ username })')
})
})

test.group('Edge | regression', () => {
Expand Down

0 comments on commit 936b3f0

Please sign in to comment.