diff --git a/yaml/_chars.ts b/yaml/_chars.ts index f995ff430a9e..5e4703918467 100644 --- a/yaml/_chars.ts +++ b/yaml/_chars.ts @@ -3,48 +3,48 @@ // Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. // Copyright 2018-2025 the Deno authors. MIT license. -export const BOM = 0xfeff; /* BOM */ -export const TAB = 0x09; /* Tab */ -export const LINE_FEED = 0x0a; /* LF */ -export const CARRIAGE_RETURN = 0x0d; /* CR */ -export const SPACE = 0x20; /* Space */ -export const EXCLAMATION = 0x21; /* ! */ -export const DOUBLE_QUOTE = 0x22; /* " */ -export const SHARP = 0x23; /* # */ -export const PERCENT = 0x25; /* % */ -export const AMPERSAND = 0x26; /* & */ -export const SINGLE_QUOTE = 0x27; /* ' */ -export const ASTERISK = 0x2a; /* * */ -export const PLUS = 0x2b; /* + */ -export const COMMA = 0x2c; /* , */ -export const MINUS = 0x2d; /* - */ -export const DOT = 0x2e; /* . */ -export const COLON = 0x3a; /* : */ -export const SMALLER_THAN = 0x3c; /* < */ -export const GREATER_THAN = 0x3e; /* > */ -export const QUESTION = 0x3f; /* ? */ -export const COMMERCIAL_AT = 0x40; /* @ */ -export const LEFT_SQUARE_BRACKET = 0x5b; /* [ */ -export const BACKSLASH = 0x5c; /* \ */ -export const RIGHT_SQUARE_BRACKET = 0x5d; /* ] */ -export const GRAVE_ACCENT = 0x60; /* ` */ -export const LEFT_CURLY_BRACKET = 0x7b; /* { */ -export const VERTICAL_LINE = 0x7c; /* | */ -export const RIGHT_CURLY_BRACKET = 0x7d; /* } */ +export const BOM = "\uFEFF"; +export const TAB = "\t"; +export const LINE_FEED = "\n"; +export const CARRIAGE_RETURN = "\r"; +export const SPACE = " "; +export const EXCLAMATION = "!"; +export const DOUBLE_QUOTE = '"'; +export const SHARP = "#"; +export const PERCENT = "%"; +export const AMPERSAND = "&"; +export const SINGLE_QUOTE = "'"; +export const ASTERISK = "*"; +export const PLUS = "+"; +export const COMMA = ","; +export const MINUS = "-"; +export const DOT = "."; +export const COLON = ":"; +export const SMALLER_THAN = "<"; +export const GREATER_THAN = ">"; +export const QUESTION = "?"; +export const COMMERCIAL_AT = "@"; +export const LEFT_SQUARE_BRACKET = "["; +export const BACKSLASH = "\\"; +export const RIGHT_SQUARE_BRACKET = "]"; +export const GRAVE_ACCENT = "`"; +export const LEFT_CURLY_BRACKET = "{"; +export const VERTICAL_LINE = "|"; +export const RIGHT_CURLY_BRACKET = "}"; -export function isEOL(c: number): boolean { +export function isEOL(c: string): boolean { return c === LINE_FEED || c === CARRIAGE_RETURN; } -export function isWhiteSpace(c: number): boolean { +export function isWhiteSpace(c: string): boolean { return c === TAB || c === SPACE; } -export function isWhiteSpaceOrEOL(c: number): boolean { +export function isWhiteSpaceOrEOL(c: string): boolean { return isWhiteSpace(c) || isEOL(c); } -export function isFlowIndicator(c: number): boolean { +export function isFlowIndicator(c: string): boolean { return ( c === COMMA || c === LEFT_SQUARE_BRACKET || diff --git a/yaml/_dumper_state.ts b/yaml/_dumper_state.ts index 4cee412a93ea..c90dbba18c0d 100644 --- a/yaml/_dumper_state.ts +++ b/yaml/_dumper_state.ts @@ -120,11 +120,12 @@ function generateNextLine(indent: number, level: number): string { * @link https://yaml.org/spec/1.2.2/ 5.1. Character Set * @return `true` if the character is printable without escaping, `false` otherwise. */ -function isPrintable(c: number): boolean { +function isPrintable(s: string): boolean { + const c = s.charCodeAt(0); return ( (0x00020 <= c && c <= 0x00007e) || (0x000a1 <= c && c <= 0x00d7ff && c !== 0x2028 && c !== 0x2029) || - (0x0e000 <= c && c <= 0x00fffd && c !== BOM) || + (0x0e000 <= c && c <= 0x00fffd && s !== BOM) || (0x10000 <= c && c <= 0x10ffff) ); } @@ -132,7 +133,7 @@ function isPrintable(c: number): boolean { /** * @return `true` if value is allowed after the first character in plain style, `false` otherwise. */ -function isPlainSafe(c: number): boolean { +function isPlainSafe(c: string): boolean { return ( isPrintable(c) && c !== BOM && @@ -149,7 +150,7 @@ function isPlainSafe(c: number): boolean { /** * @return `true` if value is allowed as the first character in plain style, `false` otherwise. */ -function isPlainSafeFirst(c: number): boolean { +function isPlainSafeFirst(c: string): boolean { return ( isPlainSafe(c) && !isWhiteSpace(c) && @@ -191,16 +192,16 @@ function chooseScalarStyle( let hasLineBreak = false; let hasFoldableLine = false; // only checked if shouldTrackWidth let previousLineBreak = -1; // count the first line correctly - let plain = isPlainSafeFirst(string.charCodeAt(0)) && - !isWhiteSpace(string.charCodeAt(string.length - 1)); + let plain = isPlainSafeFirst(string[0]!) && + !isWhiteSpace(string.at(-1)!); - let char: number; + let char: string; let i: number; if (singleLineOnly) { // Case: no block styles. // Check for disallowed characters to rule out plain and single. for (i = 0; i < string.length; i++) { - char = string.charCodeAt(i); + char = string.at(i)!; if (!isPrintable(char)) { return STYLE_DOUBLE; } @@ -209,7 +210,7 @@ function chooseScalarStyle( } else { // Case: block styles permitted. for (i = 0; i < string.length; i++) { - char = string.charCodeAt(i); + char = string.at(i)!; if (char === LINE_FEED) { hasLineBreak = true; // Check if any line can be folded. @@ -358,7 +359,7 @@ function escapeString(string: string): string { } } escapeSeq = ESCAPE_SEQUENCES.get(char); - result += !escapeSeq && isPrintable(char) + result += !escapeSeq && isPrintable(String.fromCharCode(char)) ? string[i] : escapeSeq || charCodeToHexString(char); } @@ -591,7 +592,7 @@ export class DumperState { isKey: false, }); if (string === null) continue; - const linePrefix = LINE_FEED === string.charCodeAt(0) ? "-" : "- "; + const linePrefix = LINE_FEED === string[0] ? "-" : "- "; results.push(`${linePrefix}${string}`); } return results.length ? prefix + results.join(whitespace) : "[]"; @@ -684,11 +685,11 @@ export class DumperState { let pairBuffer = ""; if (explicitPair) { - pairBuffer += keyString.charCodeAt(0) === LINE_FEED ? "?" : "? "; + pairBuffer += keyString[0] === LINE_FEED ? "?" : "? "; } pairBuffer += keyString; if (explicitPair) pairBuffer += separator; - pairBuffer += valueString.charCodeAt(0) === LINE_FEED ? ":" : ": "; + pairBuffer += valueString[0] === LINE_FEED ? ":" : ": "; pairBuffer += valueString; results.push(pairBuffer); } diff --git a/yaml/_loader_state.ts b/yaml/_loader_state.ts index 6e668be54af1..c2a3a7c8a655 100644 --- a/yaml/_loader_state.ts +++ b/yaml/_loader_state.ts @@ -67,31 +67,31 @@ export interface LoaderStateOptions { onWarning?(error: Error): void; } -const ESCAPED_HEX_LENGTHS = new Map([ - [0x78, 2], // x - [0x75, 4], // u - [0x55, 8], // U +const ESCAPED_HEX_LENGTHS = new Map([ + ["x", 2], + ["u", 4], + ["U", 8], ]); -const SIMPLE_ESCAPE_SEQUENCES = new Map([ - [0x30, "\x00"], // 0 - [0x61, "\x07"], // a - [0x62, "\x08"], // b - [0x74, "\x09"], // t - [0x09, "\x09"], // Tab - [0x6e, "\x0A"], // n - [0x76, "\x0B"], // v - [0x66, "\x0C"], // f - [0x72, "\x0D"], // r - [0x65, "\x1B"], // e - [0x20, " "], // Space - [0x22, '"'], // " - [0x2f, "/"], // / - [0x5c, "\\"], // \ - [0x4e, "\x85"], // N - [0x5f, "\xA0"], // _ - [0x4c, "\u2028"], // L - [0x50, "\u2029"], // P +const SIMPLE_ESCAPE_SEQUENCES = new Map([ + ["0", "\x00"], + ["a", "\x07"], + ["b", "\x08"], + ["t", "\x09"], + ["\t", "\x09"], + ["n", "\x0A"], + ["v", "\x0B"], + ["f", "\x0C"], + ["r", "\x0D"], + ["e", "\x1B"], + [" ", " "], + ['"', '"'], + ["/", "/"], + ["\\", "\\"], + ["N", "\x85"], + ["_", "\xA0"], + ["L", "\u2028"], + ["P", "\u2029"], ]); /** @@ -232,7 +232,7 @@ export class LoaderState { let ch = this.peek(); if (ch !== SHARP) return; ch = this.next(); - while (ch !== 0 && !isEOL(ch)) { + while (ch !== "\x00" && !isEOL(ch)) { ch = this.next(); } } @@ -246,7 +246,7 @@ export class LoaderState { } peek(offset = 0) { - return this.input.charCodeAt(this.position + offset); + return this.input[this.position + offset] ?? "\x00"; } next() { this.position += 1; @@ -374,7 +374,7 @@ export class LoaderState { let ch = this.peek(); - while (ch !== 0) { + while (ch !== "\x00") { if (ch !== MINUS) { break; } @@ -403,7 +403,9 @@ export class LoaderState { ch = this.peek(); - if ((this.line === line || this.lineIndent > nodeIndent) && ch !== 0) { + if ( + (this.line === line || this.lineIndent > nodeIndent) && ch !== "\x00" + ) { throw this.#createError( "Cannot read block sequence: bad indentation of a sequence entry", ); @@ -538,7 +540,7 @@ export class LoaderState { let lineBreaks = 0; let ch = this.peek(); - while (ch !== 0) { + while (ch !== "\x00") { this.skipWhitespaces(); ch = this.peek(); @@ -583,7 +585,7 @@ export class LoaderState { ) { ch = this.peek(3); - if (ch === 0 || isWhiteSpaceOrEOL(ch)) { + if (ch === "\x00" || isWhiteSpaceOrEOL(ch)) { return true; } } @@ -620,7 +622,7 @@ export class LoaderState { return false; } - let following: number; + let following: string; if (ch === QUESTION || ch === MINUS) { following = this.peek(1); @@ -638,7 +640,7 @@ export class LoaderState { let captureStart = this.position; let hasPendingContent = false; let line = 0; - while (ch !== 0) { + while (ch !== "\x00") { if (ch === COLON) { following = this.peek(1); @@ -716,7 +718,7 @@ export class LoaderState { let captureEnd = this.position; ch = this.peek(); - while (ch !== 0) { + while (ch !== "\x00") { if (ch === SINGLE_QUOTE) { this.captureSegment(captureStart, this.position, true); ch = this.next(); @@ -764,7 +766,7 @@ export class LoaderState { let captureStart = this.position; let tmp: number; ch = this.peek(); - while (ch !== 0) { + while (ch !== "\x00") { if (ch === DOUBLE_QUOTE) { this.captureSegment(captureStart, this.position, true); this.position++; @@ -776,7 +778,10 @@ export class LoaderState { if (isEOL(ch)) { this.skipSeparationSpace(false, nodeIndent); - } else if (ch < 256 && SIMPLE_ESCAPE_SEQUENCES.has(ch)) { + } else if ( + ch.charCodeAt(0) < 256 && + SIMPLE_ESCAPE_SEQUENCES.has(ch) + ) { this.result += SIMPLE_ESCAPE_SEQUENCES.get(ch); this.position++; } else if ((tmp = ESCAPED_HEX_LENGTHS.get(ch) ?? 0) > 0) { @@ -786,7 +791,7 @@ export class LoaderState { for (; hexLength > 0; hexLength--) { ch = this.next(); - if ((tmp = hexCharCodeToNumber(ch)) >= 0) { + if ((tmp = hexCharCodeToNumber(ch.charCodeAt(0))) >= 0) { hexResult = (hexResult << 4) + tmp; } else { throw this.#createError( @@ -829,7 +834,7 @@ export class LoaderState { } readFlowCollection(nodeIndent: number): boolean { let ch = this.peek(); - let terminator: number; + let terminator: string; let isMapping = true; let result = {}; if (ch === LEFT_SQUARE_BRACKET) { @@ -856,10 +861,10 @@ export class LoaderState { let keyTag: string | null = null; let isExplicitPair = false; let isPair = false; - let following = 0; + let following = ""; let line = 0; const overridableKeys = new Set(); - while (ch !== 0) { + while (ch !== "\x00") { this.skipSeparationSpace(true, nodeIndent); ch = this.peek(); @@ -970,7 +975,7 @@ export class LoaderState { this.result = ""; let tmp = 0; - while (ch !== 0) { + while (ch !== "\x00") { ch = this.next(); if (ch === PLUS || ch === MINUS) { @@ -981,7 +986,7 @@ export class LoaderState { "Cannot read block: chomping mode identifier repeated", ); } - } else if ((tmp = decimalCharCodeToNumber(ch)) >= 0) { + } else if ((tmp = decimalCharCodeToNumber(ch.charCodeAt(0))) >= 0) { if (tmp === 0) { throw this.#createError( "Cannot read block: indentation width must be greater than 0", @@ -1005,7 +1010,7 @@ export class LoaderState { ch = this.peek(); } - while (ch !== 0) { + while (ch !== "\x00") { this.readLineBreak(); this.lineIndent = 0; @@ -1086,7 +1091,7 @@ export class LoaderState { emptyLines = 0; const captureStart = this.position; - while (!isEOL(ch) && ch !== 0) { + while (!isEOL(ch) && ch !== "\x00") { ch = this.next(); } @@ -1100,7 +1105,6 @@ export class LoaderState { const anchor = this.anchor; const result = {}; const overridableKeys = new Set(); - let allowCompact = false; let line: number; let pos: number; @@ -1116,7 +1120,7 @@ export class LoaderState { let ch = this.peek(); - while (ch !== 0) { + while (ch !== "\x00") { const following = this.peek(1); line = this.line; // Save the current line. pos = this.position; @@ -1246,7 +1250,7 @@ export class LoaderState { ch = this.peek(); } - if (this.lineIndent > nodeIndent && ch !== 0) { + if (this.lineIndent > nodeIndent && ch !== "\x00") { throw this.#createError( "Cannot read block: bad indentation of a mapping entry", ); @@ -1314,7 +1318,7 @@ export class LoaderState { if (isVerbatim) { do { ch = this.next(); - } while (ch !== 0 && ch !== GREATER_THAN); + } while (ch !== "\x00" && ch !== GREATER_THAN); if (this.position < this.length) { tagName = this.input.slice(position, this.position); @@ -1325,7 +1329,7 @@ export class LoaderState { ); } } else { - while (ch !== 0 && !isWhiteSpaceOrEOL(ch)) { + while (ch !== "\x00" && !isWhiteSpaceOrEOL(ch)) { if (ch === EXCLAMATION) { if (!isNamed) { tagHandle = this.input.slice(position - 1, this.position + 1); @@ -1391,7 +1395,9 @@ export class LoaderState { ch = this.next(); const position = this.position; - while (ch !== 0 && !isWhiteSpaceOrEOL(ch) && !isFlowIndicator(ch)) { + while ( + ch !== "\x00" && !isWhiteSpaceOrEOL(ch) && !isFlowIndicator(ch) + ) { ch = this.next(); } @@ -1411,7 +1417,9 @@ export class LoaderState { const position = this.position; - while (ch !== 0 && !isWhiteSpaceOrEOL(ch) && !isFlowIndicator(ch)) { + while ( + ch !== "\x00" && !isWhiteSpaceOrEOL(ch) && !isFlowIndicator(ch) + ) { ch = this.next(); } @@ -1607,7 +1615,7 @@ export class LoaderState { this.anchorMap = new Map(); let ch = this.peek(); - while (ch !== 0) { + while (ch !== "\x00") { this.skipSeparationSpace(true, -1); ch = this.peek(); @@ -1620,7 +1628,7 @@ export class LoaderState { ch = this.next(); let position = this.position; - while (ch !== 0 && !isWhiteSpaceOrEOL(ch)) { + while (ch !== "\x00" && !isWhiteSpaceOrEOL(ch)) { ch = this.next(); } @@ -1633,7 +1641,7 @@ export class LoaderState { ); } - while (ch !== 0) { + while (ch !== "\x00") { this.skipWhitespaces(); this.skipComment(); ch = this.peek(); @@ -1642,14 +1650,14 @@ export class LoaderState { position = this.position; - while (ch !== 0 && !isWhiteSpaceOrEOL(ch)) { + while (ch !== "\x00" && !isWhiteSpaceOrEOL(ch)) { ch = this.next(); } directiveArgs.push(this.input.slice(position, this.position)); } - if (ch !== 0) this.readLineBreak(); + if (ch !== "\x00") this.readLineBreak(); switch (directiveName) { case "YAML": diff --git a/yaml/parse.ts b/yaml/parse.ts index 9224275a9d5e..8a4eb7721ef9 100644 --- a/yaml/parse.ts +++ b/yaml/parse.ts @@ -4,7 +4,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. -import { isEOL } from "./_chars.ts"; +import { BOM, isEOL } from "./_chars.ts"; import { LoaderState } from "./_loader_state.ts"; import { SCHEMA_MAP, type SchemaType } from "./_schema.ts"; @@ -37,15 +37,12 @@ function sanitizeInput(input: string) { if (input.length > 0) { // Add trailing `\n` if not exists - if (!isEOL(input.charCodeAt(input.length - 1))) input += "\n"; + if (!isEOL(input.at(-1)!)) input += "\n"; // Strip BOM - if (input.charCodeAt(0) === 0xfeff) input = input.slice(1); + if (input.at(0) === BOM) input = input.slice(1); } - // Use 0 as string terminator. That significantly simplifies bounds check. - input += "\0"; - return input; }