diff --git a/.editorconfig b/.editorconfig
index 0294b29..d8fb4dc 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -21,5 +21,5 @@ indent_style = tab
[*.md]
trim_trailing_whitespace = false
-[fixtures/**/*]
+[{fixtures,examples}/**/*]
insert_final_newline = ignore
diff --git a/examples/index.js b/examples/index.js
index 8f24849..865ec93 100644
--- a/examples/index.js
+++ b/examples/index.js
@@ -3,5 +3,5 @@
const edge = require('..')
const { join } = require('path')
-edge.mount('default', join(__dirname, './views'))
+edge.mount(join(__dirname, './views'))
console.log(edge.render('user', { title: 'Hello' }))
diff --git a/examples/views/alert.edge b/examples/views/alert.edge
index 756e6c8..29020e1 100644
--- a/examples/views/alert.edge
+++ b/examples/views/alert.edge
@@ -1 +1 @@
-
{{ title }}
+ {{ title }}
\ No newline at end of file
diff --git a/examples/views/user.edge b/examples/views/user.edge
index 1181f29..732771f 100644
--- a/examples/views/user.edge
+++ b/examples/views/user.edge
@@ -1,2 +1,2 @@
@!component('alert', title = 'Hey')
-@!component('alert', title = 'Wow')
+@!component('alert', title = 'Wow')
\ No newline at end of file
diff --git a/index.js b/index.js
index 7bcd5ec..3a21911 100644
--- a/index.js
+++ b/index.js
@@ -9,6 +9,6 @@
*/
const { Edge } = require('./build/src/Edge')
-const view = new Edge()
-module.exports = view
+module.exports = Edge
+module.exports.utils = require('./build/src/utils')
diff --git a/japaFile.js b/japaFile.js
index c28bb83..aa43bb7 100644
--- a/japaFile.js
+++ b/japaFile.js
@@ -6,9 +6,8 @@ const os = require('os')
Assertion.use((chai, utils) => {
chai.assert.stringEqual = function (val, exp, msg) {
- console.log(val)
new chai.Assertion(val.split(/\r\n|\n/), msg).to.deep.equal(exp.split(/\r\n|\n/))
}
})
-cli.run('test/*.spec.ts')
+cli.run('test/edge.spec.ts')
diff --git a/package-lock.json b/package-lock.json
index 4f7b0d1..28423ee 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -465,6 +465,19 @@
"os-homedir": "^1.0.1"
}
},
+ "caller-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
+ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+ "requires": {
+ "callsites": "^0.2.0"
+ }
+ },
+ "callsites": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
+ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo="
+ },
"camelcase": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
@@ -5833,6 +5846,15 @@
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
"dev": true
},
+ "require-uncached": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
+ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
+ "requires": {
+ "caller-path": "^0.1.0",
+ "resolve-from": "^1.0.0"
+ }
+ },
"requireg": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/requireg/-/requireg-0.1.8.tgz",
@@ -5863,6 +5885,11 @@
"global-modules": "^0.2.3"
}
},
+ "resolve-from": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
+ "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY="
+ },
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
diff --git a/package.json b/package.json
index 9b34ffd..5f13f9b 100644
--- a/package.json
+++ b/package.json
@@ -66,6 +66,7 @@
"edge-parser": "^1.0.9",
"he": "^1.1.1",
"macroable": "^1.0.0",
- "node-exceptions": "^3.0.0"
+ "node-exceptions": "^3.0.0",
+ "require-uncached": "^1.0.3"
}
}
diff --git a/src/Compiler/index.ts b/src/Compiler/index.ts
index 112648e..05ad936 100644
--- a/src/Compiler/index.ts
+++ b/src/Compiler/index.ts
@@ -7,44 +7,63 @@
* file that was distributed with this source code.
*/
-import { ILoader } from '../Contracts'
import { Parser } from 'edge-parser'
-import { ITag } from 'edge-parser/build/src/Contracts'
+import { ILoader, IPresenterConstructor, ICompiler, Tags } from '../Contracts'
-export class Compiler {
- private cache: Map = new Map()
+export class Compiler implements ICompiler {
+ private cacheStore: Map = new Map()
- constructor (private loader: ILoader, private tags: { [key: string]: ITag }, private shouldCache: boolean = true) {
+ constructor (private loader: ILoader, private tags: Tags, public cache: boolean = true) {
}
/**
- * Compiles a given template by loading it using the loader
+ * Returns the template and the presenter class from the
+ * cache. If caching is disabled, then it will
+ * return undefined
*/
- private _compile (templatePath: string, inline: boolean = false): string {
- const template = this.loader.resolve(templatePath)
- const parser = new Parser(this.tags)
- return parser.parseTemplate(template, !inline)
+ private _getFromCache (templatePath: string): undefined | { template: string, Presenter?: IPresenterConstructor } {
+ if (!this.cache) {
+ return
+ }
+
+ return this.cacheStore.get(templatePath)
+ }
+
+ /**
+ * Set's the template path and the payload to the cache. If
+ * cache is disable, then it will never be set.
+ */
+ private _setInCache (templatePath: string, payload: { template: string, Presenter?: IPresenterConstructor }) {
+ if (!this.cache) {
+ return
+ }
+
+ this.cacheStore.set(templatePath, payload)
}
/**
* Compiles a given template by loading it using the loader, also caches
* the template and returns from the cache (if exists).
+ *
+ * When the `inline` property is set to true, the Presenter resolution
+ * will not happen, since Presenters are tied to the top level
+ * views and not partials.
*/
- public compile (templatePath: string, inline: boolean = false): string {
+ public compile (templatePath: string, inline: boolean): { template: string, Presenter?: IPresenterConstructor } {
templatePath = this.loader.makePath(templatePath)
- /**
- * Compile right away when cache is disabled
- */
- if (!this.shouldCache) {
- return this._compile(templatePath, inline)
+ const cachedResponse = this._getFromCache(templatePath)
+ if (cachedResponse) {
+ return cachedResponse
}
- /* istanbul ignore else */
- if (!this.cache.get(templatePath)) {
- this.cache.set(templatePath, this._compile(templatePath, inline))
+ const { template, Presenter } = this.loader.resolve(templatePath, !inline)
+ const payload = {
+ template: new Parser(this.tags).parseTemplate(template, !inline),
+ Presenter,
}
- return this.cache.get(templatePath)!
+ this._setInCache(templatePath, payload)
+ return payload
}
}
diff --git a/src/Contracts/index.ts b/src/Contracts/index.ts
index 06e3e20..69aa959 100644
--- a/src/Contracts/index.ts
+++ b/src/Contracts/index.ts
@@ -1,11 +1,30 @@
+import { ITag as BaseTag } from 'edge-parser/build/src/Contracts'
+
+export interface ILoaderConstructor {
+ new (): ILoader
+}
+
export interface ILoader {
mounted: object
mount (diskName: string, dirPath: string): void
unmount (diskName: string): void
- resolve (templatePath: string): { template: string, Presenter?: IPresenterConstructor }
+ resolve (templatePath: string, withResolver: boolean): { template: string, Presenter?: IPresenterConstructor }
makePath (templatePath: string): string
}
+export interface ICompiler {
+ cache: boolean
+ compile (templatePath: string, inline: boolean): { template: string, Presenter?: IPresenterConstructor }
+}
+
+export interface ITag extends BaseTag {
+ tagName: string
+}
+
+export type Tags = {
+ [key: string]: ITag,
+}
+
export interface IPresenterConstructor {
new (state: any): IPresenter
}
diff --git a/src/Edge/index.ts b/src/Edge/index.ts
index 187e7f0..ebaae35 100644
--- a/src/Edge/index.ts
+++ b/src/Edge/index.ts
@@ -7,32 +7,126 @@
* file that was distributed with this source code.
*/
+import { merge } from 'lodash'
import * as Tags from '../Tags'
import { Compiler } from '../Compiler'
import { Loader } from '../Loader'
+import { ILoaderConstructor, ILoader, ITag } from '../Contracts'
import { Template } from '../Template'
-import { Presenter } from '../Presenter'
+
+let loader: null | ILoader = null
+let compiler: null | Compiler = null
+
+type configOptions = {
+ Loader?: ILoaderConstructor,
+ cache?: boolean,
+}
export class Edge {
- private loader: Loader
- private compiler: Compiler
+ private static globals: any = {}
+ private locals: any = {}
+
+ /**
+ * Returns the instance of loader in use. Make use of
+ * `configure` method to define a custom loader
+ */
+ public static get loader (): ILoader {
+ return loader!
+ }
+
+ /**
+ * Returns the instance of compiler in use.
+ */
+ public static get compiler (): Compiler {
+ return compiler!
+ }
+
+ /**
+ * Configure edge
+ */
+ public static configure (options: configOptions) {
+ loader = new (options.Loader || Loader)()
+ compiler = new Compiler(loader!, Tags, options.cache || true)
+ }
+
+ public static mount (diskName: string, dirPath: string): void
+ public static mount (dirPath: string): void
+
+ /**
+ * Mount a disk to the loader
+ */
+ public static mount (diskName: string, dirPath?: string): void {
+ if (!this.compiler) {
+ this.configure({})
+ }
+
+ if (!dirPath) {
+ dirPath = diskName
+ diskName = 'default'
+ }
+
+ loader!.mount(diskName, dirPath)
+ }
+
+ /**
+ * Un Mount a disk from the loader
+ */
+ public static unmount (diskName: string): void {
+ loader!.unmount(diskName)
+ }
+
+ /**
+ * Add a new global to the edge globals
+ */
+ public static global (name: string, value: any): void {
+ this.globals[name] = value
+ }
+
+ /**
+ * Add a new tag to the tags list
+ */
+ public static tag (Tag: ITag) {
+ Tags[Tag.tagName] = Tag
+ }
+
+ /**
+ * Shorthand to `new Edge().render()` or `Edge.newUp().render()`
+ */
+ public static render (templatePath: string, state: any): string {
+ return new this().render(templatePath, state)
+ }
- constructor () {
- this.loader = new Loader()
- this.compiler = new Compiler(this.loader, Tags)
+ /**
+ * Returns a new instance of edge. The instance
+ * can be used to define locals.
+ */
+ public static newUp (): Edge {
+ return new this()
}
- public mount (diskName: string, dirPath: string) {
- this.loader.mount(diskName, dirPath)
+ /**
+ * Clears registered globals, loader and
+ * the compiler instance
+ */
+ public static clear () {
+ this.globals = {}
+ loader = null
+ compiler = null
}
- public unmount (diskName: string) {
- this.loader.unmount(diskName)
+ /**
+ * Render template with state
+ */
+ public render (templatePath: string, state: any): string {
+ const template = new Template(compiler!, (this.constructor as typeof Edge).globals, this.locals)
+ return template.render(templatePath, state)
}
- public render (template: string, state: any): string {
- const templateInstance = new Template(this.compiler, {})
- const presenter = new Presenter(state)
- return templateInstance.render(template, presenter)
+ /**
+ * Share locals with the current view context
+ */
+ public share (data: any): this {
+ merge(this.locals, data)
+ return this
}
}
diff --git a/src/Loader/index.ts b/src/Loader/index.ts
index 8887da9..3699515 100644
--- a/src/Loader/index.ts
+++ b/src/Loader/index.ts
@@ -9,6 +9,8 @@
import { join, isAbsolute, extname } from 'path'
import { readFileSync } from 'fs'
+import * as requireUncached from 'require-uncached'
+
import { ILoader, IPresenterConstructor } from '../Contracts'
import { extractDiskAndTemplateName } from '../utils'
@@ -65,11 +67,11 @@ export class Loader implements ILoader {
/**
* Resolves a template from disk and returns it as a string
*/
- public resolve (templatePath: string): { template: string, Presenter?: IPresenterConstructor } {
+ public resolve (templatePath: string, withPresenter: boolean): { template: string, Presenter?: IPresenterConstructor } {
try {
templatePath = isAbsolute(templatePath) ? templatePath : this.makePath(templatePath)
const template = readFileSync(templatePath, 'utf-8')
- const Presenter = this._getPresenterForTemplate(templatePath)
+ const Presenter = withPresenter ? this._getPresenterForTemplate(templatePath) : undefined
return { template, Presenter }
} catch (error) {
if (error.code === 'ENOENT') {
@@ -86,9 +88,11 @@ export class Loader implements ILoader {
*/
private _getPresenterForTemplate (templatePath: string): IPresenterConstructor | undefined {
try {
- return require(templatePath.replace(extname(templatePath), '.presenter.js'))
+ return requireUncached(templatePath.replace(extname(templatePath), '.presenter.js'))
} catch (error) {
- // ignore if presenter missing
+ if (['ENOENT', 'MODULE_NOT_FOUND'].indexOf(error.code) === -1) {
+ throw error
+ }
}
}
}
diff --git a/src/Tags/Component.ts b/src/Tags/Component.ts
index 5dd37ea..ed059a7 100644
--- a/src/Tags/Component.ts
+++ b/src/Tags/Component.ts
@@ -16,6 +16,7 @@ export class ComponentTag {
public static block = true
public static seekable = true
public static selfclosed = true
+ public static tagName = 'component'
/**
* Compiles else block node to Javascript else statement
diff --git a/src/Tags/Each.ts b/src/Tags/Each.ts
index 4c5948d..080b916 100644
--- a/src/Tags/Each.ts
+++ b/src/Tags/Each.ts
@@ -17,6 +17,7 @@ export class EachTag {
public static block = true
public static seekable = true
public static selfclosed = true
+ public static tagName = 'each'
private allowedExpressions = ['BinaryExpression']
diff --git a/src/Tags/Else.ts b/src/Tags/Else.ts
index 15014ab..10ffa1f 100644
--- a/src/Tags/Else.ts
+++ b/src/Tags/Else.ts
@@ -15,6 +15,7 @@ export class ElseTag {
public static block = false
public static seekable = false
public static selfclosed = false
+ public static tagName = 'else'
/**
* Compiles else block node to Javascript else statement
diff --git a/src/Tags/ElseIf.ts b/src/Tags/ElseIf.ts
index 22f0527..6eb69b5 100644
--- a/src/Tags/ElseIf.ts
+++ b/src/Tags/ElseIf.ts
@@ -16,6 +16,7 @@ export class ElseIfTag {
public static block = false
public static seekable = true
public static selfclosed = false
+ public static tagName = 'elseif'
protected bannedExpressions = ['SequenceExpression']
diff --git a/src/Tags/If.ts b/src/Tags/If.ts
index 5c1b9ca..0097656 100644
--- a/src/Tags/If.ts
+++ b/src/Tags/If.ts
@@ -16,6 +16,7 @@ export class IfTag {
public static block = true
public static seekable = true
public static selfclosed = false
+ public static tagName = 'if'
/**
* Expressions which are not allowed by the sequence
diff --git a/src/Tags/Include.ts b/src/Tags/Include.ts
index 7675f11..24d4b08 100644
--- a/src/Tags/Include.ts
+++ b/src/Tags/Include.ts
@@ -16,6 +16,7 @@ export class IncludeTag {
public static block = false
public static seekable = true
public static selfclosed = false
+ public static tagName = 'include'
/**
* Expressions which are not allowed by the sequence
diff --git a/src/Tags/Slot.ts b/src/Tags/Slot.ts
index 49db9bb..320678e 100644
--- a/src/Tags/Slot.ts
+++ b/src/Tags/Slot.ts
@@ -15,6 +15,7 @@ export class SlotTag {
public static block = true
public static seekable = true
public static selfclosed = false
+ public static tagName = 'slot'
/**
* Compiles else block node to Javascript else statement
diff --git a/src/Template/index.ts b/src/Template/index.ts
index 59145ec..3f9e329 100644
--- a/src/Template/index.ts
+++ b/src/Template/index.ts
@@ -11,7 +11,6 @@ import { merge } from 'lodash'
import { Context } from '../Context'
import { Compiler } from '../Compiler'
import { Presenter as BasePresenter } from '../Presenter'
-import { IPresenterConstructor } from '../Contracts'
export class Template {
private sharedState: any
@@ -20,21 +19,32 @@ export class Template {
this.sharedState = merge({}, globals, locals)
}
+ /**
+ * Render the inline template (aka partials)
+ */
public renderInline (templatePath: string): Function {
- return new Function('template', 'ctx', this.compiler.compile(templatePath, true))
+ return new Function('template', 'ctx', this.compiler.compile(templatePath, true).template)
}
+ /**
+ * Renders an inline template, but with isolated state. This is
+ * mainly used by the components
+ */
public renderWithState (template: string, state: object, slots: object): string {
- const compiledTemplate = this.compiler.compile(template)
- const presenter = new BasePresenter(Object.assign(state, { $slots: slots }))
+ const { template: compiledTemplate, Presenter } = this.compiler.compile(template, false)
+ const presenter = new (Presenter || BasePresenter)(Object.assign(state, { $slots: slots }))
const ctx = new Context(presenter, this.sharedState)
return new Function('template', 'ctx', `return ${compiledTemplate}`)(this, ctx)
}
- public render (template: string, state: object, Presenter: IPresenterConstructor = BasePresenter): string {
- const compiledTemplate = this.compiler.compile(template)
- const presenter = new Presenter(state)
+ /**
+ * Renders the template by using the template path and the state. The presenter
+ * is resolved by the loader itself
+ */
+ public render (template: string, state: object): string {
+ const { template: compiledTemplate, Presenter } = this.compiler.compile(template, false)
+ const presenter = new (Presenter || BasePresenter)(state)
const ctx = new Context(presenter, this.sharedState)
return new Function('template', 'ctx', `return ${compiledTemplate}`)(this, ctx)
diff --git a/test/compiler.spec.ts b/test/compiler.spec.ts
index 0344a7b..bb4e45f 100644
--- a/test/compiler.spec.ts
+++ b/test/compiler.spec.ts
@@ -20,6 +20,7 @@ const tags = {
public static block = true
public static seekable = true
public static selfclosed = false
+ public static tagName = 'if'
},
}
const viewsDir = join(__dirname, 'views')
@@ -35,7 +36,7 @@ test.group('Compiler', (group) => {
loader.mount('default', viewsDir)
const compiler = new Compiler(loader, tags)
- assert.equal(compiler.compile('foo'), `(function (template, ctx) {
+ assert.equal(compiler.compile('foo', false).template, `(function (template, ctx) {
let out = ''
out += 'Hello '
out += \`\${ctx.escape(ctx.resolve('username'))}\`
@@ -50,7 +51,18 @@ test.group('Compiler', (group) => {
loader.mount('default', viewsDir)
const compiler = new Compiler(loader, tags)
- assert.equal(compiler.compile('foo'), compiler['cache'].get(join(viewsDir, 'foo.edge')))
+ assert.equal(compiler.compile('foo', false), compiler['cacheStore'].get(join(viewsDir, 'foo.edge')))
+ })
+
+ test('save template and presenter both to the cache', async (assert) => {
+ await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ username }}')
+ await fs.outputFile(join(viewsDir, 'foo.presenter.js'), 'module.exports = class Foo {}')
+ const loader = new Loader()
+ loader.mount('default', viewsDir)
+
+ const compiler = new Compiler(loader, tags)
+ compiler.compile('foo', false)
+ assert.equal(compiler['cacheStore'].get(join(viewsDir, 'foo.edge'))!.Presenter!['name'], 'Foo')
})
test('do not cache when disabled', async (assert) => {
@@ -59,7 +71,31 @@ test.group('Compiler', (group) => {
loader.mount('default', viewsDir)
const compiler = new Compiler(loader, tags, false)
- compiler.compile('foo')
- assert.isUndefined(compiler['cache'].get(join(viewsDir, 'foo.edge')))
+ compiler.compile('foo', false)
+ assert.isUndefined(compiler['cacheStore'].get(join(viewsDir, 'foo.edge')))
+ })
+
+ test('compile template as inline', async (assert) => {
+ await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ username }}')
+ const loader = new Loader()
+ loader.mount('default', viewsDir)
+
+ const compiler = new Compiler(loader, tags)
+ assert.equal(compiler.compile('foo', true).template, `
+ let out = ''
+ out += 'Hello '
+ out += \`\${ctx.escape(ctx.resolve('username'))}\`
+ out += '${EOL === '\n' ? '\\n' : '\\r\\n'}'
+ return out`)
+ })
+
+ test('do not load presenter when inline', async (assert) => {
+ await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ username }}')
+ await fs.outputFile(join(viewsDir, 'foo.presenter.js'), '')
+ const loader = new Loader()
+ loader.mount('default', viewsDir)
+
+ const compiler = new Compiler(loader, tags)
+ assert.isUndefined(compiler.compile('foo', true).Presenter)
})
})
diff --git a/test/edge.spec.ts b/test/edge.spec.ts
new file mode 100644
index 0000000..7cd5a08
--- /dev/null
+++ b/test/edge.spec.ts
@@ -0,0 +1,84 @@
+/*
+* edge
+*
+* (c) Harminder Virk
+*
+* For the full copyright and license information, please view the LICENSE
+* file that was distributed with this source code.
+*/
+
+import * as test from 'japa'
+import * as fs from 'fs-extra'
+
+import { join } from 'path'
+
+import { Edge } from '../src/Edge'
+import { Loader } from '../src/Loader'
+import { Compiler } from '../src/Compiler'
+
+const viewsDir = join(__dirname, 'views')
+
+test.group('Template', (group) => {
+ group.afterEach(async () => {
+ Edge.clear()
+ await fs.remove(viewsDir)
+ })
+
+ test('calling configure should setup the loader and compiler', (assert) => {
+ Edge.configure({})
+ assert.instanceOf(Edge.loader, Loader)
+ assert.instanceOf(Edge.compiler, Compiler)
+ })
+
+ test('mount default disk', (assert) => {
+ Edge.mount(viewsDir)
+ assert.deepEqual(Edge.loader.mounted, { default: viewsDir })
+ })
+
+ test('mount named disk', (assert) => {
+ Edge.mount('foo', viewsDir)
+ assert.deepEqual(Edge.loader.mounted, { foo: viewsDir })
+ })
+
+ test('unmount named disk', (assert) => {
+ Edge.mount('foo', viewsDir)
+ Edge.unmount('foo')
+ assert.deepEqual(Edge.loader.mounted, {})
+ })
+
+ test('register globals', (assert) => {
+ Edge.global('foo', 'bar')
+ assert.deepEqual(Edge['globals'].foo, 'bar')
+ })
+
+ test('add a custom tag to the tags list', (assert) => {
+ class MyTag {
+ public static tagName = 'mytag'
+ public static block = true
+ public static seekable = true
+ public static selfclosed = true
+ }
+ Edge.tag(MyTag)
+
+ Edge.configure({})
+ assert.deepEqual(Edge.compiler['tags'].mytag, MyTag)
+ })
+
+ test('render a view using the static render method', async (assert) => {
+ await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ username }}')
+
+ Edge.mount(viewsDir)
+ assert.equal(Edge.render('foo', { username: 'virk' }).trim(), 'Hello virk')
+ })
+
+ test('pass locals to the view context', async (assert) => {
+ await fs.outputFile(join(viewsDir, 'foo.edge'), `Hello {{ username || 'guest' }}`)
+
+ Edge.mount(viewsDir)
+ const tmpl = Edge.newUp()
+ tmpl.share({ username: 'nikk' })
+
+ assert.equal(tmpl.render('foo', {}).trim(), 'Hello nikk')
+ assert.equal(Edge.render('foo', {}).trim(), 'Hello guest')
+ })
+})
diff --git a/test/fixtures.spec.ts b/test/fixtures.spec.ts
index 1632fd8..e6b446a 100644
--- a/test/fixtures.spec.ts
+++ b/test/fixtures.spec.ts
@@ -40,7 +40,7 @@ test.group('Fixtures', (group) => {
test(dir, (assert) => {
const template = new Template(compiler, {}, {})
- const compiled = compiler.compile(`${dir}/index.edge`)
+ const { template: compiled } = compiler.compile(`${dir}/index.edge`, false)
const expectedCompiled = readFileSync(join(dirBasePath, 'compiled.js'), 'utf-8')
assert.stringEqual(compiled, expectedCompiled)
diff --git a/test/loader.spec.ts b/test/loader.spec.ts
index 147b5b7..431b0af 100644
--- a/test/loader.spec.ts
+++ b/test/loader.spec.ts
@@ -36,7 +36,7 @@ test.group('Loader', (group) => {
test('throw exception when resolve path from undefined location', (assert) => {
const loader = new Loader()
- const fn = () => loader.resolve('foo')
+ const fn = () => loader.resolve('foo', true)
assert.throw(fn, 'Attempting to resolve foo.edge template for unmounted default location')
})
@@ -46,7 +46,7 @@ test.group('Loader', (group) => {
const loader = new Loader()
loader.mount('default', viewsDir)
- const { template } = loader.resolve('foo')
+ const { template } = loader.resolve('foo', false)
assert.equal(template.trim(), 'Hello world')
})
@@ -54,7 +54,7 @@ test.group('Loader', (group) => {
const loader = new Loader()
loader.mount('default', viewsDir)
- const fn = () => loader.resolve('foo')
+ const fn = () => loader.resolve('foo', false)
assert.throw(fn, `Cannot resolve ${join(viewsDir, 'foo.edge')}. Make sure file exists.`)
})
@@ -64,7 +64,7 @@ test.group('Loader', (group) => {
const loader = new Loader()
loader.mount('default', viewsDir)
- const { template } = loader.resolve('foo.edge')
+ const { template } = loader.resolve('foo.edge', false)
assert.equal(template.trim(), 'Hello world')
})
@@ -74,7 +74,7 @@ test.group('Loader', (group) => {
const loader = new Loader()
loader.mount('users', viewsDir)
- const { template } = loader.resolve('users::foo.edge')
+ const { template } = loader.resolve('users::foo.edge', false)
assert.equal(template.trim(), 'Hello world')
})
@@ -118,8 +118,21 @@ test.group('Loader', (group) => {
const loader = new Loader()
loader.mount('users', viewsDir)
- const { template, Presenter } = loader.resolve('users::foo.edge')
+ const { template, Presenter } = loader.resolve('users::foo.edge', true)
assert.equal(template.trim(), 'Hello world')
assert.equal(Presenter!['name'], 'Foo')
})
+
+ test('do not resolve presenter if withPresenter is set to false', async (assert) => {
+ await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello world')
+ await fs.outputFile(join(viewsDir, 'foo.presenter.js'), `module.exports = class Foo {
+ }`)
+
+ const loader = new Loader()
+ loader.mount('users', viewsDir)
+
+ const { template, Presenter } = loader.resolve('users::foo.edge', false)
+ assert.equal(template.trim(), 'Hello world')
+ assert.isUndefined(Presenter)
+ })
})
diff --git a/test/tags.spec.ts b/test/tags.spec.ts
index e479720..33a38b6 100644
--- a/test/tags.spec.ts
+++ b/test/tags.spec.ts
@@ -31,7 +31,7 @@ We are writing a bad if condition
await fs.outputFile(join(viewsDir, 'foo.edge'), templateContent)
try {
- compiler.compile('foo')
+ compiler.compile('foo', true)
} catch (error) {
assert.equal(error.line, 4)
}
@@ -48,7 +48,7 @@ We are writing a bad if condition
await fs.outputFile(join(viewsDir, 'foo.edge'), templateContent)
try {
- compiler.compile('foo')
+ compiler.compile('foo', true)
} catch (error) {
assert.equal(error.line, 4)
assert.equal(error.message, 'E_UNALLOWED_EXPRESSION: SequenceExpression is not allowed for if tag\n> More details: https://err.sh/poppinss/edge-errors/E_UNALLOWED_EXPRESSION')
@@ -65,7 +65,7 @@ We are writing a bad if condition
await fs.outputFile(join(viewsDir, 'foo.edge'), templateContent)
try {
- compiler.compile('foo')
+ compiler.compile('foo', true)
} catch (error) {
assert.equal(error.message, 'Unclosed tag if')
}
@@ -81,7 +81,7 @@ test.group('Include', () => {
await fs.outputFile(join(viewsDir, 'foo.edge'), templateContent)
try {
- compiler.compile('foo')
+ compiler.compile('foo', true)
} catch (error) {
assert.equal(error.line, 2)
}
@@ -94,7 +94,7 @@ test.group('Include', () => {
await fs.outputFile(join(viewsDir, 'foo.edge'), templateContent)
try {
- compiler.compile('foo')
+ compiler.compile('foo', true)
} catch (error) {
assert.equal(error.line, 1)
assert.equal(error.message, 'E_UNALLOWED_EXPRESSION: SequenceExpression is not allowed for if tag\n> More details: https://err.sh/poppinss/edge-errors/E_UNALLOWED_EXPRESSION')
diff --git a/test/template.spec.ts b/test/template.spec.ts
index 01564f8..d37ed48 100644
--- a/test/template.spec.ts
+++ b/test/template.spec.ts
@@ -12,7 +12,6 @@ import * as fs from 'fs-extra'
import { join } from 'path'
-import { Presenter } from '../src/Presenter'
import { Template } from '../src/Template'
import { Compiler } from '../src/Compiler'
import { Loader } from '../src/Loader'
@@ -23,6 +22,7 @@ const tags = {
public static block = true
public static seekable = true
public static selfclosed = false
+ public static tagName = 'if'
},
}
@@ -43,26 +43,33 @@ test.group('Template', (group) => {
test('run template with custom presenter', async (assert) => {
await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ getUsername() }}')
- class MyPresenter extends Presenter {
- public getUsername () {
+ await fs.outputFile(join(viewsDir, 'foo.presenter.js'), `module.exports = class MyPresenter {
+ constructor (state) {
+ this.state = state
+ }
+
+ getUsername () {
return this.state.username.toUpperCase()
}
- }
+ }`)
- const output = new Template(compiler, {}, {}).render('foo', { username: 'virk' }, MyPresenter)
+ const output = new Template(compiler, {}, {}).render('foo', { username: 'virk' })
assert.equal(output.trim(), 'Hello VIRK')
})
test('run template with shared state', async (assert) => {
await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ getUsername() }}')
+ await fs.outputFile(join(viewsDir, 'foo.presenter.js'), `module.exports = class MyPresenter {
+ constructor (state) {
+ this.state = state
+ }
- class MyPresenter extends Presenter {
- public getUsername (ctx) {
+ getUsername (ctx) {
return ctx.resolve('username').toUpperCase()
}
- }
+ }`)
- const output = new Template(compiler, { username: 'virk' }, {}).render('foo', {}, MyPresenter)
+ const output = new Template(compiler, { username: 'virk' }, {}).render('foo', {})
assert.equal(output.trim(), 'Hello VIRK')
})
})