[d24f17c] | 1 | "use strict";
|
---|
| 2 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
| 3 | exports.unraw = exports.errorMessages = exports.ErrorType = void 0;
|
---|
| 4 | const errors_1 = require("./errors");
|
---|
| 5 | Object.defineProperty(exports, "ErrorType", { enumerable: true, get: function () { return errors_1.ErrorType; } });
|
---|
| 6 | Object.defineProperty(exports, "errorMessages", { enumerable: true, get: function () { return errors_1.errorMessages; } });
|
---|
| 7 | /**
|
---|
| 8 | * Parse a string as a base-16 number. This is more strict than `parseInt` as it
|
---|
| 9 | * will not allow any other characters, including (for example) "+", "-", and
|
---|
| 10 | * ".".
|
---|
| 11 | * @param hex A string containing a hexadecimal number.
|
---|
| 12 | * @returns The parsed integer, or `NaN` if the string is not a valid hex
|
---|
| 13 | * number.
|
---|
| 14 | */
|
---|
| 15 | function parseHexToInt(hex) {
|
---|
| 16 | const isOnlyHexChars = !hex.match(/[^a-f0-9]/i);
|
---|
| 17 | return isOnlyHexChars ? parseInt(hex, 16) : NaN;
|
---|
| 18 | }
|
---|
| 19 | /**
|
---|
| 20 | * Check the validity and length of a hexadecimal code and optionally enforces
|
---|
| 21 | * a specific number of hex digits.
|
---|
| 22 | * @param hex The string to validate and parse.
|
---|
| 23 | * @param errorName The name of the error message to throw a `SyntaxError` with
|
---|
| 24 | * if `hex` is invalid. This is used to index `errorMessages`.
|
---|
| 25 | * @param enforcedLength If provided, will throw an error if `hex` is not
|
---|
| 26 | * exactly this many characters.
|
---|
| 27 | * @returns The parsed hex number as a normal number.
|
---|
| 28 | * @throws {SyntaxError} If the code is not valid.
|
---|
| 29 | */
|
---|
| 30 | function validateAndParseHex(hex, errorName, enforcedLength) {
|
---|
| 31 | const parsedHex = parseHexToInt(hex);
|
---|
| 32 | if (Number.isNaN(parsedHex) ||
|
---|
| 33 | (enforcedLength !== undefined && enforcedLength !== hex.length)) {
|
---|
| 34 | throw new SyntaxError(errors_1.errorMessages.get(errorName));
|
---|
| 35 | }
|
---|
| 36 | return parsedHex;
|
---|
| 37 | }
|
---|
| 38 | /**
|
---|
| 39 | * Parse a two-digit hexadecimal character escape code.
|
---|
| 40 | * @param code The two-digit hexadecimal number that represents the character to
|
---|
| 41 | * output.
|
---|
| 42 | * @returns The single character represented by the code.
|
---|
| 43 | * @throws {SyntaxError} If the code is not valid hex or is not the right
|
---|
| 44 | * length.
|
---|
| 45 | */
|
---|
| 46 | function parseHexadecimalCode(code) {
|
---|
| 47 | const parsedCode = validateAndParseHex(code, errors_1.ErrorType.MalformedHexadecimal, 2);
|
---|
| 48 | return String.fromCharCode(parsedCode);
|
---|
| 49 | }
|
---|
| 50 | /**
|
---|
| 51 | * Parse a four-digit Unicode character escape code.
|
---|
| 52 | * @param code The four-digit unicode number that represents the character to
|
---|
| 53 | * output.
|
---|
| 54 | * @param surrogateCode Optional four-digit unicode surrogate that represents
|
---|
| 55 | * the other half of the character to output.
|
---|
| 56 | * @returns The single character represented by the code.
|
---|
| 57 | * @throws {SyntaxError} If the codes are not valid hex or are not the right
|
---|
| 58 | * length.
|
---|
| 59 | */
|
---|
| 60 | function parseUnicodeCode(code, surrogateCode) {
|
---|
| 61 | const parsedCode = validateAndParseHex(code, errors_1.ErrorType.MalformedUnicode, 4);
|
---|
| 62 | if (surrogateCode !== undefined) {
|
---|
| 63 | const parsedSurrogateCode = validateAndParseHex(surrogateCode, errors_1.ErrorType.MalformedUnicode, 4);
|
---|
| 64 | return String.fromCharCode(parsedCode, parsedSurrogateCode);
|
---|
| 65 | }
|
---|
| 66 | return String.fromCharCode(parsedCode);
|
---|
| 67 | }
|
---|
| 68 | /**
|
---|
| 69 | * Test if the text is surrounded by curly braces (`{}`).
|
---|
| 70 | * @param text Text to check.
|
---|
| 71 | * @returns `true` if the text is in the form `{*}`.
|
---|
| 72 | */
|
---|
| 73 | function isCurlyBraced(text) {
|
---|
| 74 | return text.charAt(0) === "{" && text.charAt(text.length - 1) === "}";
|
---|
| 75 | }
|
---|
| 76 | /**
|
---|
| 77 | * Parse a Unicode code point character escape code.
|
---|
| 78 | * @param codePoint A unicode escape code point, including the surrounding curly
|
---|
| 79 | * braces.
|
---|
| 80 | * @returns The single character represented by the code.
|
---|
| 81 | * @throws {SyntaxError} If the code is not valid hex or does not have the
|
---|
| 82 | * surrounding curly braces.
|
---|
| 83 | */
|
---|
| 84 | function parseUnicodeCodePointCode(codePoint) {
|
---|
| 85 | if (!isCurlyBraced(codePoint)) {
|
---|
| 86 | throw new SyntaxError(errors_1.errorMessages.get(errors_1.ErrorType.MalformedUnicode));
|
---|
| 87 | }
|
---|
| 88 | const withoutBraces = codePoint.slice(1, -1);
|
---|
| 89 | const parsedCode = validateAndParseHex(withoutBraces, errors_1.ErrorType.MalformedUnicode);
|
---|
| 90 | try {
|
---|
| 91 | return String.fromCodePoint(parsedCode);
|
---|
| 92 | }
|
---|
| 93 | catch (err) {
|
---|
| 94 | throw err instanceof RangeError
|
---|
| 95 | ? new SyntaxError(errors_1.errorMessages.get(errors_1.ErrorType.CodePointLimit))
|
---|
| 96 | : err;
|
---|
| 97 | }
|
---|
| 98 | }
|
---|
| 99 | // Have to give overload that takes boolean for when compiler doesn't know if
|
---|
| 100 | // true or false
|
---|
| 101 | function parseOctalCode(code, error = false) {
|
---|
| 102 | if (error) {
|
---|
| 103 | throw new SyntaxError(errors_1.errorMessages.get(errors_1.ErrorType.OctalDeprecation));
|
---|
| 104 | }
|
---|
| 105 | // The original regex only allows digits so we don't need to have a strict
|
---|
| 106 | // octal parser like hexToInt. Length is not enforced for octals.
|
---|
| 107 | const parsedCode = parseInt(code, 8);
|
---|
| 108 | return String.fromCharCode(parsedCode);
|
---|
| 109 | }
|
---|
| 110 | /**
|
---|
| 111 | * Map of unescaped letters to their corresponding special JS escape characters.
|
---|
| 112 | * Intentionally does not include characters that map to themselves like "\'".
|
---|
| 113 | */
|
---|
| 114 | const singleCharacterEscapes = new Map([
|
---|
| 115 | ["b", "\b"],
|
---|
| 116 | ["f", "\f"],
|
---|
| 117 | ["n", "\n"],
|
---|
| 118 | ["r", "\r"],
|
---|
| 119 | ["t", "\t"],
|
---|
| 120 | ["v", "\v"],
|
---|
| 121 | ["0", "\0"]
|
---|
| 122 | ]);
|
---|
| 123 | /**
|
---|
| 124 | * Parse a single character escape sequence and return the matching character.
|
---|
| 125 | * If none is matched, defaults to `code`.
|
---|
| 126 | * @param code A single character code.
|
---|
| 127 | */
|
---|
| 128 | function parseSingleCharacterCode(code) {
|
---|
| 129 | return singleCharacterEscapes.get(code) || code;
|
---|
| 130 | }
|
---|
| 131 | /**
|
---|
| 132 | * Matches every escape sequence possible, including invalid ones.
|
---|
| 133 | *
|
---|
| 134 | * All capture groups (described below) are unique (only one will match), except
|
---|
| 135 | * for 4, which can only potentially match if 3 does.
|
---|
| 136 | *
|
---|
| 137 | * **Capture Groups:**
|
---|
| 138 | * 0. A single backslash
|
---|
| 139 | * 1. Hexadecimal code
|
---|
| 140 | * 2. Unicode code point code with surrounding curly braces
|
---|
| 141 | * 3. Unicode escape code with surrogate
|
---|
| 142 | * 4. Surrogate code
|
---|
| 143 | * 5. Unicode escape code without surrogate
|
---|
| 144 | * 6. Octal code _NOTE: includes "0"._
|
---|
| 145 | * 7. A single character (will never be \, x, u, or 0-3)
|
---|
| 146 | */
|
---|
| 147 | const escapeMatch = /\\(?:(\\)|x([\s\S]{0,2})|u(\{[^}]*\}?)|u([\s\S]{4})\\u([^{][\s\S]{0,3})|u([\s\S]{0,4})|([0-3]?[0-7]{1,2})|([\s\S])|$)/g;
|
---|
| 148 | /**
|
---|
| 149 | * Replace raw escape character strings with their escape characters.
|
---|
| 150 | * @param raw A string where escape characters are represented as raw string
|
---|
| 151 | * values like `\'` rather than `'`.
|
---|
| 152 | * @param allowOctals If `true`, will process the now-deprecated octal escape
|
---|
| 153 | * sequences (ie, `\111`).
|
---|
| 154 | * @returns The processed string, with escape characters replaced by their
|
---|
| 155 | * respective actual Unicode characters.
|
---|
| 156 | */
|
---|
| 157 | function unraw(raw, allowOctals = false) {
|
---|
| 158 | return raw.replace(escapeMatch, function (_, backslash, hex, codePoint, unicodeWithSurrogate, surrogate, unicode, octal, singleCharacter) {
|
---|
| 159 | // Compare groups to undefined because empty strings mean different errors
|
---|
| 160 | // Otherwise, `\u` would fail the same as `\` which is wrong.
|
---|
| 161 | if (backslash !== undefined) {
|
---|
| 162 | return "\\";
|
---|
| 163 | }
|
---|
| 164 | if (hex !== undefined) {
|
---|
| 165 | return parseHexadecimalCode(hex);
|
---|
| 166 | }
|
---|
| 167 | if (codePoint !== undefined) {
|
---|
| 168 | return parseUnicodeCodePointCode(codePoint);
|
---|
| 169 | }
|
---|
| 170 | if (unicodeWithSurrogate !== undefined) {
|
---|
| 171 | return parseUnicodeCode(unicodeWithSurrogate, surrogate);
|
---|
| 172 | }
|
---|
| 173 | if (unicode !== undefined) {
|
---|
| 174 | return parseUnicodeCode(unicode);
|
---|
| 175 | }
|
---|
| 176 | if (octal === "0") {
|
---|
| 177 | return "\0";
|
---|
| 178 | }
|
---|
| 179 | if (octal !== undefined) {
|
---|
| 180 | return parseOctalCode(octal, !allowOctals);
|
---|
| 181 | }
|
---|
| 182 | if (singleCharacter !== undefined) {
|
---|
| 183 | return parseSingleCharacterCode(singleCharacter);
|
---|
| 184 | }
|
---|
| 185 | throw new SyntaxError(errors_1.errorMessages.get(errors_1.ErrorType.EndOfString));
|
---|
| 186 | });
|
---|
| 187 | }
|
---|
| 188 | exports.unraw = unraw;
|
---|
| 189 | exports.default = unraw;
|
---|