UNPKG

sucrase

Version:

Super-fast alternative to Babel for when you can target modern JS runtimes

76 lines (75 loc) 3.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tokenizer_1 = require("../sucrase-babylon/tokenizer"); /** * Traverse the given tokens and modify them if necessary to indicate that some names shadow global * variables. */ function identifyShadowedGlobals(tokens, scopes, globalNames) { if (!hasShadowedGlobals(tokens, globalNames)) { return; } markShadowedGlobals(tokens, scopes, globalNames); } exports.default = identifyShadowedGlobals; /** * We can do a fast up-front check to see if there are any declarations to global names. If not, * then there's no point in computing scope assignments. */ function hasShadowedGlobals(tokens, globalNames) { for (const token of tokens) { if (token.type.label === "name" && (token.identifierRole === tokenizer_1.IdentifierRole.FunctionScopedDeclaration || token.identifierRole === tokenizer_1.IdentifierRole.BlockScopedDeclaration) && globalNames.has(token.value)) { return true; } } return false; } function markShadowedGlobals(tokens, scopes, globalNames) { const scopeStack = []; let scopeIndex = scopes.length - 1; // Scopes were generated at completion time, so they're sorted by end index, so we can maintain a // good stack by going backwards through them. for (let i = tokens.length - 1;; i--) { while (scopeStack.length > 0 && scopeStack[scopeStack.length - 1].startTokenIndex === i + 1) { scopeStack.pop(); } while (scopeIndex >= 0 && scopes[scopeIndex].endTokenIndex === i + 1) { scopeStack.push(scopes[scopeIndex]); scopeIndex--; } // Process scopes after the last iteration so we can make sure we pop all of them. if (i < 0) { break; } const token = tokens[i]; if (token.type.label === "name" && globalNames.has(token.value)) { if (token.identifierRole === tokenizer_1.IdentifierRole.BlockScopedDeclaration) { markShadowedForScope(scopeStack[scopeStack.length - 1], tokens, token.value); } else if (token.identifierRole === tokenizer_1.IdentifierRole.FunctionScopedDeclaration) { let stackIndex = scopeStack.length - 1; while (stackIndex > 0 && !scopeStack[stackIndex].isFunctionScope) { stackIndex--; } if (stackIndex < 0) { throw new Error("Did not find parent function scope."); } markShadowedForScope(scopeStack[stackIndex], tokens, token.value); } } } if (scopeStack.length > 0) { throw new Error("Expected empty scope stack after processing file."); } } function markShadowedForScope(scope, tokens, name) { for (let i = scope.startTokenIndex; i < scope.endTokenIndex; i++) { const token = tokens[i]; if (token.type.label === "name" && token.value === name) { token.shadowsGlobal = true; } } }