| const createCustomError = require('../utils/createCustomError'); |
| const generate = require('../definition-syntax/generate'); |
| const defaultLoc = { offset: 0, line: 1, column: 1 }; |
| |
| function locateMismatch(matchResult, node) { |
| const tokens = matchResult.tokens; |
| const longestMatch = matchResult.longestMatch; |
| const mismatchNode = longestMatch < tokens.length ? tokens[longestMatch].node || null : null; |
| const badNode = mismatchNode !== node ? mismatchNode : null; |
| let mismatchOffset = 0; |
| let mismatchLength = 0; |
| let entries = 0; |
| let css = ''; |
| let start; |
| let end; |
| |
| for (let i = 0; i < tokens.length; i++) { |
| const token = tokens[i].value; |
| |
| if (i === longestMatch) { |
| mismatchLength = token.length; |
| mismatchOffset = css.length; |
| } |
| |
| if (badNode !== null && tokens[i].node === badNode) { |
| if (i <= longestMatch) { |
| entries++; |
| } else { |
| entries = 0; |
| } |
| } |
| |
| css += token; |
| } |
| |
| if (longestMatch === tokens.length || entries > 1) { // last |
| start = fromLoc(badNode || node, 'end') || buildLoc(defaultLoc, css); |
| end = buildLoc(start); |
| } else { |
| start = fromLoc(badNode, 'start') || |
| buildLoc(fromLoc(node, 'start') || defaultLoc, css.slice(0, mismatchOffset)); |
| end = fromLoc(badNode, 'end') || |
| buildLoc(start, css.substr(mismatchOffset, mismatchLength)); |
| } |
| |
| return { |
| css, |
| mismatchOffset, |
| mismatchLength, |
| start, |
| end |
| }; |
| } |
| |
| function fromLoc(node, point) { |
| const value = node && node.loc && node.loc[point]; |
| |
| if (value) { |
| return 'line' in value ? buildLoc(value) : value; |
| } |
| |
| return null; |
| } |
| |
| function buildLoc({ offset, line, column }, extra) { |
| const loc = { |
| offset, |
| line, |
| column |
| }; |
| |
| if (extra) { |
| const lines = extra.split(/\n|\r\n?|\f/); |
| |
| loc.offset += extra.length; |
| loc.line += lines.length - 1; |
| loc.column = lines.length === 1 ? loc.column + extra.length : lines.pop().length + 1; |
| } |
| |
| return loc; |
| } |
| |
| const SyntaxReferenceError = function(type, referenceName) { |
| const error = createCustomError( |
| 'SyntaxReferenceError', |
| type + (referenceName ? ' `' + referenceName + '`' : '') |
| ); |
| |
| error.reference = referenceName; |
| |
| return error; |
| }; |
| |
| const SyntaxMatchError = function(message, syntax, node, matchResult) { |
| const error = createCustomError('SyntaxMatchError', message); |
| const { |
| css, |
| mismatchOffset, |
| mismatchLength, |
| start, |
| end |
| } = locateMismatch(matchResult, node); |
| |
| error.rawMessage = message; |
| error.syntax = syntax ? generate(syntax) : '<generic>'; |
| error.css = css; |
| error.mismatchOffset = mismatchOffset; |
| error.mismatchLength = mismatchLength; |
| error.message = message + '\n' + |
| ' syntax: ' + error.syntax + '\n' + |
| ' value: ' + (css || '<empty string>') + '\n' + |
| ' --------' + new Array(error.mismatchOffset + 1).join('-') + '^'; |
| |
| Object.assign(error, start); |
| error.loc = { |
| source: (node && node.loc && node.loc.source) || '<unknown>', |
| start, |
| end |
| }; |
| |
| return error; |
| }; |
| |
| module.exports = { |
| SyntaxReferenceError, |
| SyntaxMatchError |
| }; |