"use strict"; const assert = require("assert"); const acceptableFirstCharacters = /^[a-zA-Z_]$/; const acceptableSubsequentCharacters = /^[0-9a-zA-Z_]$/; // This function deterministically and statelessly translates a name such that it is guaranteed to be valid in all identifier positions in JS. // TODO: This can probably be made more performant... function mangleCharacter(character) { return (character === "$") ? "$$" : `$${character.codePointAt(0)}` } module.exports = function mangleName(name) { assert(name.length > 0); // FIXME: Tag an identifier of this type with an internal property instead if (name.startsWith("$")) { return name; } else { let completedFirstCharacter = false; return Array.from(name) .map((character) => { if (!completedFirstCharacter) { completedFirstCharacter = true; return (acceptableFirstCharacters.test(character)) ? character : mangleCharacter(character); } else { return (acceptableSubsequentCharacters.test(character)) ? character : mangleCharacter(character); } }) .join(""); } };