From 6c0c4055120b62fcc5d60f6439e0f59e9e81f7eb Mon Sep 17 00:00:00 2001 From: Harminder virk Date: Sun, 21 Jul 2019 23:39:03 +0530 Subject: [PATCH] refactor: cleanup and upgrade to new project style --- package-lock.json | 39 +++++++++++- package.json | 15 ++++- src/CacheManager/index.ts | 49 ++++++++++++++ src/Compiler/index.ts | 99 +++++++++++++--------------- src/Context/index.ts | 10 ++- src/Contracts/index.ts | 47 +++++--------- src/Edge/index.ts | 22 ++----- src/Loader/index.ts | 131 +++++++++++++++++++++++--------------- src/Presenter/index.ts | 6 +- src/Tags/Component.ts | 10 +-- src/Tags/Debugger.ts | 5 +- src/Tags/Each.ts | 8 +-- src/Tags/Else.ts | 5 +- src/Tags/ElseIf.ts | 7 +- src/Tags/If.ts | 7 +- src/Tags/Include.ts | 7 +- src/Tags/Layout.ts | 2 +- src/Tags/Section.ts | 7 +- src/Tags/Set.ts | 7 +- src/Tags/Slot.ts | 7 +- src/Tags/Super.ts | 2 +- src/Tags/Unless.ts | 7 +- src/Tags/Yield.ts | 7 +- src/Tags/index.ts | 4 ++ src/Template/index.ts | 30 +++++---- src/utils/index.ts | 83 ++++++++++++------------ test/compiler.spec.ts | 57 ++++++++++------- test/edge.spec.ts | 2 +- test/loader.spec.ts | 62 +++++++++--------- test/tags.spec.ts | 12 ++-- test/template.spec.ts | 30 +++------ 31 files changed, 432 insertions(+), 354 deletions(-) create mode 100644 src/CacheManager/index.ts diff --git a/package-lock.json b/package-lock.json index 905ad1d..3fda235 100644 --- a/package-lock.json +++ b/package-lock.json @@ -134,6 +134,24 @@ "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", "dev": true }, + "@poppinss/dev-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@poppinss/dev-utils/-/dev-utils-1.0.0.tgz", + "integrity": "sha512-Pgx3mtjKOJokOZKhs9vDvkEfAmJ/0jqxnZSdhjjnV6FWb/wIVrbuPN2Ag42DgGqd7fYb4LmKIOErI0jFaVpmPw==", + "dev": true, + "requires": { + "clear-module": "^3.2.0", + "fs-extra": "^8.0.1" + } + }, + "@poppinss/utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@poppinss/utils/-/utils-1.0.3.tgz", + "integrity": "sha512-NSTyS9r8QLDzxdC7fWfw/S8uc1pLQ3O+TF1GrKzu2SxUDYZE8s2YK+k1BNDbfVCVeCijNj8RXv3NgsmeWQrIlw==", + "requires": { + "require-all": "^3.0.0" + } + }, "@textlint/ast-node-types": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-4.2.4.tgz", @@ -751,6 +769,16 @@ } } }, + "clear-module": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-3.2.0.tgz", + "integrity": "sha512-5uO4iiezdJX/jozvfsXnMgWh/hTgOpXMeVqLfYe9RVUIajH/bU7xF9yvDVTq9JGUrBNefitISqd7rJ8jENgj/g==", + "dev": true, + "requires": { + "parent-module": "^1.0.1", + "resolve-from": "^4.0.0" + } + }, "cli-boxes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", @@ -1261,9 +1289,9 @@ } }, "edge-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/edge-parser/-/edge-parser-2.0.5.tgz", - "integrity": "sha512-HPueC4BY2hxgSpoPQXq63qULOfRm7oMy2UC3XwqpITSnnlt4GgDfv4/VLSIFmbLuO0nJ0ezjW7/a4iQFSm5jLQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/edge-parser/-/edge-parser-2.0.6.tgz", + "integrity": "sha512-3MxGrsF9Ucm+vFIRuYC43xT5WXXfpMHbuRqxalIqVNeIIJfqslndBitO0u67G3U0tLT3zq53awjrqOQOW3xVJQ==", "requires": { "acorn": "^6.2.1", "astring": "^1.4.1", @@ -3767,6 +3795,11 @@ "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", "dev": true }, + "require-all": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/require-all/-/require-all-3.0.0.tgz", + "integrity": "sha1-Rz1JcEvjEBFc4ST3c4Ox69hnExI=" + }, "requireg": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.1.8.tgz", diff --git a/package.json b/package.json index bfe3d45..7bda3bd 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "homepage": "https://github.com/poppinss/edge#readme", "devDependencies": { "@adonisjs/mrm-preset": "^2.0.3", + "@poppinss/dev-utils": "^1.0.0", "@types/node": "^12.6.8", "commitizen": "^4.0.3", "cz-conventional-changelog": "^3.0.2", @@ -61,9 +62,10 @@ ] }, "dependencies": { + "@poppinss/utils": "^1.0.3", "debug": "^4.1.1", "edge-error": "^1.0.2", - "edge-parser": "^2.0.5", + "edge-parser": "^2.0.6", "he": "^1.2.0", "import-fresh": "^3.1.0", "lodash": "^4.17.15", @@ -84,5 +86,14 @@ "pre-commit": "doctoc README.md --title='## Table of contents' && git add README.md", "commit-msg": "node ./node_modules/@adonisjs/mrm-preset/validateCommit/conventional/validate.js" } - } + }, + "directories": { + "example": "examples", + "test": "test" + }, + "keywords": [ + "template", + "mustache", + "edge" + ] } diff --git a/src/CacheManager/index.ts b/src/CacheManager/index.ts new file mode 100644 index 0000000..bc54864 --- /dev/null +++ b/src/CacheManager/index.ts @@ -0,0 +1,49 @@ +/** + * @module edge + */ + +/* + * edge + * + * (c) Harminder Virk + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. +*/ + +import { LoaderTemplate } from '../Contracts' + +/** + * In memory cache manager to parsed pre-compiled templates + */ +export class CacheManager { + private _cacheStore: Map = new Map() + + constructor (private _enabled: boolean) { + } + + /** + * Returns the template and the presenter class from the + * cache. If caching is disabled, then it will + * return undefined. + */ + public get (templatePath: string): undefined | LoaderTemplate { + if (!this._enabled) { + return + } + + return this._cacheStore.get(templatePath) + } + + /** + * Set's the template path and the payload to the cache. If + * cache is disabled, then this function returns in noop. + */ + public set (templatePath: string, payload: LoaderTemplate) { + if (!this._enabled) { + return + } + + this._cacheStore.set(templatePath, payload) + } +} diff --git a/src/Compiler/index.ts b/src/Compiler/index.ts index 38bdb44..3378ec4 100644 --- a/src/Compiler/index.ts +++ b/src/Compiler/index.ts @@ -1,5 +1,5 @@ /** - * @module main + * @module edge */ /* @@ -11,13 +11,12 @@ * file that was distributed with this source code. */ +import { Token } from 'edge-lexer' import { Parser } from 'edge-parser' -import { Token } from 'edge-lexer/build' -import { LoaderContract, CompilerContract, Tags, LoaderTemplate } from '../Contracts' -import { mergeSections, isBlock } from '../utils' -import * as Debug from 'debug' -const debug = Debug('edge:loader') +import { LoaderContract, Tags, LoaderTemplate } from '../Contracts' +import { mergeSections, isBlock } from '../utils' +import { CacheManager } from '../CacheManager' /** * Compiler compiles the template to a function, which can be invoked at a later @@ -26,43 +25,17 @@ const debug = Debug('edge:loader') * Compiler uses [edge-parser](https://npm.im/edge-parser) under the hood and also * handles the layouts. * - * When caching is set to `true`, the compiled templates will be cached to improve performance. + * When caching is set to `true`, the compiled templates will be cached in-memory + * to improve performance. */ -export class Compiler implements CompilerContract { - private cacheStore: Map = new Map() +export class Compiler { + private _cacheManager = new CacheManager(this._cache) constructor ( - private loader: LoaderContract, - private tags: Tags, - private cache: boolean = true, - ) { - } - - /** - * Returns the template and the presenter class from the - * cache. If caching is disabled, then it will - * return undefined. - */ - private _getFromCache (templatePath: string): undefined | LoaderTemplate { - if (!this.cache) { - return - } - - return this.cacheStore.get(templatePath) - } - - /** - * Set's the template path and the payload to the cache. If - * cache is disabled, then it will never be set. - */ - private _setInCache (templatePath: string, payload: LoaderTemplate) { - if (!this.cache) { - return - } - - debug('adding to cache %s', templatePath) - this.cacheStore.set(templatePath, payload) - } + private _loader: LoaderContract, + private _tags: Tags, + private _cache: boolean = true, + ) {} /** * Generates an array of lexer tokens from the template string. Further tokens @@ -74,9 +47,11 @@ export class Compiler implements CompilerContract { const firstToken = templateTokens[0] + /** + * The `layout` is inbuilt feature from core, where we merge the layout + * and parent template sections together + */ if (isBlock(firstToken, 'layout')) { - debug('detected layout %s', firstToken.properties.jsArg) - const layoutTokens = this.generateTokens(firstToken.properties.jsArg.replace(/'/g, '')) templateTokens = mergeSections(layoutTokens, templateTokens) } @@ -85,7 +60,7 @@ export class Compiler implements CompilerContract { } /** - * Converts the template content to an [array of lexer tokens](https://github.com/poppinss/edge-lexer#nodes). If + * Converts the template content to an [array of lexer tokens](https://github.com/edge-js/edge-lexer#nodes). If * layouts detected, their sections will be merged together. * * ``` @@ -93,8 +68,9 @@ export class Compiler implements CompilerContract { * ``` */ public generateTokens (templatePath: string): Token[] { - const parser = new Parser(this.tags, { filename: templatePath }) - const { template } = this.loader.resolve(templatePath, false) + const { template } = this._loader.resolve(templatePath, false) + + const parser = new Parser(this._tags, { filename: templatePath }) return this._templateContentToTokens(template, parser) } @@ -102,7 +78,7 @@ export class Compiler implements CompilerContract { * Compiles the template contents to a function string, which can be invoked * later. * - * When `inline` is set to true, the compiled output will not have it's own scope and + * When `inline` is set to true, the compiled output **will not have it's own scope** and * neither an attempt to load the presenter is made. The `inline` is mainly used for partials. * * ```js @@ -120,29 +96,42 @@ export class Compiler implements CompilerContract { * ``` */ public compile (templatePath: string, inline: boolean): LoaderTemplate { - templatePath = this.loader.makePath(templatePath) + const absPath = this._loader.makePath(templatePath) /** * If template is in the cache, then return it without * further processing */ - const cachedResponse = this._getFromCache(templatePath) + const cachedResponse = this._cacheManager.get(absPath) if (cachedResponse) { return cachedResponse } /** - * Get a new instance of the parser + * Do not load presenter in inline mode + */ + const loadPresenter = !inline + + /** + * Inline templates are not wrapped inside a function + * call. They share the parent template scope + */ + const wrapAsFunction = !inline + + /** + * Get a new instance of the parser. We use the `templatePath` as the filename + * instead of the `absPath`, since `templatePath` are relative and readable. */ - const parser = new Parser(this.tags, { filename: templatePath }) + const parser = new Parser(this._tags, { filename: templatePath }) /** - * Resolve the base template from loader + * Resolve the template and Presenter using the given loader */ - const { template, Presenter } = this.loader.resolve(templatePath, !inline) + const { template, Presenter } = this._loader.resolve(absPath, loadPresenter) /** - * Convert template to AST. So that we can merge the layout tokens + * Convert template to AST. The AST will have the layout actions merged (if layout) + * is used. */ const templateTokens = this._templateContentToTokens(template, parser) @@ -150,11 +139,11 @@ export class Compiler implements CompilerContract { * Finally process the ast */ const payload = { - template: parser.processTokens(templateTokens, !inline), + template: parser.processTokens(templateTokens, wrapAsFunction), Presenter, } - this._setInCache(templatePath, payload) + this._cacheManager.set(absPath, payload) return payload } } diff --git a/src/Context/index.ts b/src/Context/index.ts index cceed94..ff457ba 100644 --- a/src/Context/index.ts +++ b/src/Context/index.ts @@ -1,5 +1,5 @@ /** - * @module main + * @module edge */ /* @@ -11,10 +11,9 @@ * file that was distributed with this source code. */ -import { Macroable } from 'macroable' import * as he from 'he' import { set } from 'lodash' -import { PresenterContract } from '../Contracts' +import { Macroable } from 'macroable' /** * Context is used at runtime to resolve values for a given @@ -25,7 +24,6 @@ import { PresenterContract } from '../Contracts' */ export class Context extends Macroable { protected static _macros = {} - protected static _getters = {} /** @@ -36,7 +34,7 @@ export class Context extends Macroable { */ private frames: any[] = [] - constructor (public presenter: PresenterContract, public sharedState: object) { + constructor (public presenter: any, public sharedState: any) { super() } @@ -64,7 +62,7 @@ export class Context extends Macroable { /** * Set key/value pair on the frame object. The value will only be available until - * the `removeFrame` is called. + * the `removeFrame` is not called. * * ```js * ctx.setOnFrame('username', 'virk') diff --git a/src/Contracts/index.ts b/src/Contracts/index.ts index 7990d9d..4c36fef 100644 --- a/src/Contracts/index.ts +++ b/src/Contracts/index.ts @@ -1,5 +1,5 @@ /** - * @module main + * @module edge */ /** @@ -11,14 +11,21 @@ * file that was distributed with this source code. */ -import { Token } from 'edge-lexer' import { ParseTagDefininationContract } from 'edge-parser' +/** + * The shape in which the loader must resolve the template + */ export type LoaderTemplate = { template: string, - Presenter?: PresenterConstructorContract, + Presenter?: { + new (state: any): any, + }, } +/** + * Loader contract that every loader must adheres to. + */ export interface LoaderContract { /** * List of mounted disks @@ -52,38 +59,16 @@ export interface LoaderContract { } /** - * Compiler - */ -export interface CompilerContract { - /** - * Returns an array of edge-lexer tokens - */ - generateTokens (templatePath: string): Token[] - - /** - * Compile template to a function string - */ - compile (templatePath: string, inline: boolean): LoaderTemplate -} - -/** - * Presenter - */ -export interface PresenterConstructorContract { - new (state: any): PresenterContract -} - -export interface PresenterContract { - state: any -} - -/** - * Tags + * The final tag must have a tagName along with other properties + * required by lexer and parser */ export interface TagContract extends ParseTagDefininationContract { tagName: string } +/** + * Shape of required tags + */ export type Tags = { - [key: string]: TagContract, + [tagName: string]: TagContract, } diff --git a/src/Edge/index.ts b/src/Edge/index.ts index f6ea077..c5ea0c5 100644 --- a/src/Edge/index.ts +++ b/src/Edge/index.ts @@ -1,5 +1,5 @@ /** - * @module main + * @module edge */ /* @@ -12,15 +12,13 @@ */ import { merge } from 'lodash' + import * as Tags from '../Tags' -import { Compiler } from '../Compiler' import { Loader } from '../Loader' -import { LoaderContract, TagContract, PresenterConstructorContract } from '../Contracts' -import { Template } from '../Template' import { Context } from '../Context' -import * as Debug from 'debug' - -const debug = Debug('edge') +import { Template } from '../Template' +import { Compiler } from '../Compiler' +import { LoaderContract, TagContract, LoaderTemplate } from '../Contracts' let loader: null | LoaderContract = null let compiler: null | Compiler = null @@ -87,13 +85,10 @@ export class Edge { cache: false, }, options) - debug('configure %o', edgeOptions) - loader = new edgeOptions.Loader!() compiler = new Compiler(loader!, Tags, edgeOptions.cache) Object.keys(Tags).forEach((tag) => { - debug('calling run method on %s', tag) if (typeof (Tags[tag].run) === 'function') { Tags[tag].run(Context) } @@ -155,7 +150,6 @@ export class Edge { * Add a new tag to the tags list. */ public static tag (tag: TagContract) { - debug('defining a new tag %s', tag.tagName) Tags[tag.tagName] = tag } @@ -163,11 +157,7 @@ export class Edge { * Register an in-memory template as a string. Check [loader.register](main.loader.html#register) for * more info. */ - public static register ( - templatePath: string, - contents: { template: string, Presenter?: PresenterConstructorContract }, - ) { - debug('registering dynamic template %s', templatePath) + public static register (templatePath: string, contents: LoaderTemplate) { loader!.register(templatePath, contents) } diff --git a/src/Loader/index.ts b/src/Loader/index.ts index 0cc7984..4ddefe7 100644 --- a/src/Loader/index.ts +++ b/src/Loader/index.ts @@ -1,5 +1,5 @@ /** - * @module main + * @module edge */ /** @@ -11,43 +11,46 @@ * file that was distributed with this source code. */ -import { join, isAbsolute, extname } from 'path' import { readFileSync } from 'fs' +import { Exception } from '@poppinss/utils' import requireUncached = require('import-fresh') +import { join, isAbsolute, extname } from 'path' -import { LoaderContract, PresenterConstructorContract, LoaderTemplate } from '../Contracts' import { extractDiskAndTemplateName } from '../utils' -import * as Debug from 'debug' - -const debug = Debug('edge:loader') +import { LoaderContract, LoaderTemplate } from '../Contracts' /** * The job of a loader is to load the template and it's presenter for a given path. * The base loader (shipped with edge) looks for files on the file-system and * reads them synchronously. * - * You are free to define your own loaders that implements the [[ILoader]] interface + * You are free to define your own loaders that implements the [[LoaderContract]] interface. */ export class Loader implements LoaderContract { - private mountedDirs: Map = new Map() - private preRegistered: Map = new Map() + /** + * List of mounted directories + */ + private _mountedDirs: Map = new Map() + + /** + * List of pre-registered (in-memory) templates + */ + private _preRegistered: Map = new Map() /** * Attempts to load the presenter for a given template. If presenter doesn't exists, it * will swallow the error. * - * Also this method will **bypass the require cache**, since in production compiled templates + * Also this method will **bypass the `require` cache**, since in production compiled templates * and their presenters are cached anyways. */ - private _getPresenterForTemplate (templatePath: string): PresenterConstructorContract | undefined { - try { - const presenterPath = templatePath - .replace(/^\w/, c => c.toUpperCase()) - .replace(extname(templatePath), '.presenter.js') - - debug('loading presenter %s', presenterPath) + private _getPresenterForTemplate (templatePath: string): LoaderTemplate['Presenter'] | undefined { + const presenterPath = templatePath + .replace(/^\w/, c => c.toUpperCase()) + .replace(extname(templatePath), '.presenter.js') - return requireUncached(presenterPath) as PresenterConstructorContract + try { + return requireUncached(presenterPath) as LoaderTemplate['Presenter'] } catch (error) { if (['ENOENT', 'MODULE_NOT_FOUND'].indexOf(error.code) === -1) { throw error @@ -55,6 +58,26 @@ export class Loader implements LoaderContract { } } + /** + * Reads the content of a template from the disk. An exception is raised + * when file is missing or if `readFileSync` returns an error. + */ + private _readTemplateContents (absPath: string): string { + try { + return readFileSync(absPath, 'utf-8') + } catch (error) { + if (error.code === 'ENOENT') { + throw new Exception( + `Cannot resolve ${absPath}. Make sure the file exists`, + 500, + 'E_MISSING_TEMPLATE_FILE', + ) + } else { + throw error + } + } + } + /** * Returns an object of mounted directories with their public * names. @@ -70,7 +93,7 @@ export class Loader implements LoaderContract { * ``` */ public get mounted (): { [key: string]: string } { - return Array.from(this.mountedDirs).reduce((obj, [key, value]) => { + return Array.from(this._mountedDirs).reduce((obj, [key, value]) => { obj[key] = value return obj }, {}) @@ -89,8 +112,7 @@ export class Loader implements LoaderContract { * ``` */ public mount (diskName: string, dirPath: string): void { - debug('mounting dir %s with name %s', dirPath, diskName) - this.mountedDirs.set(diskName, dirPath) + this._mountedDirs.set(diskName, dirPath) } /** @@ -101,8 +123,7 @@ export class Loader implements LoaderContract { * ``` */ public unmount (diskName: string): void { - debug('unmount dir with name %s', diskName) - this.mountedDirs.delete(diskName) + this._mountedDirs.delete(diskName) } /** @@ -118,15 +139,18 @@ export class Loader implements LoaderContract { * @throws Error if disk is not mounted and attempting to make path for it. */ public makePath (templatePath: string): string { - if (this.preRegistered.has(templatePath)) { + if (this._preRegistered.has(templatePath)) { return templatePath } - const [diskName, template] = extractDiskAndTemplateName(templatePath) + const [ diskName, template ] = extractDiskAndTemplateName(templatePath) - const mountedDir = this.mountedDirs.get(diskName) + /** + * Raise exception when disk name is not defined + */ + const mountedDir = this._mountedDirs.get(diskName) if (!mountedDir) { - throw new Error(`Attempting to resolve ${template} template for unmounted ${diskName} location`) + throw new Exception(`{${diskName}} namespace is not mounted`, 500, 'E_UNMOUNTED_DISK_NAME') } return join(mountedDir, template) @@ -138,7 +162,7 @@ export class Loader implements LoaderContract { * as the template. * * ## Presenter convention - * - View name - welcome + * - View name - welcome.edge * - Presenter name - Welcome.presenter.js * * ```js @@ -152,31 +176,22 @@ export class Loader implements LoaderContract { * ``` */ public resolve (templatePath: string, withPresenter: boolean): LoaderTemplate { - debug('attempting to resolve %s', templatePath) - debug('with presenter %s', String(withPresenter)) - /** * Return from pre-registered one's if exists */ - if (this.preRegistered.has(templatePath)) { - const contents = this.preRegistered.get(templatePath) + if (this._preRegistered.has(templatePath)) { + const contents = this._preRegistered.get(templatePath) return withPresenter ? contents! : { template: contents!.template } } - try { - templatePath = isAbsolute(templatePath) ? templatePath : this.makePath(templatePath) - - const template = readFileSync(templatePath, 'utf-8') - const presenter = withPresenter ? this._getPresenterForTemplate(templatePath) : undefined + /** + * Make absolute to the file on the disk + */ + templatePath = isAbsolute(templatePath) ? templatePath : this.makePath(templatePath) - debug('has presenter %s', !!presenter) - return { template, Presenter: presenter } - } catch (error) { - if (error.code === 'ENOENT') { - throw new Error(`Cannot resolve ${templatePath}. Make sure file exists.`) - } else { - throw error - } + return { + template: this._readTemplateContents(templatePath), + Presenter: withPresenter ? this._getPresenterForTemplate(templatePath) : undefined, } } @@ -198,14 +213,28 @@ export class Loader implements LoaderContract { * @throws Error if template content is empty. */ public register (templatePath: string, contents: LoaderTemplate) { - if (!contents.template) { - throw new Error('Make sure to define the template content for preRegistered template') + /** + * Ensure template content is defined as a string + */ + if (typeof (contents.template) !== 'string') { + throw new Exception( + 'Make sure to define the template content as a string', + 500, + 'E_MISSING_TEMPLATE_CONTENTS', + ) } - if (this.preRegistered.has(templatePath)) { - throw new Error(`Cannot override previously registered ${templatePath} template`) + /** + * Do not overwrite existing template with same template path + */ + if (this._preRegistered.has(templatePath)) { + throw new Exception( + `Cannot override previously registered {${templatePath}} template`, + 500, + 'E_DUPLICATE_TEMPLATE_PATH', + ) } - this.preRegistered.set(templatePath, contents) + this._preRegistered.set(templatePath, contents) } } diff --git a/src/Presenter/index.ts b/src/Presenter/index.ts index b69270f..7f1fcb6 100644 --- a/src/Presenter/index.ts +++ b/src/Presenter/index.ts @@ -1,5 +1,5 @@ /** - * @module main + * @module edge */ /* @@ -11,8 +11,6 @@ * file that was distributed with this source code. */ -import { PresenterContract } from '../Contracts' - /** * The Base presenter is passed to context for reading * the state values. @@ -20,7 +18,7 @@ import { PresenterContract } from '../Contracts' * However, a custom presenter a do a lot by defining * custom properties and methods. */ -export class Presenter implements PresenterContract { +export class Presenter { constructor (public state: any) { } } diff --git a/src/Tags/Component.ts b/src/Tags/Component.ts index 1b4a037..47e670b 100644 --- a/src/Tags/Component.ts +++ b/src/Tags/Component.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' import { parseSequenceExpression, ObjectifyString, parseAsKeyValuePair, isBlock } from '../utils' export class ComponentTag { @@ -22,9 +21,6 @@ export class ComponentTag { public static selfclosed = true public static tagName = 'component' - /** - * Compiles else block node to Javascript else statement - */ public static compile (parser: Parser, buffer: EdgeBuffer, token: TagToken) { const parsed = parser.generateAst(token.properties.jsArg, token.loc) const expression = parser.acornToEdgeExpression(parsed.body[0]) diff --git a/src/Tags/Debugger.ts b/src/Tags/Debugger.ts index ce0330a..81da4e8 100644 --- a/src/Tags/Debugger.ts +++ b/src/Tags/Debugger.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,8 +11,7 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' +import { Parser, EdgeBuffer } from 'edge-parser' export class DebuggerTag { public static block = false diff --git a/src/Tags/Each.ts b/src/Tags/Each.ts index 4fca39e..ceeb4bc 100644 --- a/src/Tags/Each.ts +++ b/src/Tags/Each.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,10 +11,10 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' import { each, size as lodashSize } from 'lodash' + import { allowExpressions, isBlock } from '../utils' export class EachTag { diff --git a/src/Tags/Else.ts b/src/Tags/Else.ts index 0435813..8f36903 100644 --- a/src/Tags/Else.ts +++ b/src/Tags/Else.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,8 +11,7 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' +import { Parser, EdgeBuffer } from 'edge-parser' export class ElseTag { public static block = false diff --git a/src/Tags/ElseIf.ts b/src/Tags/ElseIf.ts index 7f4f2ad..8d11ce0 100644 --- a/src/Tags/ElseIf.ts +++ b/src/Tags/ElseIf.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' import { disAllowExpressions } from '../utils' export class ElseIfTag { diff --git a/src/Tags/If.ts b/src/Tags/If.ts index 0f148d2..8f0a07c 100644 --- a/src/Tags/If.ts +++ b/src/Tags/If.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' import { disAllowExpressions } from '../utils' export class IfTag { diff --git a/src/Tags/Include.ts b/src/Tags/Include.ts index 025f4d0..39e0ef6 100644 --- a/src/Tags/Include.ts +++ b/src/Tags/Include.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' import { disAllowExpressions } from '../utils' export class IncludeTag { diff --git a/src/Tags/Layout.ts b/src/Tags/Layout.ts index 737e956..6734b91 100644 --- a/src/Tags/Layout.ts +++ b/src/Tags/Layout.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* diff --git a/src/Tags/Section.ts b/src/Tags/Section.ts index d53a434..23a080d 100644 --- a/src/Tags/Section.ts +++ b/src/Tags/Section.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' export class SectionTag { public static block = true diff --git a/src/Tags/Set.ts b/src/Tags/Set.ts index 8177dfe..91c9b32 100644 --- a/src/Tags/Set.ts +++ b/src/Tags/Set.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' import { parseAsKeyValuePair } from '../utils' export class SetTag { diff --git a/src/Tags/Slot.ts b/src/Tags/Slot.ts index c63d44a..f45a07d 100644 --- a/src/Tags/Slot.ts +++ b/src/Tags/Slot.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' export class SlotTag { public static block = true diff --git a/src/Tags/Super.ts b/src/Tags/Super.ts index 825a4bd..b014443 100644 --- a/src/Tags/Super.ts +++ b/src/Tags/Super.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* diff --git a/src/Tags/Unless.ts b/src/Tags/Unless.ts index 97c6a4c..6729029 100644 --- a/src/Tags/Unless.ts +++ b/src/Tags/Unless.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' import { disAllowExpressions } from '../utils' export class UnlessTag { diff --git a/src/Tags/Yield.ts b/src/Tags/Yield.ts index d33034d..6c08318 100644 --- a/src/Tags/Yield.ts +++ b/src/Tags/Yield.ts @@ -1,5 +1,5 @@ /** - * @module tags + * @module edge */ /* @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' -import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' -import { TagToken } from 'edge-lexer/build/src/Contracts' +import { TagToken } from 'edge-lexer' +import { Parser, EdgeBuffer } from 'edge-parser' import { allowExpressions } from '../utils' export class YieldTag { diff --git a/src/Tags/index.ts b/src/Tags/index.ts index 2bbaf47..b9fe543 100644 --- a/src/Tags/index.ts +++ b/src/Tags/index.ts @@ -1,3 +1,7 @@ +/** + * @module edge + */ + /* * edge * diff --git a/src/Template/index.ts b/src/Template/index.ts index 69f5417..d742552 100644 --- a/src/Template/index.ts +++ b/src/Template/index.ts @@ -1,5 +1,5 @@ /** - * @module main + * @module edge */ /* @@ -22,10 +22,14 @@ import { Presenter as BasePresenter } from '../Presenter' * and `dynamic components`. */ export class Template { - private sharedState: any + /** + * The shared state is used to hold the globals and locals, + * since it is shared with components too. + */ + private _sharedState: any - constructor (private compiler: Compiler, globals: any, locals: any) { - this.sharedState = merge({}, globals, locals) + constructor (private _compiler: Compiler, globals: any, locals: any) { + this._sharedState = merge({}, globals, locals) } /** @@ -39,24 +43,24 @@ export class Template { * ``` */ public renderInline (templatePath: string): Function { - return new Function('template', 'ctx', this.compiler.compile(templatePath, true).template) + return new Function('template', 'ctx', this._compiler.compile(templatePath, true).template) } /** * Renders the template with custom state. The `sharedState` of the template is still * passed to this template. * - * Also a set of custom slots can be passed along. The slots uses the state of the current + * Also a set of custom slots can be passed along. The slots uses the state of the parent * template. * * ```js * template.renderWithState('components.user', { username: 'virk' }, slotsIfAny) * ``` */ - public renderWithState (template: string, state: object, slots: object): string { - 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) + public renderWithState (template: string, state: any, slots: any): string { + const { template: compiledTemplate, Presenter } = this._compiler.compile(template, false) + const presenter = new (Presenter || BasePresenter)(merge(state, { $slots: slots })) + const ctx = new Context(presenter, this._sharedState) return new Function('template', 'ctx', `return ${compiledTemplate}`)(this, ctx) } @@ -68,10 +72,10 @@ export class Template { * template.render('welcome', { key: 'value' }) * ``` */ - public render (template: string, state: object): string { - const { template: compiledTemplate, Presenter } = this.compiler.compile(template, false) + public render (template: string, state: any): string { + const { template: compiledTemplate, Presenter } = this._compiler.compile(template, false) const presenter = new (Presenter || BasePresenter)(state) - const ctx = new Context(presenter, this.sharedState) + const ctx = new Context(presenter, this._sharedState) return new Function('template', 'ctx', `return ${compiledTemplate}`)(this, ctx) } diff --git a/src/utils/index.ts b/src/utils/index.ts index 9c405fa..c8df9d3 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -18,10 +18,10 @@ * file that was distributed with this source code. */ -import { Parser } from 'edge-parser' import { sep } from 'path' +import { Parser } from 'edge-parser' import { EdgeError } from 'edge-error' -import { Token, TagTypes, TagToken } from 'edge-lexer/build/src/Contracts' +import { Token, TagTypes, TagToken } from 'edge-lexer' /** * When passing objects in the template, we cannot pass them as real Javascript @@ -150,9 +150,9 @@ export function parseSequenceExpression (expression: any, parser: Parser): [stri * * 1. Top level expression must be `Literal` or `SequenceExpression`. * 2. If `SequenceExpression`, then first child of expression must be `Literal` - * 3. Length of `SequenceExpression` childs must be 2 at max. + * 3. Length of `SequenceExpression` children must be 2 at max. * - * Optionally, you can enforce (3rd argument) that value in the key/value pair must be one + * Optionally, you can enforce (3rd argument) that value the key/value pair must be one * of the given expressions. * * ```js @@ -163,41 +163,42 @@ export function parseSequenceExpression (expression: any, parser: Parser): [stri * ('foo', { bar: true }) * ``` */ -export function parseAsKeyValuePair (expression: any, parser: Parser, valueExpressions: string[]): [ - string, - null | string -] { - allowExpressions('slot', expression, ['Literal', 'SequenceExpression'], parser.options.filename) - - /** - * Return without counting props, value is a literal - */ - if (expression.type === 'Literal') { - return [expression.raw, null] - } - - /** - * Raise error when more than 2 arguments are passed to the slot - * expression - */ - if (expression.expressions.length > 2) { - throw new EdgeError('Maximum of 2 arguments are allowed for slot tag', 'E_MAX_ARGUMENTS', { - line: expression.loc.start.line, - col: expression.loc.start.column, - filename: parser.options.filename, - }) - } - - allowExpressions('slot', expression.expressions[0], ['Literal'], parser.options.filename) - - if (valueExpressions.length) { - allowExpressions('slot', expression.expressions[1], valueExpressions, parser.options.filename) - } - - /** - * Finally return the name and prop name for the slot - */ - return [expression.expressions[0].raw, parser.statementToString(expression.expressions[1])] +export function parseAsKeyValuePair ( + expression: any, + parser: Parser, + valueExpressions: string[], +): [string, null | string] { + allowExpressions('slot', expression, ['Literal', 'SequenceExpression'], parser.options.filename) + + /** + * Return without counting props, value is a literal + */ + if (expression.type === 'Literal') { + return [expression.raw, null] + } + + /** + * Raise error when more than 2 arguments are passed to the slot + * expression + */ + if (expression.expressions.length > 2) { + throw new EdgeError('Maximum of 2 arguments are allowed for slot tag', 'E_MAX_ARGUMENTS', { + line: expression.loc.start.line, + col: expression.loc.start.column, + filename: parser.options.filename, + }) + } + + allowExpressions('slot', expression.expressions[0], ['Literal'], parser.options.filename) + + if (valueExpressions.length) { + allowExpressions('slot', expression.expressions[1], valueExpressions, parser.options.filename) + } + + /** + * Finally return the name and prop name for the slot + */ + return [expression.expressions[0].raw, parser.statementToString(expression.expressions[1])] } /** @@ -252,11 +253,11 @@ export function hasChildSuper (token: TagToken): boolean { /** * Merges the sections of multiple lexer tokens array together. This is - * mainly used to merge sections of layouts. + * used to merge sections of layouts. */ export function mergeSections (base: Token[], extended: Token[]): Token[] { /** - * Collection all sections from the extended tokens + * Collection of all sections from the extended tokens */ const extendedSections = extended .filter((node) => isBlock(node, 'section')) diff --git a/test/compiler.spec.ts b/test/compiler.spec.ts index 81e3caa..465a02f 100644 --- a/test/compiler.spec.ts +++ b/test/compiler.spec.ts @@ -8,7 +8,7 @@ */ import * as test from 'japa' -import * as fs from 'fs-extra' +import { Filesystem } from '@poppinss/dev-utils' import { join } from 'path' import { Loader } from '../src/Loader' @@ -24,17 +24,18 @@ const tags = { }, } -const viewsDir = join(__dirname, 'views') +const fs = new Filesystem(join(__dirname, 'views')) test.group('Compiler', (group) => { group.afterEach(async () => { - await fs.remove(viewsDir) + await fs.cleanup() }) test('compile template', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ username }}') + await fs.add('foo.edge', 'Hello {{ username }}') + const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const compiler = new Compiler(loader, tags) assert.equal(compiler.compile('foo', false).template, `(function (template, ctx) { @@ -46,39 +47,48 @@ test.group('Compiler', (group) => { }) test('save template to cache', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ username }}') + await fs.add('foo.edge', 'Hello {{ username }}') + const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const compiler = new Compiler(loader, tags) - assert.equal(compiler.compile('foo', false), compiler['cacheStore'].get(join(viewsDir, 'foo.edge'))) + assert.equal( + compiler.compile('foo', false), + compiler['_cacheManager'].get(join(fs.basePath, '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 {}') + await fs.add('foo.edge', 'Hello {{ username }}') + await fs.add('foo.presenter.js', 'module.exports = class Foo {}') + const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const compiler = new Compiler(loader, tags) compiler.compile('foo', false) - assert.equal(compiler['cacheStore'].get(join(viewsDir, 'foo.edge'))!.Presenter!['name'], 'Foo') + assert.equal( + compiler['_cacheManager'].get(join(fs.basePath, 'foo.edge'))!.Presenter!['name'], + 'Foo', + ) }) - test('do not cache when disabled', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ username }}') + test('do not cache template when caching is disabled', async (assert) => { + await fs.add('foo.edge', 'Hello {{ username }}') + const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const compiler = new Compiler(loader, tags, false) compiler.compile('foo', false) - assert.isUndefined(compiler['cacheStore'].get(join(viewsDir, 'foo.edge'))) + assert.isUndefined(compiler['_cacheManager'].get(join(fs.basePath, 'foo.edge'))) }) - test('compile template as inline', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ username }}') + test('do not wrap inline templates to a function', async (assert) => { + await fs.add('foo.edge', 'Hello {{ username }}') const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const compiler = new Compiler(loader, tags) assert.equal(compiler.compile('foo', true).template, ` @@ -88,11 +98,12 @@ test.group('Compiler', (group) => { 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'), '') + test('do not load presenter for inline templates', async (assert) => { + await fs.add('foo.edge', 'Hello {{ username }}') + await fs.add('foo.presenter.js', '') + const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) 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 index 6787620..a1a8d18 100644 --- a/test/edge.spec.ts +++ b/test/edge.spec.ts @@ -64,7 +64,7 @@ test.group('Edge', (group) => { Edge.tag(MyTag) Edge.configure({}) - assert.deepEqual(Edge.compiler['tags'].mytag, MyTag) + assert.deepEqual(Edge.compiler['_tags'].mytag, MyTag) }) test('render a view using the static render method', async (assert) => { diff --git a/test/loader.spec.ts b/test/loader.spec.ts index fb3173e..64219b0 100644 --- a/test/loader.spec.ts +++ b/test/loader.spec.ts @@ -7,28 +7,28 @@ * file that was distributed with this source code. */ +import { join } from 'path' import * as test from 'japa' -import * as fs from 'fs-extra' +import { Filesystem } from '@poppinss/dev-utils' -import { join } from 'path' import { Loader } from '../src/Loader' -const viewsDir = join(__dirname, 'views') +const fs = new Filesystem(join(__dirname, 'views')) test.group('Loader', (group) => { group.afterEach(async () => { - await fs.remove(viewsDir) + await fs.cleanup() }) test('mount path with a name', (assert) => { const loader = new Loader() - loader.mount('default', viewsDir) - assert.deepEqual(loader.mounted, { default: join(__dirname, 'views') }) + loader.mount('default', fs.basePath) + assert.deepEqual(loader.mounted, { default: fs.basePath }) }) test('unmount path with a name', (assert) => { const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) loader.unmount('default') assert.deepEqual(loader.mounted, {}) @@ -37,14 +37,14 @@ test.group('Loader', (group) => { test('throw exception when resolving path from undefined location', (assert) => { const loader = new Loader() const fn = () => loader.resolve('foo', true) - assert.throw(fn, 'Attempting to resolve foo.edge template for unmounted default location') + assert.throw(fn, 'E_UNMOUNTED_DISK_NAME: {default} namespace is not mounted') }) test('resolve template for default location', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello world') + await fs.add('foo.edge', 'Hello world') const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const { template } = loader.resolve('foo', false) assert.equal(template.trim(), 'Hello world') @@ -52,27 +52,27 @@ test.group('Loader', (group) => { test('raise error when template is missing', async (assert) => { const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const fn = () => loader.resolve('foo', false) - assert.throw(fn, `Cannot resolve ${join(viewsDir, 'foo.edge')}. Make sure file exists.`) + assert.throw(fn, `Cannot resolve ${join(fs.basePath, 'foo.edge')}. Make sure the file exists`) }) test('resolve template with extension', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello world') + await fs.add('foo.edge', 'Hello world') const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const { template } = loader.resolve('foo.edge', false) assert.equal(template.trim(), 'Hello world') }) test('resolve template from a named mount path', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello world') + await fs.add('foo.edge', 'Hello world') const loader = new Loader() - loader.mount('users', viewsDir) + loader.mount('users', fs.basePath) const { template } = loader.resolve('users::foo.edge', false) assert.equal(template.trim(), 'Hello world') @@ -80,43 +80,43 @@ test.group('Loader', (group) => { test('do not replace edge within the template path name', async (assert) => { const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const templatePath = loader.makePath('edge-partial.edge') - assert.equal(templatePath, join(viewsDir, 'edge-partial.edge')) + assert.equal(templatePath, join(fs.basePath, 'edge-partial.edge')) }) test('do not replace edge within the template path seperator', async (assert) => { const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const templatePath = loader.makePath('partial/edge') - assert.equal(templatePath, join(viewsDir, 'partial/edge.edge')) + assert.equal(templatePath, join(fs.basePath, 'partial/edge.edge')) }) test('do not replace edge within the template path seperator with extension', async (assert) => { const loader = new Loader() - loader.mount('default', viewsDir) + loader.mount('default', fs.basePath) const templatePath = loader.makePath('partial/edge.edge') - assert.equal(templatePath, join(viewsDir, 'partial/edge.edge')) + assert.equal(templatePath, join(fs.basePath, 'partial/edge.edge')) }) test('do not replace edge within the template path seperator with named disk', async (assert) => { const loader = new Loader() - loader.mount('users', viewsDir) + loader.mount('users', fs.basePath) const templatePath = loader.makePath('users::partial/edge.edge') - assert.equal(templatePath, join(viewsDir, 'partial/edge.edge')) + assert.equal(templatePath, join(fs.basePath, 'partial/edge.edge')) }) test('resolve presenter if exists', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello world') - await fs.outputFile(join(viewsDir, 'foo.presenter.js'), `module.exports = class Foo { + await fs.add('foo.edge', 'Hello world') + await fs.add('foo.presenter.js', `module.exports = class Foo { }`) const loader = new Loader() - loader.mount('users', viewsDir) + loader.mount('users', fs.basePath) const { template, Presenter } = loader.resolve('users::foo.edge', true) assert.equal(template.trim(), 'Hello world') @@ -124,12 +124,12 @@ test.group('Loader', (group) => { }) 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 { + await fs.add('foo.edge', 'Hello world') + await fs.add('foo.presenter.js', `module.exports = class Foo { }`) const loader = new Loader() - loader.mount('users', viewsDir) + loader.mount('users', fs.basePath) const { template, Presenter } = loader.resolve('users::foo.edge', false) assert.equal(template.trim(), 'Hello world') @@ -152,6 +152,6 @@ test.group('Loader', (group) => { loader.register('my-view', { template: 'Hello world' }) const fn = () => loader.register('my-view', { template: 'Hello world' }) - assert.throw(fn, 'Cannot override previously registered my-view template') + assert.throw(fn, 'Cannot override previously registered {my-view} template') }) }) diff --git a/test/tags.spec.ts b/test/tags.spec.ts index 7fa3dd7..5d62e9b 100644 --- a/test/tags.spec.ts +++ b/test/tags.spec.ts @@ -92,7 +92,7 @@ test.group('Include', (group) => { try { compiler.compile('foo', true) } catch (error) { - assert.equal(error.stack.split('\n')[1], ` at (${join(viewsDir, 'foo.edge')}:2:13)`) + assert.equal(error.stack.split('\n')[1], ` at (foo:2:13)`) } }) @@ -105,7 +105,7 @@ test.group('Include', (group) => { try { compiler.compile('foo', true) } catch (error) { - assert.equal(error.stack.split('\n')[1], ` at (${join(viewsDir, 'foo.edge')}:1:9)`) + assert.equal(error.stack.split('\n')[1], ` at (foo:1:9)`) assert.equal(error.message, 'SequenceExpression is not allowed for include tag.') } }) @@ -130,7 +130,7 @@ test.group('Component', (group) => { try { compiler.compile('foo', true) } catch (error) { - assert.equal(error.stack.split('\n')[1], ` at (${join(viewsDir, 'foo.edge')}:3:0)`) + assert.equal(error.stack.split('\n')[1], ` at (foo:3:0)`) assert.equal(error.message, 'Identifier is not allowed for slot tag.') } }) @@ -149,7 +149,7 @@ test.group('Component', (group) => { try { compiler.compile('foo', true) } catch (error) { - assert.equal(error.stack.split('\n')[1], ` at (${join(viewsDir, 'foo.edge')}:3:0)`) + assert.equal(error.stack.split('\n')[1], ` at (foo:3:0)`) assert.equal(error.message, 'Maximum of 2 arguments are allowed for slot tag') } }) @@ -168,7 +168,7 @@ test.group('Component', (group) => { try { compiler.compile('foo', true) } catch (error) { - assert.equal(error.stack.split('\n')[1], ` at (${join(viewsDir, 'foo.edge')}:3:0)`) + assert.equal(error.stack.split('\n')[1], ` at (foo:3:0)`) assert.equal(error.message, 'Identifier is not allowed for slot tag.') } }) @@ -190,7 +190,7 @@ test.group('Component', (group) => { try { compiler.compile('foo', true) } catch (error) { - assert.equal(error.stack.split('\n')[1], ` at (${join(viewsDir, 'foo.edge')}:5:6)`) + assert.equal(error.stack.split('\n')[1], ` at (foo:5:6)`) assert.equal(error.message, 'Literal is not allowed for slot tag.') } }) diff --git a/test/template.spec.ts b/test/template.spec.ts index 58bcf35..d7117d8 100644 --- a/test/template.spec.ts +++ b/test/template.spec.ts @@ -8,45 +8,35 @@ */ import * as test from 'japa' -import * as fs from 'fs-extra' - import { join } from 'path' +import { Filesystem } from '@poppinss/dev-utils' import { Template } from '../src/Template' import { Compiler } from '../src/Compiler' import { Loader } from '../src/Loader' -const tags = { - if: class If { - public static block = true - public static seekable = true - public static selfclosed = false - public static tagName = 'if' - public static compile (): void { - } - }, -} +const tags = {} -const viewsDir = join(__dirname, 'views') +const fs = new Filesystem(join(__dirname, 'views')) const loader = new Loader() -loader.mount('default', viewsDir) +loader.mount('default', fs.basePath) const compiler = new Compiler(loader, tags, false) test.group('Template', (group) => { group.afterEach(async () => { - await fs.remove(viewsDir) + await fs.cleanup() }) test('run template using the given state', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ username }}') + await fs.add('foo.edge', 'Hello {{ username }}') const output = new Template(compiler, {}, {}).render('foo', { username: 'virk' }) assert.equal(output.trim(), 'Hello virk') }) test('run template with custom presenter', async (assert) => { - await fs.outputFile(join(viewsDir, 'foo.edge'), 'Hello {{ getUsername() }}') - await fs.outputFile(join(viewsDir, 'foo.presenter.js'), `module.exports = class MyPresenter { + await fs.add('foo.edge', 'Hello {{ getUsername() }}') + await fs.add('foo.presenter.js', `module.exports = class MyPresenter { constructor (state) { this.state = state } @@ -61,8 +51,8 @@ test.group('Template', (group) => { }) 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 { + await fs.add('foo.edge', 'Hello {{ getUsername() }}') + await fs.add('foo.presenter.js', `module.exports = class MyPresenter { constructor (state) { this.state = state }