'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var t = require('@babel/types'); var assert = require('assert'); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var t__namespace = /*#__PURE__*/_interopNamespace(t); /** * Traverse `ast` with `visitor`. Similar to `@babel/traverse` but much simpler. */ function traverse(ast, visitor) { const queue = [[undefined, ast]]; while (queue.length) { const next = queue.shift(); if (next) { const [parent, node] = next; const { type } = node; if (type in visitor) { const visitorFn = visitor[type]; visitorFn(node, parent); } if (!(type in t__namespace.NODE_FIELDS)) { continue; } const fields = Object.keys(t__namespace.NODE_FIELDS[type]); for (const field of fields) { const value = node[field]; if (Array.isArray(value)) { for (const item of value) { if (item) { queue.push([node, item]); } } } else if (value) { queue.push([node, value]); } } } } } /** * Process a source code string and its AST to produce source code insertions * and removals. */ function process(source, ast) { assert.strict(ast.tokens, `ast must include .tokens property; pass { tokens: true } to babel.parse`); const tokens = ast.tokens; const insertions = []; const removals = []; traverse(ast, { VariableDeclaration(node, parent) { const isForInit = (t__namespace.isForStatement(parent) && parent.init === node) || ((t__namespace.isForInStatement(parent) || t__namespace.isForOfStatement(parent)) && parent.left === node); if (!isForInit) { checkForSemicolon(node); } }, ExpressionStatement(node) { checkForSemicolon(node); }, ReturnStatement(node) { checkForSemicolon(node); }, ThrowStatement(node) { checkForSemicolon(node); }, DoWhileStatement(node) { checkForSemicolon(node); }, DebuggerStatement(node) { checkForSemicolon(node); }, BreakStatement(node) { checkForSemicolon(node); }, ContinueStatement(node) { checkForSemicolon(node); }, ImportDeclaration(node) { checkForSemicolon(node); }, ExportAllDeclaration(node) { checkForSemicolon(node); }, ExportNamedDeclaration(node) { if (!node.declaration) { checkForSemicolon(node); } }, ExportDefaultDeclaration(node) { const { declaration } = node; if (t__namespace.isClassDeclaration(declaration) || t__namespace.isFunctionDeclaration(declaration)) { if (!declaration.id) { checkForSemicolon(node); } } else { checkForSemicolon(node); } }, EmptyStatement(node, parent) { if (!t__namespace.isForStatement(parent) && !t__namespace.isForOfStatement(parent) && !t__namespace.isForInStatement(parent) && !t__namespace.isWhileStatement(parent) && !t__namespace.isDoWhileStatement(parent)) { remove(startOfNode(node), endOfNode(node)); } }, ClassBody(node) { checkClassBodyForSemicolon(tokenAfterToken(firstTokenOfNode(node))); }, ClassMethod(node) { checkClassBodyForSemicolon(tokenAfterToken(lastTokenOfNode(node))); }, }); return { insertions, removals }; /** * Checks a node to see if it's followed by a semicolon. */ function checkForSemicolon(node) { const lastToken = lastTokenOfNode(node); if (sourceOfToken(lastToken) !== ';') { insert(endOfToken(lastToken), ';'); } } /** * Class bodies don't need semicolons. */ function checkClassBodyForSemicolon(token) { while (token) { const source = sourceOfToken(token); if (source === ';') { remove(startOfToken(token), endOfToken(token)); } else { break; } token = tokenAfterToken(token); } } function firstTokenOfNode(node) { for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; if (token.start === node.start) { return token; } } throw new Error(`cannot find first token for node ${node.type} at ` + `${node.loc.start.line}:${node.loc.start.column + 1}`); } function lastTokenOfNode(node) { for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; if (token.end === node.end) { return token; } } throw new Error(`cannot find last token for node ${node.type} at ` + `${node.loc.start.line}:${node.loc.start.column + 1}`); } function tokenAfterToken(token) { const index = tokens.indexOf(token); if (index < 0) { throw new Error(`cannot find token in tokens: ${JSON.stringify(token)}`); } return tokens[index + 1]; } function sourceOfToken(token) { return source.slice(token.start, token.end); } function insert(index, content) { insertions.push({ index, content }); } function remove(start, end) { removals.push({ start, end }); } function startOfNode(node) { return node.start; } function endOfNode(node) { return node.end; } function startOfToken(token) { return token.start; } function endOfToken(token) { return token.end; } } exports.process = process;