diff --git a/packages/pug-code-gen/index.js b/packages/pug-code-gen/index.js index 3e6bf3b27..e06b03283 100644 --- a/packages/pug-code-gen/index.js +++ b/packages/pug-code-gen/index.js @@ -27,7 +27,6 @@ var INTERNAL_VARIABLES = [ ]; module.exports = generateCode; -module.exports.CodeGenerator = Compiler; function generateCode(ast, options) { return new Compiler(ast, options).compile(); } @@ -44,82 +43,82 @@ function isIdentifier(name) { } /** - * Initialize `Compiler` with the given `node`. - * - * @param {Node} node - * @param {Object} options - * @api public + * Compiler prototype. */ -function Compiler(node, options) { - this.options = options = options || {}; - this.node = node; - this.bufferedConcatenationCount = 0; - this.hasCompiledDoctype = false; - this.hasCompiledTag = false; - this.pp = options.pretty || false; - if (this.pp && typeof this.pp !== 'string') { - this.pp = ' '; - } - if (this.pp && !/^\s+$/.test(this.pp)) { - throw new Error( - 'The pretty parameter should either be a boolean or whitespace only string' - ); - } - if (this.options.templateName && !isIdentifier(this.options.templateName)) { - throw new Error( - 'The templateName parameter must be a valid JavaScript identifier if specified.' - ); - } - if ( - this.doctype && - (this.doctype.includes('<') || this.doctype.includes('>')) - ) { - throw new Error('Doctype can not contain "<" or ">"'); - } - if (this.options.globals && !this.options.globals.every(isIdentifier)) { - throw new Error( - 'The globals option must be an array of valid JavaScript identifiers if specified.' - ); - } +class Compiler { + /** + * Initialize `Compiler` with the given `node`. + * + * @param {Node} node + * @param {Object} options + * @api public + */ - this.debug = false !== options.compileDebug; - this.indents = 0; - this.parentIndents = 0; - this.terse = false; - this.mixins = {}; - this.dynamicMixins = false; - this.eachCount = 0; - if (options.doctype) this.setDoctype(options.doctype); - this.runtimeFunctionsUsed = []; - this.inlineRuntimeFunctions = options.inlineRuntimeFunctions || false; - if (this.debug && this.inlineRuntimeFunctions) { - this.runtimeFunctionsUsed.push('rethrow'); - } -} + constructor(node, options) { + this.options = options = options || {}; + this.node = node; + this.bufferedConcatenationCount = 0; + this.hasCompiledDoctype = false; + this.hasCompiledTag = false; + this.pp = options.pretty || false; + if (this.pp && typeof this.pp !== 'string') { + this.pp = ' '; + } + if (this.pp && !/^\s+$/.test(this.pp)) { + throw new Error( + 'The pretty parameter should either be a boolean or whitespace only string' + ); + } + if (this.options.templateName && !isIdentifier(this.options.templateName)) { + throw new Error( + 'The templateName parameter must be a valid JavaScript identifier if specified.' + ); + } + if ( + this.doctype && + (this.doctype.includes('<') || this.doctype.includes('>')) + ) { + throw new Error('Doctype can not contain "<" or ">"'); + } + if (this.options.globals && !this.options.globals.every(isIdentifier)) { + throw new Error( + 'The globals option must be an array of valid JavaScript identifiers if specified.' + ); + } -/** - * Compiler prototype. - */ + this.debug = false !== options.compileDebug; + this.indents = 0; + this.parentIndents = 0; + this.terse = false; + this.mixins = {}; + this.dynamicMixins = false; + this.eachCount = 0; + if (options.doctype) this.setDoctype(options.doctype); + this.runtimeFunctionsUsed = []; + this.inlineRuntimeFunctions = options.inlineRuntimeFunctions || false; + if (this.debug && this.inlineRuntimeFunctions) { + this.runtimeFunctionsUsed.push('rethrow'); + } + } -Compiler.prototype = { - runtime: function(name) { + runtime(name) { if (this.inlineRuntimeFunctions) { this.runtimeFunctionsUsed.push(name); return 'pug_' + name; } else { return 'pug.' + name; } - }, + } - error: function(message, code, node) { + error(message, code, node) { var err = makeError(code, message, { line: node.line, column: node.column, filename: node.filename, }); throw err; - }, + } /** * Compile parse tree to JavaScript. @@ -127,7 +126,7 @@ Compiler.prototype = { * @api public */ - compile: function() { + compile() { this.buf = []; if (this.pp) this.buf.push('var pug_indent = [];'); this.lastBufferedIdx = -1; @@ -197,7 +196,7 @@ Compiler.prototype = { js + ';return pug_html;}' ); - }, + } /** * Sets the default doctype `name`. Sets terse mode to `true` when @@ -208,11 +207,11 @@ Compiler.prototype = { * @api public */ - setDoctype: function(name) { + setDoctype(name) { this.doctype = doctypes[name.toLowerCase()] || ''; this.terse = this.doctype.toLowerCase() == ''; this.xml = 0 == this.doctype.indexOf(''); - }, + } /** * Visit a `YieldBlock`. @@ -761,7 +760,7 @@ Compiler.prototype = { * @api public */ - visitYieldBlock: function(block) {}, + visitYieldBlock(block) {} /** * Visit a `BlockComment`. @@ -770,14 +769,14 @@ Compiler.prototype = { * @api public */ - visitBlockComment: function(comment) { + visitBlockComment(comment) { if (!comment.buffer) return; if (this.pp) this.prettyIndent(1, true); this.buffer(''); - }, + } /** * Visit `code`, respecting buffer / escape flags. @@ -788,7 +787,7 @@ Compiler.prototype = { * @api public */ - visitCode: function(code) { + visitCode(code) { // Wrap code blocks with {}. // we only wrap unbuffered code blocks ATM // since they are usually flow control @@ -810,7 +809,7 @@ Compiler.prototype = { this.visit(code.block, code); if (!code.buffer) this.buf.push('}'); } - }, + } /** * Visit `Conditional`. @@ -819,7 +818,7 @@ Compiler.prototype = { * @api public */ - visitConditional: function(cond) { + visitConditional(cond) { var test = cond.test; this.buf.push('if (' + test + ') {'); this.visit(cond.consequent, cond); @@ -834,7 +833,7 @@ Compiler.prototype = { this.buf.push('}'); } } - }, + } /** * Visit `While`. @@ -843,12 +842,12 @@ Compiler.prototype = { * @api public */ - visitWhile: function(loop) { + visitWhile(loop) { var test = loop.test; this.buf.push('while (' + test + ') {'); this.visit(loop.block, loop); this.buf.push('}'); - }, + } /** * Visit `each` block. @@ -857,7 +856,7 @@ Compiler.prototype = { * @api public */ - visitEach: function(each) { + visitEach(each) { var indexVarName = each.key || 'pug_index' + this.eachCount; this.eachCount++; @@ -927,9 +926,9 @@ Compiler.prototype = { this.buf.push(' }'); } this.buf.push(' }\n}).call(this);\n'); - }, + } - visitEachOf: function(each) { + visitEachOf(each) { this.buf.push( '' + '// iterate ' + @@ -945,7 +944,7 @@ Compiler.prototype = { this.visit(each.block, each); this.buf.push('}\n'); - }, + } /** * Visit `attrs`. @@ -954,7 +953,7 @@ Compiler.prototype = { * @api public */ - visitAttributes: function(attrs, attributeBlocks) { + visitAttributes(attrs, attributeBlocks) { if (attributeBlocks.length) { if (attrs.length) { var val = this.attrs(attrs); @@ -984,13 +983,13 @@ Compiler.prototype = { } else if (attrs.length) { this.attrs(attrs, true); } - }, + } /** * Compile attributes. */ - attrs: function(attrs, buffer) { + attrs(attrs, buffer) { var res = compileAttrs(attrs, { terse: this.terse, format: buffer ? 'html' : 'object', @@ -1000,21 +999,22 @@ Compiler.prototype = { this.bufferExpression(res); } return res; - }, + } /** * Compile attribute blocks. */ - attributeBlocks: function(attributeBlocks) { + attributeBlocks(attributeBlocks) { return ( attributeBlocks && attributeBlocks.slice().map(function(attrBlock) { return attrBlock.val; }) ); - }, -}; + } +} +module.exports.CodeGenerator = Compiler; function tagCanInline(tag) { function isInline(node) { diff --git a/packages/pug-lexer/index.js b/packages/pug-lexer/index.js index 51836c08e..b3b511f13 100644 --- a/packages/pug-lexer/index.js +++ b/packages/pug-lexer/index.js @@ -6,59 +6,56 @@ var characterParser = require('character-parser'); var error = require('pug-error'); module.exports = lex; -module.exports.Lexer = Lexer; function lex(str, options) { var lexer = new Lexer(str, options); return JSON.parse(JSON.stringify(lexer.getTokens())); } -/** - * Initialize `Lexer` with the given `str`. - * - * @param {String} str - * @param {String} filename - * @api private - */ - -function Lexer(str, options) { - options = options || {}; - if (typeof str !== 'string') { - throw new Error( - 'Expected source code to be a string but got "' + typeof str + '"' - ); - } - if (typeof options !== 'object') { - throw new Error( - 'Expected "options" to be an object but got "' + typeof options + '"' - ); - } - //Strip any UTF-8 BOM off of the start of `str`, if it exists. - str = str.replace(/^\uFEFF/, ''); - this.input = str.replace(/\r\n|\r/g, '\n'); - this.originalInput = this.input; - this.filename = options.filename; - this.interpolated = options.interpolated || false; - this.lineno = options.startingLine || 1; - this.colno = options.startingColumn || 1; - this.plugins = options.plugins || []; - this.indentStack = [0]; - this.indentRe = null; - // If #{}, !{} or #[] syntax is allowed when adding text - this.interpolationAllowed = true; - this.whitespaceRe = /[ \n\t]/; - - this.tokens = []; - this.ended = false; -} - /** * Lexer prototype. */ -Lexer.prototype = { - constructor: Lexer, +class Lexer { + /** + * Initialize `Lexer` with the given `str`. + * + * @param {String} str + * @param {String} filename + * @api private + */ + + constructor(str, options) { + options = options || {}; + if (typeof str !== 'string') { + throw new Error( + 'Expected source code to be a string but got "' + typeof str + '"' + ); + } + if (typeof options !== 'object') { + throw new Error( + 'Expected "options" to be an object but got "' + typeof options + '"' + ); + } + //Strip any UTF-8 BOM off of the start of `str`, if it exists. + str = str.replace(/^\uFEFF/, ''); + this.input = str.replace(/\r\n|\r/g, '\n'); + this.originalInput = this.input; + this.filename = options.filename; + this.interpolated = options.interpolated || false; + this.lineno = options.startingLine || 1; + this.colno = options.startingColumn || 1; + this.plugins = options.plugins || []; + this.indentStack = [0]; + this.indentRe = null; + // If #{}, !{} or #[] syntax is allowed when adding text + this.interpolationAllowed = true; + this.whitespaceRe = /[ \n\t]/; + + this.tokens = []; + this.ended = false; + } - error: function(code, message) { + error(code, message) { var err = error(code, message, { line: this.lineno, column: this.colno, @@ -66,19 +63,19 @@ Lexer.prototype = { src: this.originalInput, }); throw err; - }, + } - assert: function(value, message) { + assert(value, message) { if (!value) this.error('ASSERT_FAILED', message); - }, + } - isExpression: function(exp) { + isExpression(exp) { return isExpression(exp, { throw: true, }); - }, + } - assertExpression: function(exp, noThrow) { + assertExpression(exp, noThrow) { //this verifies that a JavaScript expression is valid try { this.callLexerFunction('isExpression', exp); @@ -95,9 +92,9 @@ Lexer.prototype = { 'Syntax Error: ' + ex.message.replace(/ \([0-9]+:[0-9]+\)$/, ''); this.error('SYNTAX_ERROR', msg); } - }, + } - assertNestingCorrect: function(exp) { + assertNestingCorrect(exp) { //this verifies that code is properly nested, but allows //invalid JavaScript such as the contents of `attributes` var res = characterParser.default(exp); @@ -107,7 +104,7 @@ Lexer.prototype = { 'Nesting must match on expression `' + exp + '`' ); } - }, + } /** * Construct a token with the given `type` and `val`. @@ -118,7 +115,7 @@ Lexer.prototype = { * @api private */ - tok: function(type, val) { + tok(type, val) { var res = { type: type, loc: { @@ -133,7 +130,7 @@ Lexer.prototype = { if (val !== undefined) res.val = val; return res; - }, + } /** * Set the token's `loc.end` value. @@ -143,13 +140,13 @@ Lexer.prototype = { * @api private */ - tokEnd: function(tok) { + tokEnd(tok) { tok.loc.end = { line: this.lineno, column: this.colno, }; return tok; - }, + } /** * Increment `this.lineno` and reset `this.colno`. @@ -158,10 +155,10 @@ Lexer.prototype = { * @api private */ - incrementLine: function(increment) { + incrementLine(increment) { this.lineno += increment; if (increment) this.colno = 1; - }, + } /** * Increment `this.colno`. @@ -170,9 +167,9 @@ Lexer.prototype = { * @api private */ - incrementColumn: function(increment) { + incrementColumn(increment) { this.colno += increment; - }, + } /** * Consume the given `len` of input. @@ -181,9 +178,9 @@ Lexer.prototype = { * @api private */ - consume: function(len) { + consume(len) { this.input = this.input.substr(len); - }, + } /** * Scan for `type` with the given `regexp`. @@ -194,7 +191,7 @@ Lexer.prototype = { * @api private */ - scan: function(regexp, type) { + scan(regexp, type) { var captures; if ((captures = regexp.exec(this.input))) { var len = captures[0].length; @@ -205,8 +202,8 @@ Lexer.prototype = { this.incrementColumn(diff); return tok; } - }, - scanEndOfLine: function(regexp, type) { + } + scanEndOfLine(regexp, type) { var captures; if ((captures = regexp.exec(this.input))) { var whitespaceLength = 0; @@ -230,7 +227,7 @@ Lexer.prototype = { return tok; } } - }, + } /** * Return the indexOf `(` or `{` or `[` / `)` or `}` or `]` delimiters. @@ -242,7 +239,7 @@ Lexer.prototype = { * @api private */ - bracketExpression: function(skip) { + bracketExpression(skip) { skip = skip || 0; var start = this.input[skip]; assert( @@ -283,9 +280,9 @@ Lexer.prototype = { throw ex; } return range; - }, + } - scanIndentation: function() { + scanIndentation() { var captures, re; // established regexp @@ -308,13 +305,13 @@ Lexer.prototype = { } return captures; - }, + } /** * end-of-source. */ - eos: function() { + eos() { if (this.input.length) return; if (this.interpolated) { this.error( @@ -328,26 +325,26 @@ Lexer.prototype = { this.tokens.push(this.tokEnd(this.tok('eos'))); this.ended = true; return true; - }, + } /** * Blank line. */ - blank: function() { + blank() { var captures; if ((captures = /^\n[ \t]*\n/.exec(this.input))) { this.consume(captures[0].length - 1); this.incrementLine(1); return true; } - }, + } /** * Comment. */ - comment: function() { + comment() { var captures; if ((captures = /^\/\/(-)?([^\n]*)/.exec(this.input))) { this.consume(captures[0].length); @@ -360,13 +357,13 @@ Lexer.prototype = { this.callLexerFunction('pipelessText'); return true; } - }, + } /** * Interpolated tag. */ - interpolation: function() { + interpolation() { if (/^#\{/.test(this.input)) { var match = this.bracketExpression(1); this.consume(match.end + 1); @@ -382,13 +379,13 @@ Lexer.prototype = { this.tokEnd(tok); return true; } - }, + } /** * Tag. */ - tag: function() { + tag() { var captures; if ((captures = /^(\w(?:[-:\w]*\w)?)/.exec(this.input))) { @@ -402,13 +399,13 @@ Lexer.prototype = { this.tokEnd(tok); return true; } - }, + } /** * Filter. */ - filter: function(opts) { + filter(opts) { var tok = this.scan(/^:([\w\-]+)/, 'filter'); var inInclude = opts && opts.inInclude; if (tok) { @@ -422,25 +419,25 @@ Lexer.prototype = { } return true; } - }, + } /** * Doctype. */ - doctype: function() { + doctype() { var node = this.scanEndOfLine(/^doctype *([^\n]*)/, 'doctype'); if (node) { this.tokens.push(this.tokEnd(node)); return true; } - }, + } /** * Id. */ - id: function() { + id() { var tok = this.scan(/^#([\w-]+)/, 'id'); if (tok) { this.tokens.push(tok); @@ -456,13 +453,13 @@ Lexer.prototype = { '" is not a valid ID.' ); } - }, + } /** * Class. */ - className: function() { + className() { var tok = this.scan(/^\.([_a-z0-9\-]*[_a-z][_a-z0-9\-]*)/i, 'class'); if (tok) { this.tokens.push(tok); @@ -484,19 +481,19 @@ Lexer.prototype = { '" is not a valid class name. Class names can only contain "_", "-", a-z and 0-9, and must contain at least one of "_", or a-z' ); } - }, + } /** * Text. */ - endInterpolation: function() { + endInterpolation() { if (this.interpolated && this.input[0] === ']') { this.input = this.input.substr(1); this.ended = true; return true; } - }, - addText: function(type, value, prefix, escaped) { + } + addText(type, value, prefix, escaped) { var tok; if (value + prefix === '') return; prefix = prefix || ''; @@ -642,9 +639,9 @@ Lexer.prototype = { tok = this.tok(type, value); this.incrementColumn(value.length + escaped); this.tokens.push(this.tokEnd(tok)); - }, + } - text: function() { + text() { var tok = this.scan(/^(?:\| ?| )([^\n]+)/, 'text') || this.scan(/^( )/, 'text') || @@ -653,34 +650,34 @@ Lexer.prototype = { this.addText('text', tok.val); return true; } - }, + } - textHtml: function() { + textHtml() { var tok = this.scan(/^(<[^\n]*)/, 'text-html'); if (tok) { this.addText('text-html', tok.val); return true; } - }, + } /** * Dot. */ - dot: function() { + dot() { var tok; if ((tok = this.scanEndOfLine(/^\./, 'dot'))) { this.tokens.push(this.tokEnd(tok)); this.callLexerFunction('pipelessText'); return true; } - }, + } /** * Extends. */ - extends: function() { + extends() { var tok = this.scan(/^extends?(?= |$|\n)/, 'extends'); if (tok) { this.tokens.push(this.tokEnd(tok)); @@ -692,13 +689,13 @@ Lexer.prototype = { if (this.scan(/^extends?\b/)) { this.error('MALFORMED_EXTENDS', 'malformed extends'); } - }, + } /** * Block prepend. */ - prepend: function() { + prepend() { var captures; if ((captures = /^(?:block +)?prepend +([^\n]+)/.exec(this.input))) { var name = captures[1].trim(); @@ -723,13 +720,13 @@ Lexer.prototype = { this.incrementColumn(captures[0].length - comment.length - len); return true; } - }, + } /** * Block append. */ - append: function() { + append() { var captures; if ((captures = /^(?:block +)?append +([^\n]+)/.exec(this.input))) { var name = captures[1].trim(); @@ -754,13 +751,13 @@ Lexer.prototype = { this.incrementColumn(captures[0].length - comment.length - len); return true; } - }, + } /** * Block. */ - block: function() { + block() { var captures; if ((captures = /^block +([^\n]+)/.exec(this.input))) { var name = captures[1].trim(); @@ -785,37 +782,37 @@ Lexer.prototype = { this.incrementColumn(captures[0].length - comment.length - len); return true; } - }, + } /** * Mixin Block. */ - mixinBlock: function() { + mixinBlock() { var tok; if ((tok = this.scanEndOfLine(/^block/, 'mixin-block'))) { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } /** * Yield. */ - yield: function() { + yield() { var tok = this.scanEndOfLine(/^yield/, 'yield'); if (tok) { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } /** * Include. */ - include: function() { + include() { var tok = this.scan(/^include(?=:| |$|\n)/, 'include'); if (tok) { this.tokens.push(this.tokEnd(tok)); @@ -834,25 +831,25 @@ Lexer.prototype = { if (this.scan(/^include\b/)) { this.error('MALFORMED_INCLUDE', 'malformed include'); } - }, + } /** * Path */ - path: function() { + path() { var tok = this.scanEndOfLine(/^ ([^\n]+)/, 'path'); if (tok && (tok.val = tok.val.trim())) { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } /** * Case. */ - case: function() { + case() { var tok = this.scanEndOfLine(/^case +([^\n]+)/, 'case'); if (tok) { this.incrementColumn(-tok.val.length); @@ -864,13 +861,13 @@ Lexer.prototype = { if (this.scan(/^case\b/)) { this.error('NO_CASE_EXPRESSION', 'missing expression for case'); } - }, + } /** * When. */ - when: function() { + when() { var tok = this.scanEndOfLine(/^when +([^:\n]+)/, 'when'); if (tok) { var parser = characterParser.default(tok.val); @@ -893,13 +890,13 @@ Lexer.prototype = { if (this.scan(/^when\b/)) { this.error('NO_WHEN_EXPRESSION', 'missing expression for when'); } - }, + } /** * Default. */ - default: function() { + default() { var tok = this.scanEndOfLine(/^default/, 'default'); if (tok) { this.tokens.push(this.tokEnd(tok)); @@ -911,13 +908,13 @@ Lexer.prototype = { 'default should not have an expression' ); } - }, + } /** * Call mixin. */ - call: function() { + call() { var tok, captures, increment; if ((captures = /^\+(\s*)(([-\w]+)|(#\{))/.exec(this.input))) { // try to consume simple or interpolated call @@ -959,13 +956,13 @@ Lexer.prototype = { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } /** * Mixin. */ - mixin: function() { + mixin() { var captures; if ((captures = /^mixin +([-\w]+)(?: *\((.*)\))? */.exec(this.input))) { this.consume(captures[0].length); @@ -975,13 +972,13 @@ Lexer.prototype = { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } /** * Conditional. */ - conditional: function() { + conditional() { var captures; if ((captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input))) { this.consume(captures[0].length); @@ -1014,13 +1011,13 @@ Lexer.prototype = { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } /** * While. */ - while: function() { + while() { var captures, tok; if ((captures = /^while +([^\n]+)/.exec(this.input))) { this.consume(captures[0].length); @@ -1033,13 +1030,13 @@ Lexer.prototype = { if (this.scan(/^while\b/)) { this.error('NO_WHILE_EXPRESSION', 'missing expression for while'); } - }, + } /** * Each. */ - each: function() { + each() { var captures; if ( (captures = /^(?:each|for) +([a-zA-Z_$][\w$]*)(?: *, *([a-zA-Z_$][\w$]*))? * in *([^\n]+)/.exec( @@ -1079,13 +1076,13 @@ Lexer.prototype = { 'Pug each and for should no longer be prefixed with a dash ("-"). They are pug keywords and not part of JavaScript.' ); } - }, + } /** * EachOf. */ - eachOf: function() { + eachOf() { var captures; if ((captures = /^(?:each|for) (.*?) of *([^\n]+)/.exec(this.input))) { this.consume(captures[0].length); @@ -1123,13 +1120,13 @@ Lexer.prototype = { 'Pug each and for should not be prefixed with a dash ("-"). They are pug keywords and not part of JavaScript.' ); } - }, + } /** * Code. */ - code: function() { + code() { var captures; if ((captures = /^(!?=|-)[ \t]*([^\n]+)/.exec(this.input))) { var flags = captures[1]; @@ -1195,12 +1192,12 @@ Lexer.prototype = { this.tokEnd(tok); return true; } - }, + } /** * Block code. */ - blockCode: function() { + blockCode() { var tok; if ((tok = this.scanEndOfLine(/^-/, 'blockcode'))) { this.tokens.push(this.tokEnd(tok)); @@ -1208,12 +1205,12 @@ Lexer.prototype = { this.callLexerFunction('pipelessText'); return true; } - }, + } /** * Attribute Name. */ - attribute: function(str) { + attribute(str) { var quote = ''; var quoteRe = /['"]/; var key = ''; @@ -1304,12 +1301,12 @@ Lexer.prototype = { } return str.substr(i); - }, + } /** * Attribute Value. */ - attributeValue: function(str) { + attributeValue(str) { var quoteRe = /['"]/; var val = ''; var done, i, x; @@ -1431,13 +1428,13 @@ Lexer.prototype = { this.colno = col; return {val: val, mustEscape: escapeAttr, remainingSource: str.substr(i)}; - }, + } /** * Attributes. */ - attrs: function() { + attrs() { var tok; if ('(' == this.input.charAt(0)) { @@ -1459,12 +1456,12 @@ Lexer.prototype = { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } /** * &attributes block */ - attributesBlock: function() { + attributesBlock() { if (/^&attributes\b/.test(this.input)) { var consumed = 11; this.consume(consumed); @@ -1478,13 +1475,13 @@ Lexer.prototype = { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } /** * Indent | Outdent | Newline. */ - indent: function() { + indent() { var captures = this.scanIndentation(); var tok; @@ -1546,9 +1543,9 @@ Lexer.prototype = { this.interpolationAllowed = true; return true; } - }, + } - pipelessText: function pipelessText(indents) { + pipelessText(indents) { while (this.callLexerFunction('blank')); var captures = this.scanIndentation(); @@ -1580,7 +1577,7 @@ Lexer.prototype = { // line is indented less than the first line but is still indented // need to retry lexing the text block this.tokens.pop(); - return pipelessText.call(this, lineCaptures[1].length); + return this.pipelessText(lineCaptures[1].length); } } while (this.input.length - stringPtr && isMatch); this.consume(stringPtr); @@ -1599,40 +1596,40 @@ Lexer.prototype = { this.tokens.push(this.tokEnd(this.tok('end-pipeless-text'))); return true; } - }, + } /** * Slash. */ - slash: function() { + slash() { var tok = this.scan(/^\//, 'slash'); if (tok) { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } /** * ':' */ - colon: function() { + colon() { var tok = this.scan(/^: +/, ':'); if (tok) { this.tokens.push(this.tokEnd(tok)); return true; } - }, + } - fail: function() { + fail() { this.error( 'UNEXPECTED_TEXT', 'unexpected text "' + this.input.substr(0, 5) + '"' ); - }, + } - callLexerFunction: function(func) { + callLexerFunction(func) { var rest = []; for (var i = 1; i < arguments.length; i++) { rest.push(arguments[i]); @@ -1645,7 +1642,7 @@ Lexer.prototype = { } } return this[func].apply(this, rest); - }, + } /** * Move to the next token @@ -1653,7 +1650,7 @@ Lexer.prototype = { * @api private */ - advance: function() { + advance() { return ( this.callLexerFunction('blank') || this.callLexerFunction('eos') || @@ -1693,7 +1690,7 @@ Lexer.prototype = { this.callLexerFunction('colon') || this.fail() ); - }, + } /** * Return an array of tokens for the current file @@ -1701,10 +1698,12 @@ Lexer.prototype = { * @returns {Array.} * @api public */ - getTokens: function() { + getTokens() { while (!this.ended) { this.callLexerFunction('advance'); } return this.tokens; - }, -}; + } +} + +module.exports.Lexer = Lexer; diff --git a/packages/pug-parser/index.js b/packages/pug-parser/index.js index abd531ed5..1362a0589 100644 --- a/packages/pug-parser/index.js +++ b/packages/pug-parser/index.js @@ -6,53 +6,41 @@ var error = require('pug-error'); var inlineTags = require('./lib/inline-tags'); module.exports = parse; -module.exports.Parser = Parser; function parse(tokens, options) { var parser = new Parser(tokens, options); var ast = parser.parse(); return JSON.parse(JSON.stringify(ast)); } -/** - * Initialize `Parser` with the given input `str` and `filename`. - * - * @param {String} str - * @param {String} filename - * @param {Object} options - * @api public - */ - -function Parser(tokens, options) { - options = options || {}; - if (!Array.isArray(tokens)) { - throw new Error( - 'Expected tokens to be an Array but got "' + typeof tokens + '"' - ); - } - if (typeof options !== 'object') { - throw new Error( - 'Expected "options" to be an object but got "' + typeof options + '"' - ); - } - this.tokens = new TokenStream(tokens); - this.filename = options.filename; - this.src = options.src; - this.inMixin = 0; - this.plugins = options.plugins || []; -} - -/** - * Parser prototype. - */ - -Parser.prototype = { +class Parser { /** - * Save original constructor + * Initialize `Parser` with the given input `str` and `filename`. + * + * @param {String} str + * @param {String} filename + * @param {Object} options + * @api public */ + constructor(tokens, options) { + options = options || {}; + if (!Array.isArray(tokens)) { + throw new Error( + 'Expected tokens to be an Array but got "' + typeof tokens + '"' + ); + } + if (typeof options !== 'object') { + throw new Error( + 'Expected "options" to be an object but got "' + typeof options + '"' + ); + } + this.tokens = new TokenStream(tokens); + this.filename = options.filename; + this.src = options.src; + this.inMixin = 0; + this.plugins = options.plugins || []; + } - constructor: Parser, - - error: function(code, message, token) { + error(code, message, token) { var err = error(code, message, { line: token.loc.start.line, column: token.loc.start.column, @@ -60,7 +48,7 @@ Parser.prototype = { src: this.src, }); throw err; - }, + } /** * Return the next token object. @@ -69,9 +57,9 @@ Parser.prototype = { * @api private */ - advance: function() { + advance() { return this.tokens.advance(); - }, + } /** * Single token lookahead. @@ -80,9 +68,9 @@ Parser.prototype = { * @api private */ - peek: function() { + peek() { return this.tokens.peek(); - }, + } /** * `n` token lookahead. @@ -92,9 +80,9 @@ Parser.prototype = { * @api private */ - lookahead: function(n) { + lookahead(n) { return this.tokens.lookahead(n); - }, + } /** * Parse input returning a string of js for evaluation. @@ -103,7 +91,7 @@ Parser.prototype = { * @api public */ - parse: function() { + parse() { var block = this.emptyBlock(0); while ('eos' != this.peek().type) { @@ -124,7 +112,7 @@ Parser.prototype = { } return block; - }, + } /** * Expect the given type, or throw an exception. @@ -133,7 +121,7 @@ Parser.prototype = { * @api private */ - expect: function(type) { + expect(type) { if (this.peek().type === type) { return this.advance(); } else { @@ -143,7 +131,7 @@ Parser.prototype = { this.peek() ); } - }, + } /** * Accept the given `type`. @@ -152,13 +140,13 @@ Parser.prototype = { * @api private */ - accept: function(type) { + accept(type) { if (this.peek().type === type) { return this.advance(); } - }, + } - initBlock: function(line, nodes) { + initBlock(line, nodes) { /* istanbul ignore if */ if ((line | 0) !== line) throw new Error('`line` is not an integer'); /* istanbul ignore if */ @@ -169,13 +157,13 @@ Parser.prototype = { line: line, filename: this.filename, }; - }, + } - emptyBlock: function(line) { + emptyBlock(line) { return this.initBlock(line, []); - }, + } - runPlugin: function(context, tok) { + runPlugin(context, tok) { var rest = [this]; for (var i = 2; i < arguments.length; i++) { rest.push(arguments[i]); @@ -196,7 +184,7 @@ Parser.prototype = { } if (pluginContext) return pluginContext[tok.type].apply(pluginContext, rest); - }, + } /** * tag @@ -216,7 +204,7 @@ Parser.prototype = { * | interpolation */ - parseExpr: function() { + parseExpr() { switch (this.peek().type) { case 'tag': return this.parseTag(); @@ -283,18 +271,18 @@ Parser.prototype = { this.peek() ); } - }, + } - parseDot: function() { + parseDot() { this.advance(); return this.parseTextBlock(); - }, + } /** * Text */ - parseText: function(options) { + parseText(options) { var tags = []; var lineno = this.peek().loc.start.line; var nextTok = this.peek(); @@ -351,9 +339,9 @@ Parser.prototype = { } if (tags.length === 1) return tags[0]; else return this.initBlock(lineno, tags); - }, + } - parseTextHtml: function() { + parseTextHtml() { var nodes = []; var currentNode = null; loop: while (true) { @@ -402,14 +390,14 @@ Parser.prototype = { } } return nodes; - }, + } /** * ':' expr * | block */ - parseBlockExpansion: function() { + parseBlockExpansion() { var tok = this.accept(':'); if (tok) { var expr = this.parseExpr(); @@ -419,13 +407,13 @@ Parser.prototype = { } else { return this.block(); } - }, + } /** * case */ - parseCase: function() { + parseCase() { var tok = this.expect('case'); var node = { type: 'Case', @@ -466,13 +454,13 @@ Parser.prototype = { node.block = block; return node; - }, + } /** * when */ - parseWhen: function() { + parseWhen() { var tok = this.expect('when'); if (this.peek().type !== 'newline') { return { @@ -494,13 +482,13 @@ Parser.prototype = { filename: this.filename, }; } - }, + } /** * default */ - parseDefault: function() { + parseDefault() { var tok = this.expect('default'); return { type: 'When', @@ -511,13 +499,13 @@ Parser.prototype = { column: tok.loc.start.column, filename: this.filename, }; - }, + } /** * code */ - parseCode: function(noBlock) { + parseCode(noBlock) { var tok = this.expect('code'); assert( typeof tok.mustEscape === 'boolean', @@ -554,8 +542,8 @@ Parser.prototype = { } return node; - }, - parseConditional: function() { + } + parseConditional() { var tok = this.expect('if'); var node = { type: 'Conditional', @@ -602,8 +590,8 @@ Parser.prototype = { } return node; - }, - parseWhile: function() { + } + parseWhile() { var tok = this.expect('while'); var node = { type: 'While', @@ -621,13 +609,13 @@ Parser.prototype = { } return node; - }, + } /** * block code */ - parseBlockCode: function() { + parseBlockCode() { var tok = this.expect('blockcode'); var line = tok.loc.start.line; var column = tok.loc.start.column; @@ -669,12 +657,12 @@ Parser.prototype = { column: column, filename: this.filename, }; - }, + } /** * comment */ - parseComment: function() { + parseComment() { var tok = this.expect('comment'); var block; if ((block = this.parseTextBlock())) { @@ -697,13 +685,13 @@ Parser.prototype = { filename: this.filename, }; } - }, + } /** * doctype */ - parseDoctype: function() { + parseDoctype() { var tok = this.expect('doctype'); return { type: 'Doctype', @@ -712,9 +700,9 @@ Parser.prototype = { column: tok.loc.start.column, filename: this.filename, }; - }, + } - parseIncludeFilter: function() { + parseIncludeFilter() { var tok = this.expect('filter'); var attrs = []; @@ -730,13 +718,13 @@ Parser.prototype = { column: tok.loc.start.column, filename: this.filename, }; - }, + } /** * filter attrs? text-block */ - parseFilter: function() { + parseFilter() { var tok = this.expect('filter'); var block, attrs = []; @@ -771,13 +759,13 @@ Parser.prototype = { column: tok.loc.start.column, filename: this.filename, }; - }, + } /** * each block */ - parseEach: function() { + parseEach() { var tok = this.expect('each'); var node = { type: 'Each', @@ -794,9 +782,9 @@ Parser.prototype = { node.alternate = this.block(); } return node; - }, + } - parseEachOf: function() { + parseEachOf() { var tok = this.expect('eachOf'); var node = { type: 'EachOf', @@ -808,12 +796,12 @@ Parser.prototype = { filename: this.filename, }; return node; - }, + } /** * 'extends' name */ - parseExtends: function() { + parseExtends() { var tok = this.expect('extends'); var path = this.expect('path'); return { @@ -829,13 +817,13 @@ Parser.prototype = { column: tok.loc.start.column, filename: this.filename, }; - }, + } /** * 'block' name block */ - parseBlock: function() { + parseBlock() { var tok = this.expect('block'); var node = @@ -849,9 +837,9 @@ Parser.prototype = { node.column = tok.loc.start.column; return node; - }, + } - parseMixinBlock: function() { + parseMixinBlock() { var tok = this.expect('mixin-block'); if (!this.inMixin) { this.error( @@ -866,9 +854,9 @@ Parser.prototype = { column: tok.loc.start.column, filename: this.filename, }; - }, + } - parseYield: function() { + parseYield() { var tok = this.expect('yield'); return { type: 'YieldBlock', @@ -876,13 +864,13 @@ Parser.prototype = { column: tok.loc.start.column, filename: this.filename, }; - }, + } /** * include block? */ - parseInclude: function() { + parseInclude() { var tok = this.expect('include'); var node = { type: 'Include', @@ -934,13 +922,13 @@ Parser.prototype = { } } return node; - }, + } /** * call ident block */ - parseCall: function() { + parseCall() { var tok = this.expect('call'); var name = tok.val; var args = tok.args; @@ -964,13 +952,13 @@ Parser.prototype = { } if (mixin.block.nodes.length === 0) mixin.block = null; return mixin; - }, + } /** * mixin block */ - parseMixin: function() { + parseMixin() { var tok = this.expect('mixin'); var name = tok.val; var args = tok.args; @@ -996,13 +984,13 @@ Parser.prototype = { tok ); } - }, + } /** * indent (text | newline)* outdent */ - parseTextBlock: function() { + parseTextBlock() { var tok = this.accept('start-pipeless-text'); if (!tok) return; var block = this.emptyBlock(tok.loc.start.line); @@ -1055,13 +1043,13 @@ Parser.prototype = { } this.advance(); return block; - }, + } /** * indent expr* outdent */ - block: function() { + block() { var tok = this.expect('indent'); var block = this.emptyBlock(tok.loc.start.line); while ('outdent' != this.peek().type) { @@ -1080,13 +1068,13 @@ Parser.prototype = { } this.expect('outdent'); return block; - }, + } /** * interpolation (attrs | class | id)* (text | code | ':')? newline* block? */ - parseInterpolation: function() { + parseInterpolation() { var tok = this.advance(); var tag = { type: 'InterpolatedTag', @@ -1102,13 +1090,13 @@ Parser.prototype = { }; return this.tag(tag, {selfClosingAllowed: true}); - }, + } /** * tag (attrs | class | id)* (text | code | ':')? newline* block? */ - parseTag: function() { + parseTag() { var tok = this.advance(); var tag = { type: 'Tag', @@ -1124,13 +1112,13 @@ Parser.prototype = { }; return this.tag(tag, {selfClosingAllowed: true}); - }, + } /** * Parse tag. */ - tag: function(tag, options) { + tag(tag, options) { var seenAttrs = false; var attributeNames = []; var selfClosingAllowed = options && options.selfClosingAllowed; @@ -1265,9 +1253,9 @@ Parser.prototype = { } return tag; - }, + } - attrs: function(attributeNames) { + attrs(attributeNames) { this.expect('start-attributes'); var attrs = []; @@ -1296,5 +1284,6 @@ Parser.prototype = { this.tokens.defer(tok); this.expect('end-attributes'); return attrs; - }, -}; + } +} +module.exports.Parser = Parser; diff --git a/packages/pug/test/error.reporting.test.js b/packages/pug/test/error.reporting.test.js index 99ee8e3a4..1815fa6f4 100644 --- a/packages/pug/test/error.reporting.test.js +++ b/packages/pug/test/error.reporting.test.js @@ -209,7 +209,9 @@ describe('error reporting', function() { {} ); expect(err.message).toMatch(/mixin.error.pug:2/); - expect(err.message).toMatch(/Cannot read property 'length' of null/); + expect(err.message).toMatch( + /Cannot read (property 'length' of null|properties of null)/ + ); }); }); describe('in a layout', function() { @@ -220,7 +222,7 @@ describe('error reporting', function() { ); expect(err.message).toMatch(/layout.with.runtime.error.pug:3/); expect(err.message).toMatch( - /Cannot read property 'length' of undefined/ + /Cannot read (property 'length' of undefined|properties of undefined)/ ); }); });