Socket
Socket
Sign inDemoInstall

edge-lexer

Package Overview
Dependencies
Maintainers
1
Versions
62
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

edge-lexer - npm Package Compare versions

Comparing version 2.0.10 to 2.0.11

11

build/index.d.ts

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

/**
* @module lexer
*/
/**
* edge-lexer
*
* (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.
*/
export { Tokenizer } from './src/Tokenizer/index';
export { Tags, TagToken, MustacheToken, NewLineToken, RawToken, Token, LexerTagDefinitionContract, MustacheTypes, TagTypes, TagProps, MustacheProps, } from './src/Contracts/index';
"use strict";
/**
* @module lexer
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* edge-lexer
*
* (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 index_1 = require("./src/Tokenizer/index");

@@ -4,0 +15,0 @@ exports.Tokenizer = index_1.Tokenizer;

@@ -0,1 +1,15 @@

/**
* @module lexer
*/
/**
* edge-lexer
*
* (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.
*/
/**
* Types for mustache statements
*/
export declare enum MustacheTypes {

@@ -7,2 +21,6 @@ SMUSTACHE = "s__mustache",

}
/**
* The type of node types. Each token
* will have one of these types
*/
export declare enum TagTypes {

@@ -12,2 +30,5 @@ TAG = "tag",

}
/**
* Properties node for a tag
*/
export declare type TagProps = {

@@ -18,5 +39,11 @@ name: string;

};
/**
* Properties for a mustache block
*/
export declare type MustacheProps = {
jsArg: string;
};
/**
* Location node for tags and mustache braces
*/
export declare type LexerLoc = {

@@ -32,2 +59,6 @@ start: {

};
/**
* The properties required by the lexer on a tag
* definition
*/
export interface LexerTagDefinitionContract {

@@ -37,2 +68,5 @@ block: boolean;

}
/**
* Raw line token
*/
export declare type RawToken = {

@@ -43,2 +77,5 @@ type: 'raw';

};
/**
* New line token
*/
export declare type NewLineToken = {

@@ -48,2 +85,5 @@ type: 'newline';

};
/**
* Mustache token
*/
export declare type MustacheToken = {

@@ -54,2 +94,5 @@ type: MustacheTypes;

};
/**
* Tag token
*/
export declare type TagToken = {

@@ -62,2 +105,5 @@ type: TagTypes;

export declare type Token = RawToken | NewLineToken | TagToken | MustacheToken;
/**
* The runtime tag node to know the shape of a tag
*/
export declare type RuntimeTag = {

@@ -73,2 +119,5 @@ name: string;

};
/**
* Runtime mustache node to know the shape of the mustache
*/
export declare type RuntimeMustache = {

@@ -75,0 +124,0 @@ escaped: boolean;

"use strict";
/**
* @module lexer
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* edge-lexer
*
* (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.
*/
/**
* Types for mustache statements
*/
var MustacheTypes;

@@ -10,2 +24,6 @@ (function (MustacheTypes) {

})(MustacheTypes = exports.MustacheTypes || (exports.MustacheTypes = {}));
/**
* The type of node types. Each token
* will have one of these types
*/
var TagTypes;

@@ -12,0 +30,0 @@ (function (TagTypes) {

@@ -0,3 +1,20 @@

/**
* @module lexer
*/
/**
* edge-lexer
*
* (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.
*/
import { RuntimeTag, RuntimeMustache, Tags } from '../Contracts';
/**
* Returns runtime tag node if tag is detected and is a registered tag
*/
export declare function getTag(content: string, line: number, col: number, tags: Tags): RuntimeTag | null;
/**
* Returns the runtime mustache node if mustache is detected
*/
export declare function getMustache(content: string, line: number, col: number): RuntimeMustache | null;
"use strict";
/**
* @module lexer
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* The only regex we need in the entire lexer. Also tested
* with https://github.com/substack/safe-regex
*/
const TAG_REGEX = /^(\s*)(@{1,2})(!)?(\w+)(\s{0,2})/;
/**
* Returns runtime tag node if tag is detected and is a registered tag
*/
function getTag(content, line, col, tags) {
const match = TAG_REGEX.exec(content);
/**
* Return when their is no match
*/
if (!match) {

@@ -11,2 +24,5 @@ return null;

const tag = tags[name];
/**
* Return when not a registered tag
*/
if (!tag) {

@@ -21,2 +37,5 @@ return null;

const block = tag.block;
/**
* Advanced the col position
*/
col += whitespaceLeft + match[2].length + name.length + whitespaceRight;

@@ -26,2 +45,5 @@ if (selfclosed) {

}
/**
* Seekable tags without the brace in same line are invalid
*/
const hasBrace = seekable && content[col] === '(';

@@ -40,2 +62,5 @@ return {

exports.getTag = getTag;
/**
* Returns the runtime mustache node if mustache is detected
*/
function getMustache(content, line, col) {

@@ -42,0 +67,0 @@ const mustacheIndex = content.indexOf('{{');

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

/**
* @module lexer
*/
/**
* edge-lexer
*
* (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.
*/
import { EdgeError } from 'edge-error';
/**
* Raised when there is inline content next to a tag opening
* block. For example:
*
* Incorrect
* ```
* @if(username) Hello {{ username }} @endif
* ```
*
* Correct
* ```
* @if(username)
* Hello {{ username }}
* @endif
* ```
*/
export declare function cannotSeekStatement(chars: string, pos: {

@@ -6,2 +33,15 @@ line: number;

}, filename: string): EdgeError;
/**
* Raised when a tag opening body doesn't have a closing brace. For example:
*
* Incorrect
* ```
* @if(username
* ```
*
* Correct
* ```
* @if(username)
* ```
*/
export declare function unclosedParen(pos: {

@@ -11,2 +51,15 @@ line: number;

}, filename: string): EdgeError;
/**
* Raised when a tag is used without an opening brace. For example:
*
* Incorrect
* ```
* @if username
* ```
*
* Correct
* ```
* @if(username)
* ```
*/
export declare function unopenedParen(pos: {

@@ -16,2 +69,17 @@ line: number;

}, filename: string): EdgeError;
/**
* Raised when the curly closing brace is missing from the mustache
* statement. For example:
*
* Incorrect
* ```
* {{ username }
* ```
*
* Correct
*
* ```
* {{ username }}
* ```
*/
export declare function unclosedCurlyBrace(pos: {

@@ -21,2 +89,16 @@ line: number;

}, filename: string): EdgeError;
/**
* Raised when a block level tag is opened but never closed. For example:
*
* Incorrect
* ```
* @if(username)
* ```
*
* Correct
* ```
* @if(username)
* @endif
* ```
*/
export declare function unclosedTag(tag: string, pos: {

@@ -23,0 +105,0 @@ line: number;

88

build/src/Exceptions/index.js
"use strict";
/**
* @module lexer
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* edge-lexer
*
* (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.
*/
const edge_error_1 = require("edge-error");
/**
* Raised when there is inline content next to a tag opening
* block. For example:
*
* Incorrect
* ```
* @if(username) Hello {{ username }} @endif
* ```
*
* Correct
* ```
* @if(username)
* Hello {{ username }}
* @endif
* ```
*/
function cannotSeekStatement(chars, pos, filename) {

@@ -12,4 +39,17 @@ return new edge_error_1.EdgeError(`Unexpected token "${chars}"`, 'E_CANNOT_SEEK_STATEMENT', {

exports.cannotSeekStatement = cannotSeekStatement;
/**
* Raised when a tag opening body doesn't have a closing brace. For example:
*
* Incorrect
* ```
* @if(username
* ```
*
* Correct
* ```
* @if(username)
* ```
*/
function unclosedParen(pos, filename) {
return new edge_error_1.EdgeError(`Missing token ")"`, 'E_UNCLOSED_PAREN', {
return new edge_error_1.EdgeError('Missing token ")"', 'E_UNCLOSED_PAREN', {
line: pos.line,

@@ -21,4 +61,17 @@ col: pos.col,

exports.unclosedParen = unclosedParen;
/**
* Raised when a tag is used without an opening brace. For example:
*
* Incorrect
* ```
* @if username
* ```
*
* Correct
* ```
* @if(username)
* ```
*/
function unopenedParen(pos, filename) {
return new edge_error_1.EdgeError(`Missing token "("`, 'E_UNOPENED_PAREN', {
return new edge_error_1.EdgeError('Missing token "("', 'E_UNOPENED_PAREN', {
line: pos.line,

@@ -30,4 +83,19 @@ col: pos.col,

exports.unopenedParen = unopenedParen;
/**
* Raised when the curly closing brace is missing from the mustache
* statement. For example:
*
* Incorrect
* ```
* {{ username }
* ```
*
* Correct
*
* ```
* {{ username }}
* ```
*/
function unclosedCurlyBrace(pos, filename) {
return new edge_error_1.EdgeError(`Missing token "}"`, 'E_UNCLOSED_CURLY_BRACE', {
return new edge_error_1.EdgeError('Missing token "}"', 'E_UNCLOSED_CURLY_BRACE', {
line: pos.line,

@@ -39,2 +107,16 @@ col: pos.col,

exports.unclosedCurlyBrace = unclosedCurlyBrace;
/**
* Raised when a block level tag is opened but never closed. For example:
*
* Incorrect
* ```
* @if(username)
* ```
*
* Correct
* ```
* @if(username)
* @endif
* ```
*/
function unclosedTag(tag, pos, filename) {

@@ -41,0 +123,0 @@ return new edge_error_1.EdgeError(`Unclosed tag ${tag}`, 'E_UNCLOSED_TAG', {

@@ -0,11 +1,58 @@

/**
* @module lexer
*/
/**
* edge-lexer
*
* (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.
*/
/**
* Scan a string and seperate it into 2 pairs. The first pair will be series
* of characters until the ending pattern is found and 2nd pair is the
* left over.
*
* Their are some special behaviors over the regular `string.split` method.
*
* 1. Multiple lines can be passed by calling `scan` method for each line.
* 2. Tolerates characters when they conflict with the ending pattern.
*
* ```js
* const pattern = ')'
* const tolerations = ['(', ')']
* const scanner = new Scanner(pattern, tolerations)
*
* scanner.scan('2 + 2 * (3))')
* if (scanner.closed) {
* scanner.match // 2 + 2 * (3)
* scanner.leftOver // ''
* }
* ```
*
* If we take the same string `2 + 2 * (3))` and split it using ')', then we
* will get unexpected result, since the split method splits by finding the
* first match.
*/
export declare class Scanner {
private _pattern;
private _line;
private _col;
private _tolaretionCounts;
private _tolerateLhs;
private _tolerateRhs;
private _patternLength;
private pattern;
private line;
private col;
private tolaretionCounts;
private tolerateLhs;
private tolerateRhs;
private patternLength;
/**
* Tracking if the scanner has been closed
*/
closed: boolean;
/**
* The matched content within the pattern
*/
match: string;
/**
* The content in the same line but after the closing
* of the pattern
*/
leftOver: string;

@@ -16,5 +63,18 @@ loc: {

};
constructor(_pattern: string, _toleratePair: [string, string], _line: number, _col: number);
private _matchesPattern;
scan(chunk: any): void;
constructor(pattern: string, toleratePair: [string, string], line: number, col: number);
/**
* Returns a boolean telling if the pattern matches the current
* char and the upcoming chars or not.
*
* This will be used to mark the scanner as closed and stop scanning
* for more chars
*/
private matchesPattern;
/**
* Scan a string and look for the closing pattern. The string will
* be seperated with the closing pattern and also tracks the
* toleration patterns to make sure they are not making the
* scanner to end due to pattern mis-match.
*/
scan(chunk: string): void;
}
"use strict";
/**
* @module lexer
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* edge-lexer
*
* (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.
*/
/**
* Scan a string and seperate it into 2 pairs. The first pair will be series
* of characters until the ending pattern is found and 2nd pair is the
* left over.
*
* Their are some special behaviors over the regular `string.split` method.
*
* 1. Multiple lines can be passed by calling `scan` method for each line.
* 2. Tolerates characters when they conflict with the ending pattern.
*
* ```js
* const pattern = ')'
* const tolerations = ['(', ')']
* const scanner = new Scanner(pattern, tolerations)
*
* scanner.scan('2 + 2 * (3))')
* if (scanner.closed) {
* scanner.match // 2 + 2 * (3)
* scanner.leftOver // ''
* }
* ```
*
* If we take the same string `2 + 2 * (3))` and split it using ')', then we
* will get unexpected result, since the split method splits by finding the
* first match.
*/
class Scanner {
constructor(_pattern, _toleratePair, _line, _col) {
this._pattern = _pattern;
this._line = _line;
this._col = _col;
this._tolaretionCounts = 0;
this._tolerateLhs = '';
this._tolerateRhs = '';
this._patternLength = this._pattern.length;
constructor(pattern, toleratePair, line, col) {
this.pattern = pattern;
this.line = line;
this.col = col;
this.tolaretionCounts = 0;
this.tolerateLhs = '';
this.tolerateRhs = '';
this.patternLength = this.pattern.length;
/**
* Tracking if the scanner has been closed
*/
this.closed = false;
/**
* The matched content within the pattern
*/
this.match = '';
/**
* The content in the same line but after the closing
* of the pattern
*/
this.leftOver = '';
this.loc = {
line: this._line,
col: this._col,
line: this.line,
col: this.col,
};
this._tolerateLhs = _toleratePair[0];
this._tolerateRhs = _toleratePair[1];
this.tolerateLhs = toleratePair[0];
this.tolerateRhs = toleratePair[1];
}
_matchesPattern(chars, iterationCount) {
for (let i = 0; i < this._patternLength; i++) {
if (this._pattern[i] !== chars[iterationCount + i]) {
/**
* Returns a boolean telling if the pattern matches the current
* char and the upcoming chars or not.
*
* This will be used to mark the scanner as closed and stop scanning
* for more chars
*/
matchesPattern(chars, iterationCount) {
for (let i = 0; i < this.patternLength; i++) {
if (this.pattern[i] !== chars[iterationCount + i]) {
return false;

@@ -30,2 +84,8 @@ }

}
/**
* Scan a string and look for the closing pattern. The string will
* be seperated with the closing pattern and also tracks the
* toleration patterns to make sure they are not making the
* scanner to end due to pattern mis-match.
*/
scan(chunk) {

@@ -45,16 +105,36 @@ if (chunk === '\n') {

const char = chunk[iterations];
if (this._tolaretionCounts === 0 && this._matchesPattern(chunk, iterations)) {
iterations += this._patternLength;
/**
* Toleration count is 0 and closing pattern matches the current
* or series of upcoming characters
*/
if (this.tolaretionCounts === 0 && this.matchesPattern(chunk, iterations)) {
iterations += this.patternLength;
this.closed = true;
break;
}
if (char === this._tolerateLhs) {
this._tolaretionCounts++;
/**
* Increments the tolarate counts when char is the
* tolerate lhs character
*/
if (char === this.tolerateLhs) {
this.tolaretionCounts++;
}
if (char === this._tolerateRhs) {
this._tolaretionCounts--;
/**
* Decrements the tolare counts when char is the
* tolerate rhs character
*/
if (char === this.tolerateRhs) {
this.tolaretionCounts--;
}
/**
* Append to the matched string and waiting for the
* closing pattern
*/
this.match += char;
iterations++;
}
/**
* If closed, then return the matched string and also the
* left over string
*/
if (this.closed) {

@@ -61,0 +141,0 @@ this.loc.col += iterations;

@@ -1,31 +0,131 @@

import { Tags, Token } from '../Contracts';
/**
* @module lexer
*/
import { Scanner } from '../Scanner';
import { Tags, RuntimeTag, RuntimeMustache, Token } from '../Contracts';
/**
* Tokenizer converts a bunch of text into an array of tokens. Later
* these tokens can be used to build the transformed text.
*
* Go through the README file to learn more about the syntax and
* the tokens output.
*/
export declare class Tokenizer {
private _template;
private _tagsDef;
private _options;
private template;
private tagsDef;
private options;
tokens: Token[];
private _tagStatement;
private _mustacheStatement;
private _line;
private _openedTags;
private _skipNewLine;
constructor(_template: string, _tagsDef: Tags, _options: {
/**
* Holds the current tag statement, until it is closed
*/
tagStatement: null | {
scanner: Scanner;
tag: RuntimeTag;
};
/**
* Holds the current tag statement, until it is closed
*/
mustacheStatement: null | {
scanner: Scanner;
mustache: RuntimeMustache;
};
/**
* Current line number
*/
private line;
/**
* An array of opened block level tags
*/
private openedTags;
/**
* We skip newlines after the opening/closing tags
*/
private skipNewLine;
constructor(template: string, tagsDef: Tags, options: {
filename: string;
});
private _getRawNode;
private _getNewLineNode;
private _getTagNode;
private _consumeTag;
private _handleTagOpening;
private _feedCharsToCurrentTag;
private _getMustacheType;
/**
* Returns the raw token
*/
private getRawNode;
/**
* Returns the new line token
*/
private getNewLineNode;
/**
* Returns the TagToken for a runtime tag. The `jsArg` and ending
* loc is computed using the scanner and must be passed to this
* method.
*/
private getTagNode;
/**
* Consume the runtime tag node.
*
* If tag is `block`, then we push it to the list of
* opened tags and wait for the closing statement to
* appear.
*
* Otherwise, we move it to the tokens array directly.
*/
private consumeTag;
/**
* Handles the opening of the tag.
*/
private handleTagOpening;
/**
* Scans the string using the scanner and waits for the
* closing brace ')' to appear
*/
private feedCharsToCurrentTag;
/**
* Returns the mustache type by checking for `safe` and `escaped`
* properties.
*/
private getMustacheType;
/**
* Returns the mustache token using the runtime mustache node. The `jsArg` and
* ending `loc` is fetched using the scanner.
*/
private _getMustacheNode;
private _handleMustacheOpening;
private _feedCharsToCurrentMustache;
private _isClosingTag;
private _consumeNode;
private _pushNewLine;
private _processText;
private _checkForErrors;
/**
* Handles the line which has mustache opening braces.
*/
private handleMustacheOpening;
/**
* Feed chars to the mustache statement, which isn't closed yet.
*/
private feedCharsToCurrentMustache;
/**
* Returns a boolean telling if the content of the line is the
* closing tag for the most recently opened tag.
*
* The opening and closing has to be in a order, otherwise the
* compiler will get mad.
*/
private isClosingTag;
/**
* Consume any type of token by moving it to the correct list. If there are
* opened tags, then the token becomes part of the tag children. Otherwise
* moved as top level token.
*/
private consumeNode;
/**
* Pushes a new line to the list. This method avoids
* new lines at position 0.
*/
private pushNewLine;
/**
* Process the current line based upon what it is. What it is?
* That's the job of this method to find out.
*/
private processText;
/**
* Checks for errors after the tokenizer completes it's work, so that we
* can find broken statements or unclosed tags.
*/
private checkForErrors;
/**
* Parse the template and generate an AST out of it
*/
parse(): void;
}

372

build/src/Tokenizer/index.js
"use strict";
/**
* @module lexer
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* edge-lexer
*
* (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.
*/
const Detector_1 = require("../Detector");

@@ -7,28 +18,61 @@ const Scanner_1 = require("../Scanner");

const Contracts_1 = require("../Contracts");
/**
* Tokenizer converts a bunch of text into an array of tokens. Later
* these tokens can be used to build the transformed text.
*
* Go through the README file to learn more about the syntax and
* the tokens output.
*/
class Tokenizer {
constructor(_template, _tagsDef, _options) {
this._template = _template;
this._tagsDef = _tagsDef;
this._options = _options;
constructor(template, tagsDef, options) {
this.template = template;
this.tagsDef = tagsDef;
this.options = options;
this.tokens = [];
this._tagStatement = null;
this._mustacheStatement = null;
this._line = 0;
this._openedTags = [];
this._skipNewLine = false;
/**
* Holds the current tag statement, until it is closed
*/
this.tagStatement = null;
/**
* Holds the current tag statement, until it is closed
*/
this.mustacheStatement = null;
/**
* Current line number
*/
this.line = 0;
/**
* An array of opened block level tags
*/
this.openedTags = [];
/**
* We skip newlines after the opening/closing tags
*/
this.skipNewLine = false;
}
_getRawNode(text) {
/**
* Returns the raw token
*/
getRawNode(text) {
return {
type: 'raw',
value: text,
line: this._line,
line: this.line,
};
}
_getNewLineNode() {
/**
* Returns the new line token
*/
getNewLineNode() {
return {
type: 'newline',
line: this._line - 1,
line: this.line - 1,
};
}
_getTagNode(tag, jsArg, closingLoc) {
/**
* Returns the TagToken for a runtime tag. The `jsArg` and ending
* loc is computed using the scanner and must be passed to this
* method.
*/
getTagNode(tag, jsArg, closingLoc) {
return {

@@ -51,38 +95,85 @@ type: tag.escaped ? Contracts_1.TagTypes.ETAG : Contracts_1.TagTypes.TAG,

}
_consumeTag(tag, jsArg, loc) {
/**
* Consume the runtime tag node.
*
* If tag is `block`, then we push it to the list of
* opened tags and wait for the closing statement to
* appear.
*
* Otherwise, we move it to the tokens array directly.
*/
consumeTag(tag, jsArg, loc) {
if (tag.block && !tag.selfclosed) {
this._openedTags.push(this._getTagNode(tag, jsArg, loc));
this.openedTags.push(this.getTagNode(tag, jsArg, loc));
}
else {
this._consumeNode(this._getTagNode(tag, jsArg, loc));
this.consumeNode(this.getTagNode(tag, jsArg, loc));
}
}
_handleTagOpening(line, tag) {
/**
* Handles the opening of the tag.
*/
handleTagOpening(line, tag) {
if (tag.seekable && !tag.hasBrace) {
throw Exceptions_1.unopenedParen({ line: tag.line, col: tag.col }, this._options.filename);
throw Exceptions_1.unopenedParen({ line: tag.line, col: tag.col }, this.options.filename);
}
/**
* When tag is not seekable, then their is no need to create
* a scanner instance, just consume it right away.
*/
if (!tag.seekable) {
this._consumeTag(tag, '', { line: tag.line, col: tag.col });
this.consumeTag(tag, '', { line: tag.line, col: tag.col });
return;
}
/**
* Advance the `col`, since we do not want to start from the
* starting brace `(`.
*/
tag.col += 1;
this._tagStatement = {
/**
* Create a new block statement with the scanner to find
* the closing brace ')'
*/
this.tagStatement = {
tag: tag,
scanner: new Scanner_1.Scanner(')', ['(', ')'], this._line, tag.col),
scanner: new Scanner_1.Scanner(')', ['(', ')'], this.line, tag.col),
};
this._feedCharsToCurrentTag(line.slice(tag.col));
/**
* Pass all remaining content to the scanner
*/
this.feedCharsToCurrentTag(line.slice(tag.col));
}
_feedCharsToCurrentTag(content) {
const { tag, scanner } = this._tagStatement;
/**
* Scans the string using the scanner and waits for the
* closing brace ')' to appear
*/
feedCharsToCurrentTag(content) {
const { tag, scanner } = this.tagStatement;
scanner.scan(content);
/**
* If scanner is not closed, then we need to keep on
* feeding more content
*/
if (!scanner.closed) {
return;
}
this._consumeTag(tag, scanner.match, scanner.loc);
/**
* Consume the tag once we have found the closing brace and set
* block statement to null
*/
this.consumeTag(tag, scanner.match, scanner.loc);
/**
* Raise error, if there is inline content after the closing brace ')'
* `@if(username) hello {{ username }}` is invalid
*/
if (scanner.leftOver.trim()) {
throw Exceptions_1.cannotSeekStatement(scanner.leftOver, scanner.loc, this._options.filename);
throw Exceptions_1.cannotSeekStatement(scanner.leftOver, scanner.loc, this.options.filename);
}
this._tagStatement = null;
this.tagStatement = null;
}
_getMustacheType(mustache) {
/**
* Returns the mustache type by checking for `safe` and `escaped`
* properties.
*/
getMustacheType(mustache) {
if (mustache.safe) {

@@ -93,5 +184,9 @@ return mustache.escaped ? Contracts_1.MustacheTypes.ESMUSTACHE : Contracts_1.MustacheTypes.SMUSTACHE;

}
/**
* Returns the mustache token using the runtime mustache node. The `jsArg` and
* ending `loc` is fetched using the scanner.
*/
_getMustacheNode(mustache, jsArg, closingLoc) {
return {
type: this._getMustacheType(mustache),
type: this.getMustacheType(mustache),
properties: {

@@ -109,43 +204,102 @@ jsArg: jsArg,

}
_handleMustacheOpening(line, mustache) {
/**
* Handles the line which has mustache opening braces.
*/
handleMustacheOpening(line, mustache) {
const pattern = mustache.safe ? '}}}' : '}}';
const textLeftIndex = mustache.escaped ? mustache.realCol - 1 : mustache.realCol;
/**
* Pull everything that is on the left of the mustache
* statement and use it as a raw node
*/
if (textLeftIndex > 0) {
this._consumeNode(this._getRawNode(line.slice(0, textLeftIndex)));
this.consumeNode(this.getRawNode(line.slice(0, textLeftIndex)));
}
/**
* Skip the curly braces when reading the expression inside
* it. We are actually skipping opening curly braces
* `{{`, however, their length will be same as the
* closing one's/
*/
mustache.col += pattern.length;
mustache.realCol += pattern.length;
this._mustacheStatement = {
/**
* Create a new mustache statement with a scanner to scan for
* closing mustache braces. Note the closing `pattern` is
* different for safe and normal mustache.
*/
this.mustacheStatement = {
mustache,
scanner: new Scanner_1.Scanner(pattern, ['{', '}'], mustache.line, mustache.col),
};
this._feedCharsToCurrentMustache(line.slice(mustache.realCol));
/**
* Feed text to the mustache statement and wait for the closing braces
*/
this.feedCharsToCurrentMustache(line.slice(mustache.realCol));
}
_feedCharsToCurrentMustache(content) {
const { mustache, scanner } = this._mustacheStatement;
/**
* Feed chars to the mustache statement, which isn't closed yet.
*/
feedCharsToCurrentMustache(content) {
const { mustache, scanner } = this.mustacheStatement;
scanner.scan(content);
/**
* If scanner is not closed, then return early, since their
* not much we can do here.
*/
if (!scanner.closed) {
return;
}
this._consumeNode(this._getMustacheNode(mustache, scanner.match, scanner.loc));
/**
* Consume the node as soon as we have found the closing brace
*/
this.consumeNode(this._getMustacheNode(mustache, scanner.match, scanner.loc));
/**
* If their is leftOver text after the mustache closing brace, then re-scan
* it for more mustache statements. Example:
*
* I following statement, `, and {{ age }}` is the left over.
* ```
* {{ username }}, and {{ age }}
* ```
*
* This block is same the generic new line handler method. However, their is
* no need to check for tags and comments, so we ditch that method and
* process it here by duplicating code (which is fine).
*/
if (scanner.leftOver.trim()) {
const anotherMustache = Detector_1.getMustache(scanner.leftOver, scanner.loc.line, scanner.loc.col);
if (anotherMustache) {
this._handleMustacheOpening(scanner.leftOver, anotherMustache);
this.handleMustacheOpening(scanner.leftOver, anotherMustache);
return;
}
this._consumeNode(this._getRawNode(scanner.leftOver));
this.consumeNode(this.getRawNode(scanner.leftOver));
}
this._mustacheStatement = null;
/**
* Set mustache statement to null
*/
this.mustacheStatement = null;
}
_isClosingTag(line) {
if (!this._openedTags.length) {
/**
* Returns a boolean telling if the content of the line is the
* closing tag for the most recently opened tag.
*
* The opening and closing has to be in a order, otherwise the
* compiler will get mad.
*/
isClosingTag(line) {
if (!this.openedTags.length) {
return false;
}
const recentTag = this._openedTags[this._openedTags.length - 1];
const recentTag = this.openedTags[this.openedTags.length - 1];
return line.trim() === `@end${recentTag.properties.name}`;
}
_consumeNode(tag) {
if (this._openedTags.length) {
this._openedTags[this._openedTags.length - 1].children.push(tag);
/**
* Consume any type of token by moving it to the correct list. If there are
* opened tags, then the token becomes part of the tag children. Otherwise
* moved as top level token.
*/
consumeNode(tag) {
if (this.openedTags.length) {
this.openedTags[this.openedTags.length - 1].children.push(tag);
return;

@@ -155,65 +309,117 @@ }

}
_pushNewLine() {
if (this._line === 1) {
/**
* Pushes a new line to the list. This method avoids
* new lines at position 0.
*/
pushNewLine() {
if (this.line === 1) {
return;
}
this._consumeNode(this._getNewLineNode());
this.consumeNode(this.getNewLineNode());
}
_processText(line) {
if (this._tagStatement) {
this._feedCharsToCurrentTag('\n');
this._feedCharsToCurrentTag(line);
/**
* Process the current line based upon what it is. What it is?
* That's the job of this method to find out.
*/
processText(line) {
/**
* There is an open block statement, so feed line to it
*/
if (this.tagStatement) {
this.feedCharsToCurrentTag('\n');
this.feedCharsToCurrentTag(line);
return;
}
if (this._mustacheStatement) {
this._feedCharsToCurrentMustache('\n');
this._feedCharsToCurrentMustache(line);
/**
* There is an open mustache statement, so feed line to it
*/
if (this.mustacheStatement) {
this.feedCharsToCurrentMustache('\n');
this.feedCharsToCurrentMustache(line);
return;
}
if (this._isClosingTag(line)) {
this._consumeNode(this._openedTags.pop());
/**
* The line is an closing statement for a previously opened
* block level tag
*/
if (this.isClosingTag(line)) {
this.consumeNode(this.openedTags.pop());
return;
}
if (!this._skipNewLine) {
this._pushNewLine();
/**
* Everything from here pushes a new line to the stack before
* moving forward
*/
if (!this.skipNewLine) {
this.pushNewLine();
}
const tag = Detector_1.getTag(line, this._line, 0, this._tagsDef);
/**
* Check if the current line is a tag or not. If yes, then handle
* it appropriately
*/
const tag = Detector_1.getTag(line, this.line, 0, this.tagsDef);
if (tag) {
this._handleTagOpening(line, tag);
this._skipNewLine = true;
this.handleTagOpening(line, tag);
this.skipNewLine = true;
return;
}
this._skipNewLine = false;
const mustache = Detector_1.getMustache(line, this._line, 0);
this.skipNewLine = false;
/**
* Check if the current line contains a mustache statement or not. If yes,
* then handle it appropriately.
*/
const mustache = Detector_1.getMustache(line, this.line, 0);
if (mustache) {
this._handleMustacheOpening(line, mustache);
this.handleMustacheOpening(line, mustache);
return;
}
this._consumeNode(this._getRawNode(line));
/**
* Otherwise it is a raw line
*/
this.consumeNode(this.getRawNode(line));
}
_checkForErrors() {
if (this._tagStatement) {
const { tag } = this._tagStatement;
throw Exceptions_1.unclosedParen({ line: tag.line, col: tag.col }, this._options.filename);
/**
* Checks for errors after the tokenizer completes it's work, so that we
* can find broken statements or unclosed tags.
*/
checkForErrors() {
/**
* We are done scanning the content and there is an open tagStatement
* seeking for new content. Which means we are missing a closing
* brace `)`.
*/
if (this.tagStatement) {
const { tag } = this.tagStatement;
throw Exceptions_1.unclosedParen({ line: tag.line, col: tag.col }, this.options.filename);
}
if (this._mustacheStatement) {
const { mustache } = this._mustacheStatement;
throw Exceptions_1.unclosedCurlyBrace({ line: mustache.line, col: mustache.col }, this._options.filename);
/**
* We are done scanning the content and there is an open mustache statement
* seeking for new content. Which means we are missing closing braces `}}`.
*/
if (this.mustacheStatement) {
const { mustache } = this.mustacheStatement;
throw Exceptions_1.unclosedCurlyBrace({ line: mustache.line, col: mustache.col }, this.options.filename);
}
if (this._openedTags.length) {
const openedTag = this._openedTags[this._openedTags.length - 1];
throw Exceptions_1.unclosedTag(openedTag.properties.name, openedTag.loc.start, this._options.filename);
/**
* A tag was opened, but forgot to close it
*/
if (this.openedTags.length) {
const openedTag = this.openedTags[this.openedTags.length - 1];
throw Exceptions_1.unclosedTag(openedTag.properties.name, openedTag.loc.start, this.options.filename);
}
}
/**
* Parse the template and generate an AST out of it
*/
parse() {
const lines = this._template.split(/\r\n|\r|\n/g);
const lines = this.template.split(/\r\n|\r|\n/g);
const linesLength = lines.length;
while (this._line < linesLength) {
const line = lines[this._line];
this._line++;
this._processText(line);
while (this.line < linesLength) {
const line = lines[this.line];
this.line++;
this.processText(line);
}
this._checkForErrors();
this.checkForErrors();
}
}
exports.Tokenizer = Tokenizer;
{
"name": "edge-lexer",
"version": "2.0.10",
"version": "2.0.11",
"description": "Parses raw markup files to converts them to Edge tokens",

@@ -23,3 +23,3 @@ "main": "build/index.js",

"prepublishOnly": "npm run build",
"lint": "tslint --project tsconfig.json"
"lint": "eslint . --ext=.ts"
},

@@ -34,22 +34,22 @@ "keywords": [

"devDependencies": {
"@adonisjs/mrm-preset": "^2.1.0",
"@types/node": "^12.7.5",
"@adonisjs/mrm-preset": "^2.2.4",
"@types/node": "^13.7.1",
"benchmark": "^2.1.4",
"commitizen": "^4.0.3",
"cz-conventional-changelog": "^3.0.2",
"cz-conventional-changelog": "^3.1.0",
"dedent": "^0.7.0",
"del-cli": "^3.0.0",
"doctoc": "^1.4.0",
"husky": "^3.0.5",
"eslint": "^6.8.0",
"eslint-plugin-adonis": "^1.0.8",
"husky": "^4.2.3",
"japa": "^3.0.1",
"japa-cli": "^1.0.1",
"mrm": "^1.2.2",
"np": "^5.1.0",
"ts-node": "^8.3.0",
"tslint": "^5.20.0",
"tslint-eslint-rules": "^5.4.0",
"typedoc": "^0.15.0",
"typedoc-plugin-external-module-name": "^2.1.0",
"typedoc-plugin-markdown": "^2.2.1",
"typescript": "^3.6.3"
"mrm": "^2.0.4",
"np": "^6.0.0",
"ts-node": "^8.6.2",
"typedoc": "^0.16.9",
"typedoc-plugin-external-module-name": "^3.0.0",
"typedoc-plugin-markdown": "^2.2.16",
"typescript": "^3.7.5"
},

@@ -56,0 +56,0 @@ "dependencies": {

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