Socket
Socket
Sign inDemoInstall

edge.js

Package Overview
Dependencies
Maintainers
1
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

edge.js - npm Package Compare versions

Comparing version 2.2.0 to 3.0.0

build/src/Edge/globals/PrettyPrint.d.ts

8

build/index.d.ts
export * from './src/Contracts';
import { Edge } from './src/Edge';
import { safeValue } from './src/Context';
import { GLOBALS } from './src/Edge/globals';
/**
* Default export
*/
declare const edge: Edge;
export { Edge };
export default edge;
export { safeValue, withCtx } from './src/Context';
export { Edge, safeValue, GLOBALS };

@@ -10,14 +10,14 @@ "use strict";

*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Edge_1 = require("./src/Edge");
exports.Edge = Edge_1.Edge;
const globals_1 = __importDefault(require("./src/Edge/globals"));
const Context_1 = require("./src/Context");
exports.safeValue = Context_1.safeValue;
const globals_1 = require("./src/Edge/globals");
exports.GLOBALS = globals_1.GLOBALS;
/**
* Default export
*/
const edge = new Edge_1.Edge();
globals_1.default(edge);
Object.keys(globals_1.GLOBALS).forEach((key) => edge.global(key, globals_1.GLOBALS[key]));
exports.default = edge;
var Context_1 = require("./src/Context");
exports.safeValue = Context_1.safeValue;
exports.withCtx = Context_1.withCtx;

@@ -15,5 +15,4 @@ import { LoaderTemplate, CacheManagerContract } from '../Contracts';

/**
* Returns the template and the presenter class from the
* cache. If caching is disabled, then it will
* return undefined.
* Returns the template from the cache. If caching is disabled,
* then it will return undefined.
*/

@@ -23,5 +22,5 @@ get(absPath: string): undefined | LoaderTemplate;

* Set's the template path and the payload to the cache. If
* cache is disabled, then this function returns in noop.
* cache is disabled, then this function results in a noop.
*/
set(absPath: string, payload: LoaderTemplate): void;
}

@@ -27,5 +27,4 @@ "use strict";

/**
* Returns the template and the presenter class from the
* cache. If caching is disabled, then it will
* return undefined.
* Returns the template from the cache. If caching is disabled,
* then it will return undefined.
*/

@@ -40,3 +39,3 @@ get(absPath) {

* Set's the template path and the payload to the cache. If
* cache is disabled, then this function returns in noop.
* cache is disabled, then this function results in a noop.
*/

@@ -43,0 +42,0 @@ set(absPath, payload) {

@@ -0,1 +1,2 @@

import { Parser } from 'edge-parser';
import { Token } from 'edge-lexer';

@@ -5,8 +6,4 @@ import { CacheManager } from '../CacheManager';

/**
* Compiler compiles the template to a function, which can be invoked at a later
* stage using the [[Context]]. [edge-parser](https://npm.im/edge-parser) is
* used under the hood to parse the templates.
*
* Also, the `layouts` are handled natively by the compiler. Before starting
* the parsing process, it will recursively merge the layout sections.
* Compiler is to used to compile templates using the `edge-parser`. Along with that
* it natively merges the contents of a layout with a parent template.
*/

@@ -30,3 +27,3 @@ export declare class Compiler implements CompilerContract {

/**
* Converts the template content to an [array of lexer tokens]. The method is
* Converts the template content to an array of lexer tokens. The method is
* same as the `parser.tokenize`, but it also handles layouts natively.

@@ -38,25 +35,13 @@ *

*/
tokenize(templatePath: string): Token[];
tokenize(templatePath: string, parser?: Parser): Token[];
/**
* Compiles the template contents to a function string, which can be invoked
* later.
* Compiles the template contents to string. The output is same as the `edge-parser`,
* it's just that the compiler uses the loader to load the templates and also
* handles layouts.
*
* 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
* compiler.compile('welcome', false)
* // output
*
* {
* template: `function (template, ctx) {
* let out = ''
* out += ''
* return out
* })(template, ctx)`,
* Presenter: class Presenter | undefined
* }
* compiler.compile('welcome')
* ```
*/
compile(templatePath: string, inline: boolean): LoaderTemplate;
compile(templatePath: string, localVariables?: string[]): LoaderTemplate;
}

@@ -16,8 +16,4 @@ "use strict";

/**
* Compiler compiles the template to a function, which can be invoked at a later
* stage using the [[Context]]. [edge-parser](https://npm.im/edge-parser) is
* used under the hood to parse the templates.
*
* Also, the `layouts` are handled natively by the compiler. Before starting
* the parsing process, it will recursively merge the layout sections.
* Compiler is to used to compile templates using the `edge-parser`. Along with that
* it natively merges the contents of a layout with a parent template.
*/

@@ -47,8 +43,9 @@ class Compiler {

/**
* Ignore new lines, layout tag and empty raw nodes inside the parent
* Ignore new lines, comments, layout tag and empty raw nodes inside the parent
* template
*/
if (edge_lexer_1.utils.isTag(node, 'layout') ||
node.type === 'newline' ||
(node.type === 'raw' && !node.value.trim())) {
if (edge_lexer_1.utils.isTag(node, 'layout')
|| node.type === 'newline'
|| (node.type === 'raw' && !node.value.trim())
|| node.type === 'comment') {
return;

@@ -107,4 +104,4 @@ }

*/
templateContentToTokens(content, parser) {
let templateTokens = parser.tokenize(content);
templateContentToTokens(content, parser, absPath) {
let templateTokens = parser.tokenize(content, absPath);
const firstToken = templateTokens[0];

@@ -117,3 +114,3 @@ /**

const layoutName = firstToken.properties.jsArg.replace(/'|"/g, '');
templateTokens = this.mergeSections(this.tokenize(layoutName), templateTokens);
templateTokens = this.mergeSections(this.tokenize(layoutName, parser), templateTokens);
}

@@ -123,3 +120,3 @@ return templateTokens;

/**
* Converts the template content to an [array of lexer tokens]. The method is
* Converts the template content to an array of lexer tokens. The method is
* same as the `parser.tokenize`, but it also handles layouts natively.

@@ -131,30 +128,17 @@ *

*/
tokenize(templatePath) {
tokenize(templatePath, parser) {
const absPath = this.loader.makePath(templatePath);
const { template } = this.loader.resolve(absPath, false);
const parser = new edge_parser_1.Parser(this.tags, { filename: absPath });
return this.templateContentToTokens(template, parser);
const { template } = this.loader.resolve(absPath);
return this.templateContentToTokens(template, parser || new edge_parser_1.Parser(this.tags), absPath);
}
/**
* Compiles the template contents to a function string, which can be invoked
* later.
* Compiles the template contents to string. The output is same as the `edge-parser`,
* it's just that the compiler uses the loader to load the templates and also
* handles layouts.
*
* 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
* compiler.compile('welcome', false)
* // output
*
* {
* template: `function (template, ctx) {
* let out = ''
* out += ''
* return out
* })(template, ctx)`,
* Presenter: class Presenter | undefined
* }
* compiler.compile('welcome')
* ```
*/
compile(templatePath, inline) {
compile(templatePath, localVariables) {
const absPath = this.loader.makePath(templatePath);

@@ -169,38 +153,19 @@ /**

}
const parser = new edge_parser_1.Parser(this.tags);
const buffer = new edge_parser_1.EdgeBuffer(absPath);
/**
* Do not return presenter in inline mode
* Define local variables on the parser. This is helpful when trying to compile
* a partail and we want to share the local state of the parent template
* with it
*/
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.
*/
const parser = new edge_parser_1.Parser(this.tags, { filename: absPath });
/**
* Resolve the template and Presenter using the given loader. We always
* load the presenter but don't return it when `loadPresenter = false`.
*/
const { template, Presenter } = this.loader.resolve(absPath, true);
/**
* Convert template to AST. The AST will have the layout actions merged (if layout)
* is used.
*/
const templateTokens = this.templateContentToTokens(template, parser);
/**
* Finally process the ast
*/
const buffer = new edge_parser_1.EdgeBuffer(absPath, wrapAsFunction);
if (localVariables) {
localVariables.forEach((localVariable) => parser.stack.defineVariable(localVariable));
}
const templateTokens = this.tokenize(absPath, parser);
templateTokens.forEach((token) => parser.processToken(token, buffer));
const payload = {
template: buffer.flush(),
Presenter,
};
this.cacheManager.set(absPath, payload);
return loadPresenter ? payload : { template: payload.template };
const template = buffer.flush();
this.cacheManager.set(absPath, { template });
return { template };
}
}
exports.Compiler = Compiler;
import { Macroable } from 'macroable';
import { Presenter } from '../Presenter';
import { ContextContract } from '../Contracts';

@@ -9,3 +8,3 @@ /**

*/
declare class SafeValue {
export declare class SafeValue {
value: any;

@@ -15,29 +14,10 @@ constructor(value: any);

/**
* A class to wrap callbacks that can access the `ctx`
*/
declare class WithCtx {
private callback;
constructor(callback: (ctx: ContextContract, ...args: any[]) => any);
/**
* Invoke the callback
*/
invoke(ctx: ContextContract, bindState: any): (...args: any[]) => any;
}
/**
* Context is used at runtime to resolve values for a given
* template.
* The context passed to templates during Runtime. Context enables tags to
* register custom methods which are available during runtime.
*
* Also the context can be extended to add `getters` and `methods`. Checkout
* [macroable](https://github.com/poppinss/macroable) for same.
* For example: The `@each` tag defines `ctx.loop` method to loop over
* Arrays and Objects.
*/
export declare class Context extends Macroable implements ContextContract {
presenter: Presenter;
/**
* Frames are used to define a inner scope in which values will
* be resolved. The resolve function starts with the deepest
* frame and then resolve the value up until the first
* frame.
*/
private frames;
/**
* Required by Macroable

@@ -47,100 +27,13 @@ */

protected static getters: {};
constructor();
/**
* Added by compiler
*/
$filename: string;
$lineNumber: number;
constructor(presenter: Presenter);
/**
* Returns value for a key inside frames. Stops looking for it,
* when value is found inside any frame.
*/
private getFromFrame;
/**
* Returns a merged copy of the current state. The objects are merged
* in the same order as they are resolved.
*/
private getCurrentState;
/**
* Creates a new frame scope. Think of a scope as a Javacript block
* scope, where variables defined inside the scope are only available
* to that scope.
*
* ```js
* ctx.newFrame()
* ```
*/
newFrame(): void;
/**
* Set key/value pair on the frame object. The value will only be available til
* the `removeFrame` is not called.
*
* ```js
* ctx.setOnFrame('username', 'virk')
*
* // nested values
* ctx.setOnFrame('user.username', 'virk')
* ```
*
* @throws Error if no frame scopes exists.
*/
setOnFrame(key: string, value: any): void;
/**
* Removes the most recent frame/scope. All values set inside the
* frame via `setOnFrame` will be removed.
*/
removeFrame(): void;
/**
* Returns all the frames
*/
getFrames(): any[];
/**
* Mark output as safe
*/
safe<T extends any>(value: T): SafeValue;
/**
* Escapes the value to be HTML safe. Only strings are escaped
* and rest all values will be returned as it is.
*/
escape<T>(input: T): T;
escape<T>(input: T): T extends SafeValue ? T['value'] : T;
/**
* Transform the resolved value before returning it
* back
*/
private transformValue;
/**
* Resolves value for a given key. It will look for the value in different
* locations and continues till the end if `undefined` is returned at
* each step.
*
* The following steps are followed in the same order as defined.
*
* 1. Check for value inside frames.
* 2. Then on the presenter instance.
* 3. Then the presenter `state` object.
* 4. Finally fallback to the sharedState.
*
* @example
* ```js
* ctx.resolve('username')
* ```
*/
resolve(key: string): any;
/**
* Set/Update the value in the context. The value is defined in the following
* order.
*
* 1. If the scope is inside a frame, then will be created/updated on the frame.
* 2. Otherwise, the value is created on the presenter state.
*
* ```js
* ctx.set('username', 'virk')
* ```
*/
set(key: string, value: any, isolated?: boolean): void;
/**
* Rethrows the runtime exception by re-constructing the error message
* to point back to the original filename
*/
reThrow(error: any): void;
reThrow(error: any, filename: string, lineNumber: number): never;
}

@@ -151,7 +44,1 @@ /**

export declare function safeValue(value: string): SafeValue;
/**
* Wrap a function that receives the template engine current
* ctx when invoked.
*/
export declare function withCtx(callback: (ctx: ContextContract, ...args: any[]) => any): WithCtx;
export {};

@@ -15,3 +15,2 @@ "use strict";

const he_1 = __importDefault(require("he"));
const lodash_1 = require("lodash");
const macroable_1 = require("macroable");

@@ -29,109 +28,15 @@ const edge_error_1 = require("edge-error");

}
exports.SafeValue = SafeValue;
/**
* A class to wrap callbacks that can access the `ctx`
*/
class WithCtx {
constructor(callback) {
this.callback = callback;
}
/**
* Invoke the callback
*/
invoke(ctx, bindState) {
return (...args) => {
return this.callback.bind(bindState)(ctx, ...args);
};
}
}
/**
* Context is used at runtime to resolve values for a given
* template.
* The context passed to templates during Runtime. Context enables tags to
* register custom methods which are available during runtime.
*
* Also the context can be extended to add `getters` and `methods`. Checkout
* [macroable](https://github.com/poppinss/macroable) for same.
* For example: The `@each` tag defines `ctx.loop` method to loop over
* Arrays and Objects.
*/
class Context extends macroable_1.Macroable {
constructor(presenter) {
constructor() {
super();
this.presenter = presenter;
/**
* Frames are used to define a inner scope in which values will
* be resolved. The resolve function starts with the deepest
* frame and then resolve the value up until the first
* frame.
*/
this.frames = [];
/**
* Added by compiler
*/
this.$filename = '';
this.$lineNumber = 0;
}
/**
* Returns value for a key inside frames. Stops looking for it,
* when value is found inside any frame.
*/
getFromFrame(key) {
const frameWithVal = this.frames.find((frame) => frame[key] !== undefined);
return frameWithVal ? frameWithVal[key] : undefined;
}
/**
* Returns a merged copy of the current state. The objects are merged
* in the same order as they are resolved.
*/
getCurrentState() {
return Object.assign({}, this.presenter.sharedState, this.presenter.state, ...this.frames);
}
/**
* Creates a new frame scope. Think of a scope as a Javacript block
* scope, where variables defined inside the scope are only available
* to that scope.
*
* ```js
* ctx.newFrame()
* ```
*/
newFrame() {
this.frames.unshift({});
}
/**
* Set key/value pair on the frame object. The value will only be available til
* the `removeFrame` is not called.
*
* ```js
* ctx.setOnFrame('username', 'virk')
*
* // nested values
* ctx.setOnFrame('user.username', 'virk')
* ```
*
* @throws Error if no frame scopes exists.
*/
setOnFrame(key, value) {
const recentFrame = this.frames[0];
if (!recentFrame) {
throw new Error('Make sure to call "newFrame" before calling "setOnFrame"');
}
lodash_1.set(recentFrame, key, value);
}
/**
* Removes the most recent frame/scope. All values set inside the
* frame via `setOnFrame` will be removed.
*/
removeFrame() {
this.frames.shift();
}
/**
* Returns all the frames
*/
getFrames() {
return this.frames;
}
/**
* Mark output as safe
*/
safe(value) {
return new SafeValue(value);
}
/**
* Escapes the value to be HTML safe. Only strings are escaped

@@ -146,125 +51,13 @@ * and rest all values will be returned as it is.

/**
* Transform the resolved value before returning it
* back
*/
transformValue(value, bindState) {
if (value instanceof WithCtx) {
return value.invoke(this, bindState);
}
if (typeof (value) === 'function') {
return value.bind(bindState);
}
return value;
}
/**
* Resolves value for a given key. It will look for the value in different
* locations and continues till the end if `undefined` is returned at
* each step.
*
* The following steps are followed in the same order as defined.
*
* 1. Check for value inside frames.
* 2. Then on the presenter instance.
* 3. Then the presenter `state` object.
* 4. Finally fallback to the sharedState.
*
* @example
* ```js
* ctx.resolve('username')
* ```
*/
resolve(key) {
/**
* A special key to return the template current state
*/
if (key === '$state') {
return this.getCurrentState();
}
/**
* A special key to return the filename of the current execution
* scope.
*/
if (key === '$filename') {
return this.$filename;
}
/**
* A special key to return the current execution line number pointing
* fowards the original template file.
*/
if (key === '$lineNumber') {
return this.$lineNumber;
}
let value;
/**
* Pull from one of the nested frames
*/
value = this.getFromFrame(key);
if (value !== undefined) {
return this.transformValue(value, this);
}
/**
* Check for value as a property on the presenter
* itself.
*/
value = this.presenter[key];
if (value !== undefined) {
return this.transformValue(value, this.presenter);
}
/**
* Otherwise look into presenter state
*/
value = this.presenter.state[key];
if (value !== undefined) {
return this.transformValue(value, this.presenter.state);
}
/**
* Finally fallback to shared globals
*/
value = this.presenter.sharedState[key];
return this.transformValue(value, this.presenter.sharedState);
}
/**
* Set/Update the value in the context. The value is defined in the following
* order.
*
* 1. If the scope is inside a frame, then will be created/updated on the frame.
* 2. Otherwise, the value is created on the presenter state.
*
* ```js
* ctx.set('username', 'virk')
* ```
*/
set(key, value, isolated = false) {
/**
* Set value on the presenter state if it already exists and user
* doesn't want an isolated state for the current frame scope
*/
if (lodash_1.get(this.presenter.state, key) !== undefined && !isolated) {
lodash_1.set(this.presenter.state, key, value);
return;
}
/**
* If frames exists, then set the value on the framework
*/
if (this.frames.length) {
this.setOnFrame(key, value);
return;
}
/**
* Otherwise set on presenter
*/
lodash_1.set(this.presenter.state, key, value);
}
/**
* Rethrows the runtime exception by re-constructing the error message
* to point back to the original filename
*/
reThrow(error) {
reThrow(error, filename, lineNumber) {
if (error instanceof edge_error_1.EdgeError) {
throw error;
}
// const message = error.message.replace(/ctx\.resolve\(\.\.\.\)/, this.lastResolvedKey)
throw new edge_error_1.EdgeError(error.message, 'E_RUNTIME_EXCEPTION', {
filename: this.$filename,
line: this.$lineNumber,
const message = error.message.replace(/state\./, '');
throw new edge_error_1.EdgeError(message, 'E_RUNTIME_EXCEPTION', {
filename: filename,
line: lineNumber,
col: 0,

@@ -287,9 +80,1 @@ });

exports.safeValue = safeValue;
/**
* Wrap a function that receives the template engine current
* ctx when invoked.
*/
function withCtx(callback) {
return new WithCtx(callback);
}
exports.withCtx = withCtx;

@@ -9,5 +9,5 @@ /**

*/
import { Token } from 'edge-lexer';
import { Token, TagToken } from 'edge-lexer';
import { MacroableConstructorContract } from 'macroable';
import { ParserTagDefininationContract } from 'edge-parser';
import { ParserTagDefinitionContract, Parser, EdgeBuffer } from 'edge-parser';
/**

@@ -18,5 +18,2 @@ * The shape in which the loader must resolve the template

template: string;
Presenter?: {
new (state: any): any;
};
};

@@ -44,3 +41,3 @@ /**

*/
resolve(templatePath: string, withPresenter: boolean): LoaderTemplate;
resolve(templatePath: string): LoaderTemplate;
/**

@@ -56,22 +53,7 @@ * Make absolute path to a template

/**
* Shape of a view presenter
*/
export interface PresenterContract {
state: any;
sharedState: any;
}
/**
* Shape of runtime context
*/
export interface ContextContract {
presenter: PresenterContract;
safe<T extends any>(value: T): {
value: T;
};
newFrame(): void;
setOnFrame(key: string, value: any): void;
removeFrame(): void;
escape<T>(input: T): T;
resolve(key: string): any;
set(key: string, value: any): void;
reThrow(error: any, filename: string, linenumber: number): never;
}

@@ -82,9 +64,9 @@ /**

export interface ContextConstructorContract extends MacroableConstructorContract<ContextContract> {
new (presenter: any, sharedState: any): ContextContract;
new (): ContextContract;
}
/**
* The final tag must have a tagName along with other properties
* The tag must have a tagName along with other properties
* required by lexer and parser
*/
export interface TagContract extends ParserTagDefininationContract {
export interface TagContract extends ParserTagDefinitionContract {
tagName: string;

@@ -112,4 +94,4 @@ run?(context: ContextConstructorContract): void;

cacheManager: CacheManagerContract;
compile(templatePath: string, inline: boolean): LoaderTemplate;
tokenize(templatePath: string): Token[];
compile(templatePath: string, localVariables?: string[]): LoaderTemplate;
tokenize(templatePath: string, parser?: Parser): Token[];
}

@@ -147,1 +129,7 @@ /**

}
/**
* Required for someone creating custom tags
*/
export declare type EdgeBufferContract = EdgeBuffer;
export declare type ParserContract = Parser;
export declare type TagTokenContract = TagToken;

@@ -1,5 +0,13 @@

import { EdgeContract } from '../../Contracts';
/**
* A list of default globals
*/
export default function globals(edge: EdgeContract): void;
import { safeValue } from '../../Context';
export declare const GLOBALS: {
inspect: (value: any) => import("../../Context").SafeValue;
truncate: (value: string, length?: number, options?: {
strict: boolean;
suffix: string;
} | undefined) => any;
excerpt: (value: string, length?: number, options?: {
strict: boolean;
suffix: string;
} | undefined) => any;
safe: typeof safeValue;
};
"use strict";
/*
* edge
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* edge.js
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const util_1 = require("util");
const lodash_1 = require("lodash");
const truncatise_1 = __importDefault(require("truncatise"));
const PrettyPrint_1 = require("./PrettyPrint");
const Context_1 = require("../../Context");
/**
* Inspect value.
*/
function inspect(ctx, valueToInspect, depth = 1) {
const inspectedString = `<pre>${util_1.inspect(valueToInspect, {
showHidden: true,
compact: false,
depth: depth,
})}</pre>`;
const filename = `<span style="color: #999; position: absolute; right: 20px; top: 10px;">
${ctx.resolve('$filename')}
</span>`;
return Context_1.safeValue(`<div class="__inspect_output" style="background: #000; color: #fff; padding: 20px; position: relative;">${inspectedString}${filename}</div>`);
}
/**
* Compacting the inspect output of self
*/
inspect[Symbol.for('nodejs.util.inspect.custom')] = function customInspect() {
return '[inspect]';
exports.GLOBALS = {
inspect: (value) => {
return Context_1.safeValue(new PrettyPrint_1.PrettyPrint().print(value));
},
truncate: (value, length = 20, options) => {
return truncatise_1.default(value, {
Strict: !!(options === null || options === void 0 ? void 0 : options.strict),
StripHTML: false,
TruncateLength: length,
TruncateBy: 'characters',
Suffix: options === null || options === void 0 ? void 0 : options.suffix,
});
},
excerpt: (value, length = 20, options) => {
return truncatise_1.default(value, {
Strict: !!(options === null || options === void 0 ? void 0 : options.strict),
StripHTML: true,
TruncateLength: length,
TruncateBy: 'characters',
Suffix: options === null || options === void 0 ? void 0 : options.suffix,
});
},
safe: Context_1.safeValue,
};
/**
* A list of default globals
*/
function globals(edge) {
edge.global('inspect', Context_1.withCtx(inspect));
edge.global('range', (start, end, step) => lodash_1.range(start, end, step));
edge.global('first', lodash_1.first);
edge.global('last', lodash_1.last);
edge.global('groupBy', lodash_1.groupBy);
edge.global('size', lodash_1.size);
edge.global('truncate', lodash_1.truncate);
edge.global('toAnchor', (url, title = url) => {
return Context_1.safeValue(`<a href="${url}"> ${title} </a>`);
});
edge.global('style', (url, title = url) => {
return Context_1.safeValue(`<a href="${url}"> ${title} </a>`);
});
}
exports.default = globals;
import { Compiler } from '../Compiler';
import { TagContract, EdgeOptions, EdgeContract, LoaderTemplate, EdgeRendererContract } from '../Contracts';
/**
* Exposes the API to render templates, register custom tags and globals
*/
export declare class Edge implements EdgeContract {

@@ -4,0 +7,0 @@ private options;

@@ -23,2 +23,5 @@ "use strict";

const Renderer_1 = require("../Renderer");
/**
* Exposes the API to render templates, register custom tags and globals
*/
class Edge {

@@ -25,0 +28,0 @@ constructor(options = {}) {

@@ -11,5 +11,5 @@ /**

/**
* 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.
* The job of a loader is to load the template from a given path.
* The base loader (shipped with edge) looks for files on the
* file-system and reads them synchronously.
*

@@ -28,10 +28,2 @@ * You are free to define your own loaders that implements the [[LoaderContract]] interface.

/**
* 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
* and their presenters are cached anyways.
*/
private getPresenterForTemplate;
/**
* Reads the content of a template from the disk. An exception is raised

@@ -108,10 +100,4 @@ * when file is missing or if `readFileSync` returns an error.

/**
* Resolves the template from the disk, optionally loads the presenter too. The presenter
* resolution is based on the convention and resolved from the same directory
* as the template.
* Resolves the template by reading its contents from the disk
*
* ## Presenter convention
* - View name - welcome.edge
* - Presenter name - Welcome.presenter.js
*
* ```js

@@ -123,9 +109,8 @@ * loader.resolve('welcome', true)

* template: `<h1> Template content </h1>`,
* Presenter: class Presenter | undefined
* }
* ```
*/
resolve(templatePath: string, withPresenter: boolean): LoaderTemplate;
resolve(templatePath: string): LoaderTemplate;
/**
* Register in memory template and Presenter for a given path. This is super helpful
* Register in memory template for a given path. This is super helpful
* when distributing components.

@@ -132,0 +117,0 @@ *

@@ -10,14 +10,9 @@ "use strict";

*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const import_fresh_1 = __importDefault(require("import-fresh"));
const path_1 = require("path");
const utils_1 = require("@poppinss/utils");
/**
* 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.
* The job of a loader is to load the template from a given path.
* The base loader (shipped with edge) looks for files on the
* file-system and reads them synchronously.
*

@@ -38,22 +33,2 @@ * You are free to define your own loaders that implements the [[LoaderContract]] interface.

/**
* 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
* and their presenters are cached anyways.
*/
getPresenterForTemplate(templatePath) {
const presenterPath = templatePath
.replace(/^\w/, c => c.toUpperCase())
.replace(path_1.extname(templatePath), '.presenter.js');
try {
return utils_1.esmResolver(import_fresh_1.default(presenterPath));
}
catch (error) {
if (['ENOENT', 'MODULE_NOT_FOUND'].indexOf(error.code) === -1) {
throw error;
}
}
}
/**
* Reads the content of a template from the disk. An exception is raised

@@ -68,3 +43,3 @@ * when file is missing or if `readFileSync` returns an error.

if (error.code === 'ENOENT') {
throw new utils_1.Exception(`Cannot resolve "${absPath}". Make sure the file exists`, 500, 'E_MISSING_TEMPLATE_FILE');
throw new Error(`Cannot resolve "${absPath}". Make sure the file exists`);
}

@@ -179,3 +154,3 @@ else {

if (!mountedDir) {
throw new utils_1.Exception(`"${diskName}" namespace is not mounted`, 500, 'E_UNMOUNTED_DISK_NAME');
throw new Error(`"${diskName}" namespace is not mounted`);
}

@@ -185,10 +160,4 @@ return path_1.join(mountedDir, template);

/**
* Resolves the template from the disk, optionally loads the presenter too. The presenter
* resolution is based on the convention and resolved from the same directory
* as the template.
* Resolves the template by reading its contents from the disk
*
* ## Presenter convention
* - View name - welcome.edge
* - Presenter name - Welcome.presenter.js
*
* ```js

@@ -200,7 +169,6 @@ * loader.resolve('welcome', true)

* template: `<h1> Template content </h1>`,
* Presenter: class Presenter | undefined
* }
* ```
*/
resolve(templatePath, withPresenter) {
resolve(templatePath) {
/**

@@ -210,4 +178,3 @@ * Return from pre-registered one's if exists

if (this.preRegistered.has(templatePath)) {
const contents = this.preRegistered.get(templatePath);
return withPresenter ? contents : { template: contents.template };
return this.preRegistered.get(templatePath);
}

@@ -220,7 +187,6 @@ /**

template: this.readTemplateContents(templatePath),
Presenter: withPresenter ? this.getPresenterForTemplate(templatePath) : undefined,
};
}
/**
* Register in memory template and Presenter for a given path. This is super helpful
* Register in memory template for a given path. This is super helpful
* when distributing components.

@@ -246,3 +212,3 @@ *

if (typeof (contents.template) !== 'string') {
throw new utils_1.Exception('Make sure to define the template content as a string', 500, 'E_MISSING_TEMPLATE_CONTENTS');
throw new Error('Make sure to define the template content as a string');
}

@@ -253,3 +219,3 @@ /**

if (this.preRegistered.has(templatePath)) {
throw new utils_1.Exception(`Cannot override previously registered "${templatePath}" template`, 500, 'E_DUPLICATE_TEMPLATE_PATH');
throw new Error(`Cannot override previously registered "${templatePath}" template`);
}

@@ -256,0 +222,0 @@ this.preRegistered.set(templatePath, contents);

@@ -10,4 +10,7 @@ "use strict";

*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const lodash_merge_1 = __importDefault(require("lodash.merge"));
const Template_1 = require("../Template");

@@ -28,3 +31,3 @@ /**

share(data) {
lodash_1.merge(this.locals, data);
lodash_merge_1.default(this.locals, data);
return this;

@@ -31,0 +34,0 @@ }

@@ -17,3 +17,3 @@ import { Parser } from 'edge-parser';

*/
add(key: any, value: any): void;
add(key: any, value: any, isComputed?: boolean): void;
/**

@@ -20,0 +20,0 @@ * Returns the object alike string back.

@@ -28,3 +28,4 @@ "use strict";

*/
add(key, value) {
add(key, value, isComputed = false) {
key = isComputed ? `[${key}]` : key;
this.obj += this.obj.length ? `, ${key}: ${value}` : `${key}: ${value}`;

@@ -72,3 +73,3 @@ }

const value = parser.utils.stringify(prop.value);
objectifyString.add(key, value);
objectifyString.add(key, value, prop.computed);
});

@@ -75,0 +76,0 @@ }

@@ -48,3 +48,3 @@ "use strict";

utils_1.isSubsetOf(name, ALLOWED_EXPRESSION_FOR_COMPONENT_NAME, () => {
utils_1.unallowedExpression(`"${parser.utils.stringify(name)}" is not a valid argument for component name`, name, filename);
utils_1.unallowedExpression(`"${parser.utils.stringify(name)}" is not a valid argument for component name`, filename, parser.utils.getExpressionLoc(name));
});

@@ -76,3 +76,3 @@ /**

utils_1.isSubsetOf(parsed, [edge_parser_1.expressions.Literal, edge_parser_1.expressions.SequenceExpression], () => {
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @slot tag`, parsed, token.filename);
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @slot tag`, token.filename, parser.utils.getExpressionLoc(parsed));
});

@@ -93,3 +93,3 @@ /**

utils_1.isSubsetOf(name, [edge_parser_1.expressions.Literal], () => {
utils_1.unallowedExpression('slot name must be a valid string literal', name, token.filename);
utils_1.unallowedExpression('slot name must be a valid string literal', token.filename, parser.utils.getExpressionLoc(name));
});

@@ -115,3 +115,3 @@ /**

utils_1.isSubsetOf(parsed.expressions[1], [edge_parser_1.expressions.Identifier], () => {
utils_1.unallowedExpression(`"${parser.utils.stringify(parsed.expressions[1])}" is not valid prop identifier for @slot tag`, parsed.expressions[1], token.filename);
utils_1.unallowedExpression(`"${parser.utils.stringify(parsed.expressions[1])}" is not valid prop identifier for @slot tag`, token.filename, parser.utils.getExpressionLoc(parsed.expressions[1]));
});

@@ -132,3 +132,3 @@ /**

compile(parser, buffer, token) {
const parsed = parser.utils.transformAst(parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), token.filename);
const parsed = utils_1.parseJsArg(parser, token);
/**

@@ -138,3 +138,3 @@ * Check component jsProps for allowed expressions

utils_1.isSubsetOf(parsed, ALLOWED_EXPRESSION_FOR_COMPONENT_NAME.concat(edge_parser_1.expressions.SequenceExpression), () => {
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @component tag`, parsed, token.filename);
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @component tag`, token.filename, parser.utils.getExpressionLoc(parsed));
});

@@ -156,11 +156,13 @@ /**

const mainSlot = {
outputVar: 'slot_main',
props: {},
buffer: new edge_parser_1.EdgeBuffer(token.filename, false, { outputVar: 'slot_main' }),
buffer: new edge_parser_1.EdgeBuffer(token.filename, { outputVar: 'slot_main' }),
line: -1,
filename: token.filename,
};
let slotsCounter = 0;
/**
* Loop over all the component children
*/
token.children.forEach((child, index) => {
token.children.forEach((child) => {
/**

@@ -170,2 +172,8 @@ * If children is not a slot, then add it to the main slot

if (!edge_lexer_1.utils.isTag(child, 'slot')) {
/**
* Ignore first newline inside slots
*/
if (mainSlot.buffer.size === 0 && child.type === 'newline') {
return;
}
parser.processToken(child, mainSlot.buffer);

@@ -178,4 +186,5 @@ return;

const [slotName, slotProps] = getSlotNameAndProps(child, parser);
slotsCounter++;
/**
* Create a new slot with buffer to process the childs
* Create a new slot with buffer to process the children
*/

@@ -188,3 +197,4 @@ if (!slots[slotName]) {

slots[slotName] = {
buffer: new edge_parser_1.EdgeBuffer(token.filename, false, { outputVar: `slot_${index}` }),
outputVar: `slot_${slotsCounter}`,
buffer: new edge_parser_1.EdgeBuffer(token.filename, { outputVar: `slot_${slotsCounter}` }),
props: slotProps,

@@ -198,4 +208,4 @@ line: -1,

if (slotProps) {
slots[slotName].buffer.writeExpression('ctx.newFrame()', slots[slotName].filename, slots[slotName].line);
slots[slotName].buffer.writeExpression(`ctx.setOnFrame('${slotProps}', ${slotProps})`, slots[slotName].filename, slots[slotName].line);
parser.stack.defineScope();
parser.stack.defineVariable(slotProps);
}

@@ -206,3 +216,8 @@ }

*/
child.children.forEach((grandChildren) => parser.processToken(grandChildren, slots[slotName].buffer));
child.children.forEach((grandChildren) => {
if (slots[slotName].buffer.size === 0 && grandChildren.type === 'newline') {
return;
}
parser.processToken(grandChildren, slots[slotName].buffer);
});
/**

@@ -212,3 +227,3 @@ * Close the frame after process the slot children

if (slotProps) {
slots[slotName].buffer.writeExpression('ctx.removeFrame()', slots[slotName].filename, slots[slotName].line);
parser.stack.clearScope();
}

@@ -224,3 +239,3 @@ });

mainSlot.buffer.wrap('function () {', '}');
obj.add('main', mainSlot.buffer.flush());
obj.add('main', mainSlot.buffer.disableFileAndLineVariables().flush());
}

@@ -239,3 +254,3 @@ else {

slots[slotName].buffer.wrap(fnCall, '}');
obj.add(slotName, slots[slotName].buffer.flush());
obj.add(slotName, slots[slotName].buffer.disableFileAndLineVariables().flush());
}

@@ -242,0 +257,0 @@ else {

import { TagContract } from '../Contracts';
/**
* Add debugger break point to the compiled template
*
* ```edge
* @debugger
* ```
*/
export declare const debuggerTag: TagContract;

@@ -13,6 +13,2 @@ "use strict";

* Add debugger break point to the compiled template
*
* ```edge
* @debugger
* ```
*/

@@ -24,3 +20,3 @@ exports.debuggerTag = {

/**
* Compiles else block node to Javascript else statement
* Compiles `@debugger` tags
*/

@@ -27,0 +23,0 @@ compile(_, buffer, token) {

@@ -6,4 +6,4 @@ import { TagContract } from '../Contracts';

* ```edge
* @each(user in users)
* {{ user }} {{ $loop.index }}
* @each((user, index) in users)
* {{ user }} {{ index }}
* @endeach

@@ -10,0 +10,0 @@ * ```

@@ -10,19 +10,32 @@ "use strict";

*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_size_1 = __importDefault(require("lodash.size"));
const lodash_foreach_1 = __importDefault(require("lodash.foreach"));
const edge_lexer_1 = require("edge-lexer");
const edge_parser_1 = require("edge-parser");
const lodash_1 = require("lodash");
const utils_1 = require("../utils");
/**
* Returns the list to loop over for the each expression
* Returns the list to loop over for the each binary expression
*/
function getLoopList(expression, parser, filename) {
return parser.utils.stringify(parser.utils.transformAst(expression, filename));
function getLoopList(rhsExpression, parser, filename) {
return parser.utils.stringify(parser.utils.transformAst(rhsExpression, filename, parser));
}
/**
* Returns loop item and the index for the each expression
* Returns loop item and the index for the each binary expression
*/
function getLoopItemAndIndex(expression, filename) {
utils_1.isSubsetOf(expression, [edge_parser_1.expressions.SequenceExpression, edge_parser_1.expressions.Identifier], () => {
utils_1.unallowedExpression(`invalid left hand side "${expression.type}" for the @each tag`, expression, filename);
function getLoopItemAndIndex(lhsExpression, parser, filename) {
/**
* Ensure the LHS content inside `@each()` curly braces is a `SequenceExpression` or
* `Identifier`. Anything else is not allowed.
*
* For example:
*
* - In `@each(user in users)`, `user` is an indentifier
* - In `@each((user, index) in users)`, `(user, index)` is a sequence expression
*/
utils_1.isSubsetOf(lhsExpression, [edge_parser_1.expressions.SequenceExpression, edge_parser_1.expressions.Identifier], () => {
utils_1.unallowedExpression(`invalid left hand side "${lhsExpression.type}" expression for the @each tag`, filename, parser.utils.getExpressionLoc(lhsExpression));
});

@@ -32,12 +45,21 @@ /**

*/
if (expression.type === 'SequenceExpression') {
utils_1.isSubsetOf(expression.expressions[0], [edge_parser_1.expressions.Identifier], () => {
utils_1.unallowedExpression(`"${expression.expressions[0]}.type" is not allowed as value identifier for @each tag`, expression.expressions[0], filename);
if (lhsExpression.type === 'SequenceExpression') {
/**
* First item of the sequence expression must be an idenifier
*/
utils_1.isSubsetOf(lhsExpression.expressions[0], [edge_parser_1.expressions.Identifier], () => {
utils_1.unallowedExpression(`"${lhsExpression.expressions[0]}.type" is not allowed as value identifier for @each tag`, filename, parser.utils.getExpressionLoc(lhsExpression.expressions[0]));
});
utils_1.isSubsetOf(expression.expressions[1], [edge_parser_1.expressions.Identifier], () => {
utils_1.unallowedExpression(`"${expression.expressions[1]}.type" is not allowed as key identifier for @each tag`, expression.expressions[1], filename);
/**
* Second item of the sequence expression must be an idenifier
*/
utils_1.isSubsetOf(lhsExpression.expressions[1], [edge_parser_1.expressions.Identifier], () => {
utils_1.unallowedExpression(`"${lhsExpression.expressions[1]}.type" is not allowed as key identifier for @each tag`, filename, parser.utils.getExpressionLoc(lhsExpression.expressions[1]));
});
return [expression.expressions[0].name, expression.expressions[1].name];
return [lhsExpression.expressions[0].name, lhsExpression.expressions[1].name];
}
return [expression.name, 'key'];
/**
* There is no key, just the value
*/
return [lhsExpression.name];
}

@@ -48,4 +70,4 @@ /**

* ```edge
* @each(user in users)
* {{ user }} {{ $loop.index }}
* @each((user, index) in users)
* {{ user }} {{ index }}
* @endeach

@@ -64,7 +86,10 @@ * ```

* We just generate the AST and do not transform it, since the transform
* function attempts to resolve identifiers
* function attempts to resolve identifiers and we don't want that
*/
const parsed = parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename).expression;
utils_1.isSubsetOf(parsed, [edge_parser_1.expressions.BinaryExpression], () => {
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid binary expression for the @each tag`, parsed, token.filename);
const { expression } = parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename);
/**
* Each tag only accepts the binary expression or sequence expression. ie `user in users`
*/
utils_1.isSubsetOf(expression, [edge_parser_1.expressions.BinaryExpression], () => {
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not valid expression for the @each tag`, token.filename, parser.utils.getExpressionLoc(expression));
});

@@ -75,11 +100,10 @@ /**

const elseIndex = token.children.findIndex((child) => edge_lexer_1.utils.isTag(child, 'else'));
const elseChild = elseIndex > -1 ? token.children.splice(elseIndex) : [];
const elseChildren = elseIndex > -1 ? token.children.splice(elseIndex) : [];
/**
* Fetching the item,index and list for the each loop
*/
const [item, index] = getLoopItemAndIndex(parsed.left, token.filename);
const list = getLoopList(parsed.right, parser, token.filename);
const list = getLoopList(expression.right, parser, token.filename);
const [item, index] = getLoopItemAndIndex(expression.left, parser, token.filename);
/**
* If there is an else statement, then wrap the loop
* inside the `if` statement first
* If there is an else statement, then wrap the loop inside the `if` statement first
*/

@@ -92,23 +116,19 @@ if (elseIndex > -1) {

*/
buffer.writeStatement(`ctx.loop(${list}, function (${item}, loop) {`, token.filename, token.loc.start.line);
const loopCallbackArgs = (index ? [item, index] : [item]).join(',');
buffer.writeStatement(`ctx.loop(${list}, function (${loopCallbackArgs}) {`, token.filename, token.loc.start.line);
/**
* Start a new context frame. Frame ensures the value inside
* the loop is given priority over top level values. Think
* of it as a Javascript block scope.
* Start a new parser scope. So that all variable resolutions for the `item`
* are pointing to the local variable and not the template `state`.
*/
buffer.writeExpression('ctx.newFrame()', token.filename, -1);
parser.stack.defineScope();
parser.stack.defineVariable(item);
index && parser.stack.defineVariable(index);
/**
* Set key and value pair on the context
* Process all children
*/
buffer.writeExpression(`ctx.setOnFrame('${item}', ${item})`, token.filename, -1);
buffer.writeExpression('ctx.setOnFrame(\'$loop\', loop)', token.filename, -1);
buffer.writeExpression(`ctx.setOnFrame('${index}', loop.key)`, token.filename, -1);
/**
* Process all kids
*/
token.children.forEach((child) => parser.processToken(child, buffer));
/**
* Remove the frame
* Clear scope
*/
buffer.writeExpression('ctx.removeFrame()', token.filename, -1);
parser.stack.clearScope();
/**

@@ -123,3 +143,3 @@ * Close each loop

if (elseIndex > -1) {
elseChild.forEach((child) => parser.processToken(child, buffer));
elseChildren.forEach((elseChild) => parser.processToken(elseChild, buffer));
buffer.writeStatement('}', token.filename, -1);

@@ -133,20 +153,6 @@ }

context.macro('loop', function loop(source, callback) {
let index = 0;
const total = lodash_1.size(source);
lodash_1.each(source, (value, key) => {
const isEven = (index + 1) % 2 === 0;
callback(value, {
key: key,
index: index,
first: index === 0,
isOdd: !isEven,
isEven: isEven,
last: (index + 1 === total),
total: total,
});
index++;
});
lodash_foreach_1.default(source, callback);
});
context.macro('size', lodash_1.size);
context.macro('size', lodash_size_1.default);
},
};
import { TagContract } from '../Contracts';
/**
* Else if tag is used to define conditional blocks.
*
* ```edge
* @if(username)
* // If
* @elseif(user.username)
* // Else if
* @endif
* ```
* Else if tag is used to define conditional blocks. We keep `@elseif` tag
* is a inline tag, so that everything between the `if` and the `elseif`
* comes `if` children.
*/
export declare const elseIfTag: TagContract;

@@ -14,11 +14,5 @@ "use strict";

/**
* Else if tag is used to define conditional blocks.
*
* ```edge
* @if(username)
* // If
* @elseif(user.username)
* // Else if
* @endif
* ```
* Else if tag is used to define conditional blocks. We keep `@elseif` tag
* is a inline tag, so that everything between the `if` and the `elseif`
* comes `if` children.
*/

@@ -33,8 +27,11 @@ exports.elseIfTag = {

compile(parser, buffer, token) {
const parsed = parser.utils.transformAst(parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), token.filename);
const parsed = utils_1.parseJsArg(parser, token);
/**
* Disallow sequence expressions
*/
utils_1.isNotSubsetOf(parsed, [edge_parser_1.expressions.SequenceExpression], () => {
utils_1.unallowedExpression(`{${token.properties.jsArg}} is not a valid argument type for the @elseif tag`, parsed, token.filename);
utils_1.unallowedExpression(`{${token.properties.jsArg}} is not a valid argument type for the @elseif tag`, token.filename, parser.utils.getExpressionLoc(parsed));
});
/**
* Start else block
* Start else if block
*/

@@ -41,0 +38,0 @@ buffer.writeStatement(`} else if (${parser.utils.stringify(parsed)}) {`, token.filename, token.loc.start.line);

import { TagContract } from '../Contracts';
/**
* If tag is used to define conditional blocks.
*
* ```edge
* @if(username)
* @endif
* ```
*/
export declare const ifTag: TagContract;

@@ -15,7 +15,2 @@ "use strict";

* If tag is used to define conditional blocks.
*
* ```edge
* @if(username)
* @endif
* ```
*/

@@ -30,5 +25,8 @@ exports.ifTag = {

compile(parser, buffer, token) {
const parsed = parser.utils.transformAst(parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), token.filename);
const parsed = utils_1.parseJsArg(parser, token);
/**
* Disallow sequence expressions
*/
utils_1.isNotSubsetOf(parsed, [edge_parser_1.expressions.SequenceExpression], () => {
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @if tag`, parsed, token.filename);
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @if tag`, token.filename, parser.utils.getExpressionLoc(parsed));
});

@@ -40,3 +38,3 @@ /**

/**
* Process of all kids recursively
* Process of all children recursively
*/

@@ -43,0 +41,0 @@ token.children.forEach((child) => parser.processToken(child, buffer));

@@ -0,3 +1,12 @@

import { Parser } from 'edge-parser';
import { TagContract } from '../Contracts';
/**
* List of expressions allowed for the include tag
*/
export declare const ALLOWED_EXPRESSION: ("Identifier" | "MemberExpression" | "CallExpression" | "Literal" | "TemplateLiteral" | "ConditionalExpression" | "LogicalExpression")[];
/**
* Returns the expression for rendering the partial
*/
export declare function getRenderExpression(parser: Parser, parsedExpression: any): string;
/**
* Include tag is used to include partials in the same scope of the parent

@@ -4,0 +13,0 @@ * template.

@@ -14,2 +14,37 @@ "use strict";

/**
* List of expressions allowed for the include tag
*/
exports.ALLOWED_EXPRESSION = [
edge_parser_1.expressions.Identifier,
edge_parser_1.expressions.Literal,
edge_parser_1.expressions.LogicalExpression,
edge_parser_1.expressions.MemberExpression,
edge_parser_1.expressions.ConditionalExpression,
edge_parser_1.expressions.CallExpression,
edge_parser_1.expressions.TemplateLiteral,
];
/**
* Returns the expression for rendering the partial
*/
function getRenderExpression(parser, parsedExpression) {
/**
* We need to pass the local variables to the partial render function
*/
const localVariables = parser.stack.list();
/**
* Arguments for the `renderInline` method
*/
const renderArgs = localVariables.length
? [parser.utils.stringify(parsedExpression), localVariables.map((localVar) => `"${localVar}"`).join(',')]
: [parser.utils.stringify(parsedExpression)];
/**
* Arguments for invoking the output function of `renderInline`
*/
const callFnArgs = localVariables.length
? ['template', 'state', 'ctx', localVariables.map((localVar) => localVar).join(',')]
: ['template', 'state', 'ctx'];
return `template.renderInline(${renderArgs.join(',')})(${callFnArgs.join(',')})`;
}
exports.getRenderExpression = getRenderExpression;
/**
* Include tag is used to include partials in the same scope of the parent

@@ -30,21 +65,11 @@ * template.

compile(parser, buffer, token) {
const parsed = parser.utils.transformAst(parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), token.filename);
utils_1.isSubsetOf(parsed, [
edge_parser_1.expressions.Identifier,
edge_parser_1.expressions.Literal,
edge_parser_1.expressions.LogicalExpression,
edge_parser_1.expressions.MemberExpression,
edge_parser_1.expressions.ConditionalExpression,
edge_parser_1.expressions.CallExpression,
edge_parser_1.expressions.TemplateLiteral,
], () => {
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @include tag`, parsed, token.filename);
});
const parsed = utils_1.parseJsArg(parser, token);
/**
* Include template. Since the partials can be a runtime value, we cannot inline
* the content right now and have to defer to runtime to get the value of
* the partial and then process it
* Only mentioned expressions are allowed inside `@include` tag
*/
buffer.outputExpression(`template.renderInline(${parser.utils.stringify(parsed)})(template, ctx)`, token.filename, token.loc.start.line, true);
utils_1.isSubsetOf(parsed, exports.ALLOWED_EXPRESSION, () => {
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @include tag`, token.filename, parser.utils.getExpressionLoc(parsed));
});
buffer.outputExpression(getRenderExpression(parser, parsed), token.filename, token.loc.start.line, false);
},
};

@@ -5,2 +5,3 @@ export { ifTag as if } from './If';

export { includeTag as include } from './Include';
export { includeIfTag as includeIf } from './IncludeIf';
export { eachTag as each } from './Each';

@@ -7,0 +8,0 @@ export { componentTag as component } from './Component';

@@ -19,2 +19,4 @@ "use strict";

exports.include = Include_1.includeTag;
var IncludeIf_1 = require("./IncludeIf");
exports.includeIf = IncludeIf_1.includeIfTag;
var Each_1 = require("./Each");

@@ -21,0 +23,0 @@ exports.each = Each_1.eachTag;

@@ -40,3 +40,3 @@ "use strict";

compile(parser, buffer, token) {
const parsed = parser.utils.transformAst(parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), token.filename);
const parsed = utils_1.parseJsArg(parser, token);
/**

@@ -46,3 +46,3 @@ * The set tag only accepts a sequence expression.

utils_1.isSubsetOf(parsed, [edge_parser_1.expressions.SequenceExpression], () => {
throw utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid key-value pair for the @slot tag`, parsed, token.filename);
throw utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid key-value pair for the @slot tag`, token.filename, parser.utils.getExpressionLoc(parsed));
});

@@ -52,4 +52,4 @@ /**

*/
if (parsed.expressions.length > 3) {
throw new edge_error_1.EdgeError('maximum of 3 arguments are allowed for the @set tag', 'E_MAX_ARGUMENTS', {
if (parsed.expressions.length > 2) {
throw new edge_error_1.EdgeError('maximum of 2 arguments are allowed for the @set tag', 'E_MAX_ARGUMENTS', {
line: parsed.loc.start.line,

@@ -60,3 +60,3 @@ col: parsed.loc.start.column,

}
const [key, value, isolated] = parsed.expressions;
const [key, value] = parsed.expressions;
/**

@@ -66,9 +66,16 @@ * The key has to be a literal value

utils_1.isSubsetOf(key, [edge_parser_1.expressions.Literal], () => {
throw utils_1.unallowedExpression('The first argument for @set tag must be a string literal', parsed, token.filename);
throw utils_1.unallowedExpression('The first argument for @set tag must be a string literal', token.filename, parser.utils.getExpressionLoc(key));
});
/**
* Write statement to mutate the key
* Write statement to mutate the key. If the variable has already been
* defined, then just update it's value.
*
* We do not allow re-declaring a variable as of now
*/
buffer.writeExpression(`ctx.set(${key.raw}, ${parser.utils.stringify(value)}, ${!!isolated})`, token.filename, token.loc.start.line);
const expression = parser.stack.has(key.value)
? `${key.value} = ${parser.utils.stringify(value)}`
: `let ${key.value} = ${parser.utils.stringify(value)}`;
buffer.writeExpression(expression, token.filename, token.loc.start.line);
parser.stack.defineVariable(key.value);
},
};

@@ -31,5 +31,8 @@ "use strict";

compile(parser, buffer, token) {
const parsed = parser.utils.transformAst(parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), token.filename);
const parsed = utils_1.parseJsArg(parser, token);
/**
* Disallow sequence expressions
*/
utils_1.isNotSubsetOf(parsed, [edge_parser_1.expressions.SequenceExpression], () => {
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @unless tag`, parsed, token.filename);
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @unless tag`, token.filename, parser.utils.getExpressionLoc(parsed));
});

@@ -41,3 +44,3 @@ /**

/**
* Process of all kids recursively
* Process of all children recursively
*/

@@ -44,0 +47,0 @@ token.children.forEach((child) => parser.processToken(child, buffer));

@@ -40,11 +40,26 @@ "use strict";

compile(parser, buffer, token) {
/**
* Holding the yield variable counder on the buffer as a private
* variable
*/
let yieldCounter = buffer['yieldCounter'] || 0;
buffer['yieldCounter'] = yieldCounter++;
const parsed = parser.utils.transformAst(parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), token.filename);
const parsed = utils_1.parseJsArg(parser, token);
/**
* Sequence expression is not
*/
utils_1.isNotSubsetOf(parsed, [edge_parser_1.expressions.SequenceExpression], () => {
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @yield tag`, parsed, token.filename);
utils_1.unallowedExpression(`"${token.properties.jsArg}" is not a valid argument type for the @yield tag`, token.filename, parser.utils.getExpressionLoc(parsed));
});
const parsedString = parser.utils.stringify(parsed);
/**
* Write main content when it's truthy
* Write main content when it's truthy. The reason we store a reference to a variable first, is that
* at times the properties can have side-effects, so calling it inside `if` and then yield may
* cause unintended behavior. For example:
*
* `@yield(getPropertyAge())`
*
* The `getPropertyAge` uses timestamp comparsion for some logic. So if we will call this method
* twice, first inside the `if` block and then to yield it, then it may cause some unintended
* behavior.
*/

@@ -55,3 +70,3 @@ buffer.writeExpression(`let yield_${yieldCounter} = ${parsedString}`, token.filename, token.loc.start.line);

/**
* Else write fallback
* Write fallback content
*/

@@ -58,0 +73,0 @@ if (!token.properties.selfclosed) {

@@ -16,2 +16,10 @@ import { CompilerContract } from '../Contracts';

/**
* Wraps template to a function
*/
private wrapToFunction;
/**
* Trims top and bottom new lines from the content
*/
trimTopBottomNewLines(value: any): any;
/**
* Render the template inline by sharing the state of the current template.

@@ -23,6 +31,6 @@ *

* // render and use output
* partialFn(template, ctx)
* partialFn(template, state, ctx)
* ```
*/
renderInline(templatePath: string): Function;
renderInline(templatePath: string, ...localVariables: string[]): Function;
/**

@@ -29,0 +37,0 @@ * Renders the template with custom state. The `sharedState` of the template is still

@@ -10,6 +10,8 @@ "use strict";

*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const lodash_merge_1 = __importDefault(require("lodash.merge"));
const Context_1 = require("../Context");
const Presenter_1 = require("../Presenter");
/**

@@ -23,5 +25,18 @@ * The template is used to compile and run templates. Also the instance

this.compiler = compiler;
this.sharedState = lodash_1.merge({}, globals, locals);
this.sharedState = lodash_merge_1.default({}, globals, locals);
}
/**
* Wraps template to a function
*/
wrapToFunction(template, ...localVariables) {
const args = ['template', 'state', 'ctx'].concat(localVariables);
return new Function('', `return function template (${args.join(',')}) { ${template} }`)();
}
/**
* Trims top and bottom new lines from the content
*/
trimTopBottomNewLines(value) {
return value.replace(/^\n|^\r\n/, '').replace(/\n$|\r\n$/, '');
}
/**
* Render the template inline by sharing the state of the current template.

@@ -33,7 +48,8 @@ *

* // render and use output
* partialFn(template, ctx)
* partialFn(template, state, ctx)
* ```
*/
renderInline(templatePath) {
return new Function('template', 'ctx', this.compiler.compile(templatePath, true).template);
renderInline(templatePath, ...localVariables) {
const { template: compiledTemplate } = this.compiler.compile(templatePath, localVariables);
return this.wrapToFunction(compiledTemplate, ...localVariables);
}

@@ -52,6 +68,5 @@ /**

renderWithState(template, state, slots) {
const { template: compiledTemplate, Presenter } = this.compiler.compile(template, false);
const presenter = new (Presenter || Presenter_1.Presenter)(lodash_1.merge(state, { $slots: slots }), this.sharedState);
const ctx = new Context_1.Context(presenter);
return new Function('template', 'ctx', compiledTemplate)(this, ctx);
const { template: compiledTemplate } = this.compiler.compile(template);
const templateState = Object.assign({}, this.sharedState, state, { $slots: slots });
return this.wrapToFunction(compiledTemplate)(this, templateState, new Context_1.Context());
}

@@ -66,8 +81,8 @@ /**

render(template, state) {
const { template: compiledTemplate, Presenter } = this.compiler.compile(template, false);
const presenter = new (Presenter || Presenter_1.Presenter)(state, this.sharedState);
const ctx = new Context_1.Context(presenter);
return new Function('template', 'ctx', compiledTemplate)(this, ctx);
const { template: compiledTemplate } = this.compiler.compile(template);
const templateState = Object.assign({}, this.sharedState, state);
const fn = this.wrapToFunction(compiledTemplate);
return this.trimTopBottomNewLines(fn(this, templateState, new Context_1.Context()));
}
}
exports.Template = Template;

@@ -1,2 +0,3 @@

import { expressions as expressionsList } from 'edge-parser';
import { TagToken } from 'edge-lexer';
import { expressions as expressionsList, Parser } from 'edge-parser';
declare type ExpressionList = readonly (keyof typeof expressionsList)[];

@@ -7,3 +8,6 @@ /**

*/
export declare function unallowedExpression(message: string, expression: any, filename: string): void;
export declare function unallowedExpression(message: string, filename: string, loc: {
line: number;
col: number;
}): void;
/**

@@ -31,2 +35,6 @@ * Validates the expression type to be part of the allowed

export declare function isNotSubsetOf(expression: any, expressions: ExpressionList, errorCallback: () => void): void;
/**
* Parses the jsArg by generating and transforming its AST
*/
export declare function parseJsArg(parser: Parser, token: TagToken): any;
export {};

@@ -16,6 +16,6 @@ "use strict";

*/
function unallowedExpression(message, expression, filename) {
function unallowedExpression(message, filename, loc) {
throw new edge_error_1.EdgeError(message, 'E_UNALLOWED_EXPRESSION', {
line: expression.loc.start.line,
col: expression.loc.start.column,
line: loc.line,
col: loc.col,
filename: filename,

@@ -57,1 +57,8 @@ });

exports.isNotSubsetOf = isNotSubsetOf;
/**
* Parses the jsArg by generating and transforming its AST
*/
function parseJsArg(parser, token) {
return parser.utils.transformAst(parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), token.filename, parser);
}
exports.parseJsArg = parseJsArg;
{
"name": "edge.js",
"version": "2.2.0",
"version": "3.0.0",
"description": "Template engine",

@@ -35,7 +35,6 @@ "main": "build/index.js",

"devDependencies": {
"@adonisjs/mrm-preset": "^2.2.4",
"@poppinss/dev-utils": "^1.0.4",
"@types/lodash": "^4.14.149",
"@types/node": "^13.9.0",
"commitizen": "^4.0.3",
"@adonisjs/mrm-preset": "^2.3.0",
"@poppinss/dev-utils": "^1.0.6",
"@types/node": "^13.11.1",
"commitizen": "^4.0.4",
"cz-conventional-changelog": "^3.1.0",

@@ -46,15 +45,10 @@ "dedent-js": "^1.0.1",

"eslint": "^6.8.0",
"eslint-plugin-adonis": "^1.0.8",
"fs-extra": "^8.1.0",
"husky": "^4.2.3",
"eslint-plugin-adonis": "^1.0.9",
"husky": "^4.2.5",
"japa": "^3.0.1",
"mrm": "^2.1.0",
"np": "^5.2.1",
"ts-node": "^8.6.2",
"typedoc": "^0.16.11",
"typedoc-clarity-theme": "^1.1.0",
"typedoc-plugin-external-module-name": "^3.0.0",
"typescript": "^3.8.3",
"yorkie": "^2.0.0",
"youch": "^2.0.10"
"js-stringify": "^1.0.2",
"mrm": "^2.2.1",
"np": "^6.2.1",
"ts-node": "^8.8.2",
"typescript": "^3.8.3"
},

@@ -75,14 +69,12 @@ "config": {

"dependencies": {
"@poppinss/utils": "^2.1.2",
"edge-error": "^1.0.4",
"edge-lexer": "^2.1.0",
"edge-parser": "^3.0.8",
"edge-lexer": "^3.0.3",
"edge-parser": "^5.0.6",
"he": "^1.2.0",
"import-fresh": "^3.2.1",
"lodash": "^4.17.15",
"macroable": "^4.0.2"
"lodash.foreach": "^4.5.0",
"lodash.merge": "^4.6.2",
"lodash.size": "^4.2.0",
"macroable": "^4.0.4",
"truncatise": "0.0.8"
},
"gitHooks": {
"commit-msg": "node ./node_modules/@adonisjs/mrm-preset/validateCommit/conventional/validate.js"
},
"husky": {

@@ -89,0 +81,0 @@ "hooks": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc