UNPKG

cst

Version:

JavaScript CST Implementation

247 lines (207 loc) 8.38 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); exports.buildElementTree = buildElementTree; exports.buildTokenList = buildTokenList; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } var _babylon = require('babylon'); var babylon = _interopRequireWildcard(_babylon); var _visitorKeys = require('./visitorKeys'); var _visitorKeys2 = _interopRequireDefault(_visitorKeys); var _elementsElementIndex = require('./elements/elementIndex'); var _elementsElementIndex2 = _interopRequireDefault(_elementsElementIndex); var _elementsToken = require('./elements/Token'); var _elementsToken2 = _interopRequireDefault(_elementsToken); /** * Creates CST using AST and token list. * * @param {Object} ast * @param {Array} tokens * @returns {Program} */ function buildElementTree(ast, tokens) { var firstToken = tokens[0]; ast.start = firstToken.start; ast.end = tokens[tokens.length - 1].end; return buildElementTreeItem(ast, { tokens: tokens, token: firstToken, pos: 0 }); } /** * @param {Object} ast * @param {{tokens: Array, token: Object, pos: Number}} state * @returns {Element} */ function buildElementTreeItem(ast, state) { var elementType = ast.type; var childProps = _visitorKeys2['default'][elementType]; if (!childProps) { throw new Error('Cannot iterate using ' + elementType); } // Babel uses AST-related ranges for cases, but actually they also include all the whitespaces till // the next case or till the end of the switch statement. if (elementType === 'SwitchStatement') { for (var i = 0; i < ast.cases.length; i++) { var switchCase = ast.cases[i]; var nextCase = ast.cases[i + 1]; if (nextCase) { switchCase.end = nextCase.start; } else { switchCase.end = ast.end - 1; } } } var childElements = []; for (var i = 0; i < childProps.length; i++) { var childAst = ast[childProps[i]]; if (childAst) { if (Array.isArray(childAst)) { for (var j = 0; j < childAst.length; j++) { if (childAst[j] !== null) { childElements[childElements.length] = childAst[j]; } } } else { childElements[childElements.length] = childAst; } } } childElements.sort(function (ast1, ast2) { return ast1.start < ast2.start ? -1 : ast1.start > ast2.start ? 1 : 0; }); var NodeClass = _elementsElementIndex2['default'][elementType]; if (!NodeClass) { throw new Error('Cannot create ' + elementType + ' instance'); } var children = []; var childElementIndex = 0; var childElement = childElements[0]; var end = ast.end; do { if (childElement && state.token.start === childElement.start) { if (state.token.end > childElement.end) { var EmptyNodeClass = _elementsElementIndex2['default'][childElement.type]; if (!EmptyNodeClass) { throw new Error('Cannot create ' + childElement.type + ' instance'); } children[children.length] = new EmptyNodeClass([]); childElement = childElements[++childElementIndex]; } else { children[children.length] = buildElementTreeItem(childElement, state); childElement = childElements[++childElementIndex]; if (!state.token || state.token.start === end && (state.token.end !== end || elementType !== 'Program')) { return new NodeClass(children); } } } else { var endOfAstReached = state.token.end === end; var addedTokenType = state.token.type; if (endOfAstReached && ast.type === 'Identifier' && addedTokenType === 'Keyword') { state.token.type = addedTokenType = 'Identifier'; } children[children.length] = _elementsToken2['default'].createFromToken(state.token); state.pos++; state.token = state.tokens[state.pos]; if (elementType === 'Program' && addedTokenType !== 'EOF') { continue; } if (endOfAstReached) { return new NodeClass(children); } } } while (state.token); } /** * Build single token list using code tokens, comments and whitespace. * * @param {Array} codeTokens * @param {String} code * @returns {Array} */ function buildTokenList(codeTokens, code) { var prevPos = 0; var result = []; for (var i = 0; i < codeTokens.length; i++) { var _token = processToken(codeTokens[i], code); var _pos = _token.start; if (prevPos !== _pos) { var _value = code.substring(prevPos, _pos); result[result.length] = { type: 'Whitespace', value: _value, sourceCode: _value, start: prevPos, end: _pos }; } result[result.length] = _token; prevPos = _token.end; } return result; } /** * Babylon token types. */ var tt = babylon.tokTypes; /** * Transforms Babylon-style token to Esprima-style token. * * @param {Object} token * @param {String} source */ function processToken(token, source) { var type = token.type; if (type === tt.name) { token.type = 'Identifier'; } else if (type === tt.semi || type === tt.comma || type === tt.parenL || type === tt.parenR || type === tt.braceL || type === tt.braceR || type === tt.slash || type === tt.dot || type === tt.bracketL || type === tt.bracketR || type === tt.ellipsis || type === tt.arrow || type === tt.star || type === tt.incDec || type === tt.colon || type === tt.question || type === tt.backQuote || type === tt.dollarBraceL || type === tt.at || type === tt.logicalOR || type === tt.logicalAND || type === tt.bitwiseOR || type === tt.bitwiseXOR || type === tt.bitwiseAND || type === tt.equality || type === tt.relational || type === tt.bitShift || type === tt.plusMin || type === tt.modulo || type === tt.exponent || type === tt.prefix || type === tt.doubleColon || type.isAssign) { token.type = 'Punctuator'; if (!token.value) { token.sourceCode = token.value = type.label; } } else if (type === tt.template) { token.type = 'Template'; if (!token.value) { token.sourceCode = token.value = type.label; } } else if (type === tt.jsxTagStart) { token.type = 'Punctuator'; token.sourceCode = token.value = '<'; } else if (type === tt.jsxTagEnd) { token.type = 'Punctuator'; token.sourceCode = token.value = '>'; } else if (type === tt.jsxName) { token.type = 'JSXIdentifier'; } else if (type === tt.jsxText) { token.type = 'JSXText'; } else if (type.keyword === 'null') { token.type = 'Null'; token.value = null; } else if (type.keyword === 'false' || type.keyword === 'true') { token.type = 'Boolean'; token.value = type.keyword === 'true'; } else if (type.keyword) { token.type = 'Keyword'; } else if (type === tt.num) { token.type = 'Numeric'; } else if (type === tt.string) { token.type = 'String'; } else if (type === tt.regexp) { token.type = 'RegularExpression'; token.value = token.value.value; } else if (type === 'CommentLine') { token.sourceCode = '//' + token.value; } else if (type === 'CommentBlock') { token.sourceCode = '/*' + token.value + '*/'; } else if (type === tt.eof) { token.type = 'EOF'; token.sourceCode = token.value = ''; } if (!('sourceCode' in token)) { token.sourceCode = source.slice(token.start, token.end); } return token; }