UNPKG

prettier-plugin-blade

Version:

Prettier plugin for Laravel Blade templates

1,770 lines (1,766 loc) 551 kB
// src/lexer/directives.ts var DEFAULT_DIRECTIVES = [ "if", "elseif", "else", "endif", "unless", "endunless", "isset", "endisset", "empty", "endempty", "switch", "case", "break", "default", "endswitch", "foreach", "endforeach", "for", "endfor", "while", "endwhile", "forelse", "endforelse", "continue", "auth", "elseauth", "endauth", "guest", "elseguest", "endguest", "can", "elsecan", "endcan", "canany", "elsecanany", "endcanany", "cannot", "elsecannot", "endcannot", "env", "elseenv", "endenv", "production", "elseproduction", "endproduction", "section", "endsection", "yield", "show", "stop", "append", "overwrite", "extends", "extendsFirst", "parent", "hasSection", "sectionMissing", "endhasSection", "endsectionMissing", "include", "includeIf", "includeWhen", "includeUnless", "includeFirst", "includeIsolated", "each", "once", "endonce", "push", "endpush", "pushOnce", "endPushOnce", "pushIf", "elsePushIf", "elsePush", "endPushIf", "prepend", "endprepend", "prependOnce", "endPrependOnce", "stack", "hasStack", "component", "endcomponent", "endComponentClass", "componentFirst", "endComponentFirst", "slot", "endslot", "props", "aware", "csrf", "method", "error", "enderror", "old", "inject", "dd", "dump", "vite", "viteReactRefresh", "fonts", "json", "js", "unset", "class", "style", "checked", "selected", "disabled", "readonly", "required", "bool", "php", "endphp", "verbatim", "endverbatim", "fragment", "endfragment", "session", "endsession", "context", "endcontext", "lang", "endlang", "choice", "livewire", "livewireStyles", "livewireScripts", "entangle", "this", "persist", "endpersist", "teleport", "endteleport", "volt", // Inertia.js "inertia", "inertiaHead", // Filament "filamentStyles", "filamentScripts", // Blade Icons "svg", // Spatie Permission "role", "endrole", "hasrole", "endhasrole", "hasanyrole", "endhasanyrole", "hasallroles", "endhasallroles", "unlessrole", "endunlessrole", // Pennant "feature", "endfeature", "featureany", "endfeatureany", // Cashier Paddle "paddleJS", "use" ]; var CANONICAL_DIRECTIVE_CASE = /* @__PURE__ */ new Map(); for (const name of DEFAULT_DIRECTIVES) { const lower = name.toLowerCase(); if (!CANONICAL_DIRECTIVE_CASE.has(lower)) { CANONICAL_DIRECTIVE_CASE.set(lower, name); } } var IF_WRAPPER_DIRECTIVES = /* @__PURE__ */ new Set([ "if", "elseif", "unless", "isset", "once", "auth", "elseauth", "guest", "elseguest", "can", "elsecan", "cannot", "elsecannot", "canany", "elsecanany", "env", "elseenv", "production", "elseproduction", "hassection", "sectionmissing", "error", "role", "hasrole", "hasanyrole", "hasallroles", "unlessrole", "pushif", "elsepushif", "hasstack" ]); var AGGRESSIVE_WRAPPER_ORDER = [ "if", "while", "switch", "foreach", "for", "case", "call" ]; function getDirectivePhpWrapperKind(directiveName, context = {}) { const name = directiveName.toLowerCase(); const hasDirective = context.hasDirective; if (context.isConditionLikeDirective?.(name)) { return "if"; } if (name === "for") return "for"; if (name === "foreach" || name === "forelse") return "foreach"; if (name === "while") return "while"; if (name === "switch") return "switch"; if (name === "case") return "case"; if (IF_WRAPPER_DIRECTIVES.has(name)) return "if"; if (hasDirective?.(`else${name}`) && hasDirective(`end${name}`)) { return "if"; } if (name.startsWith("else") && name.length > 4) { const baseName = name.slice(4); if (hasDirective?.(baseName) && hasDirective(`end${baseName}`)) { return "if"; } } return "call"; } function getDirectivePhpWrapperKinds(directiveName, mode, context = {}) { const first = getDirectivePhpWrapperKind(directiveName, context); if (mode === "safe") { return first === "call" ? ["call"] : [first, "call"]; } const kinds = [first]; for (const kind of AGGRESSIVE_WRAPPER_ORDER) { if (!kinds.includes(kind)) { kinds.push(kind); } } return kinds; } function getCanonicalDirectiveName(directiveName) { return CANONICAL_DIRECTIVE_CASE.get(directiveName.toLowerCase()) ?? null; } var Directives = class _Directives { known; _acceptAll; constructor(known, acceptAll) { this.known = known; this._acceptAll = acceptAll; } static acceptAll() { return new _Directives(/* @__PURE__ */ new Set(), true); } static withDefaults(extraNames = []) { const set = /* @__PURE__ */ new Set(); for (const name of DEFAULT_DIRECTIVES) { set.add(name.toLowerCase()); } for (const name of extraNames) { const normalized = name.trim().toLowerCase().replace(/^@/, ""); if (normalized) set.add(normalized); } return new _Directives(set, false); } static empty() { return new _Directives(/* @__PURE__ */ new Set(), false); } static from(names) { const set = /* @__PURE__ */ new Set(); for (const name of names) { set.add(name.toLowerCase()); } return new _Directives(set, false); } isDirective(name) { if (this._acceptAll) return true; return this.known.has(name.toLowerCase()); } isDirectiveLower(nameLower) { if (this._acceptAll) return true; return this.known.has(nameLower); } acceptsAll() { return this._acceptAll; } register(name) { this.known.add(name.toLowerCase()); return this; } }; // src/frontend-attribute-names.ts var DOM_EVENT_NAMES = /* @__PURE__ */ new Set([ "abort", "animationend", "animationiteration", "animationstart", "auxclick", "beforeinput", "beforematch", "beforetoggle", "beforeunload", "blur", "cancel", "canplay", "canplaythrough", "change", "click", "close", "contentvisibilityautostatechange", "contextlost", "contextmenu", "contextrestored", "copy", "cuechange", "cut", "dblclick", "drag", "dragend", "dragenter", "dragleave", "dragover", "dragstart", "drop", "durationchange", "emptied", "ended", "error", "focus", "focusin", "focusout", "formdata", "gotpointercapture", "input", "invalid", "keydown", "keypress", "keyup", "load", "loadeddata", "loadedmetadata", "loadstart", "lostpointercapture", "mousedown", "mouseenter", "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup", "paste", "pause", "play", "playing", "pointercancel", "pointerdown", "pointerenter", "pointerleave", "pointermove", "pointerout", "pointerover", "pointerrawupdate", "pointerup", "progress", "ratechange", "reset", "resize", "scroll", "scrollend", "securitypolicyviolation", "seeked", "seeking", "select", "selectionchange", "selectstart", "slotchange", "stalled", "submit", "suspend", "timeupdate", "toggle", "touchcancel", "touchend", "touchmove", "touchstart", "transitioncancel", "transitionend", "transitionrun", "transitionstart", "unload", "volumechange", "waiting", "webkitanimationend", "webkitanimationiteration", "webkitanimationstart", "webkittransitionend", "wheel" ]); function isFrontendEventStyleAtName(name) { return DOM_EVENT_NAMES.has(name.toLowerCase()); } function isHtmlEventAttribute(name) { const lower = name.toLowerCase(); if (!lower.startsWith("on")) return false; return DOM_EVENT_NAMES.has(lower.slice(2)); } // src/lexer/scan-primitives.ts function isAsciiAlpha(ch) { return ch >= 65 && ch <= 90 || ch >= 97 && ch <= 122; } function isAsciiDigit(ch) { return ch >= 48 && ch <= 57; } function isAsciiAlnum(ch) { return isAsciiAlpha(ch) || isAsciiDigit(ch); } function canStartBladeDirectiveAt(source, pos, boundaryStart = 0) { if (pos <= boundaryStart) return pos === boundaryStart; const prev = source.charCodeAt(pos - 1); return !isAsciiAlnum(prev) && prev !== 64; } function findLineEnding(src, pos, len) { for (let i = pos; i < len; i++) { const ch = src[i]; if (ch === "\n" || ch === "\r") return i; } return -1; } function skipLineEnding(src, pos, len) { if (pos.value >= len) return; const byte = src[pos.value]; if (byte === "\n") { pos.value++; } else if (byte === "\r") { pos.value++; if (pos.value < len && src[pos.value] === "\n") { pos.value++; } } } function skipQuotedString(src, pos, len, quote) { while (pos.value < len) { const quotePos = src.indexOf(quote, pos.value); if (quotePos === -1) { pos.value = len; return; } pos.value = quotePos; let backslashCount = 0; let checkPos = pos.value - 1; while (checkPos >= 0 && src[checkPos] === "\\") { backslashCount++; checkPos--; } pos.value++; if (backslashCount % 2 === 0) { return; } } pos.value = len; } function skipBlockComment(src, pos, len) { while (pos.value < len) { const starPos = src.indexOf("*", pos.value); if (starPos === -1) { pos.value = len; return; } pos.value = starPos + 1; if (pos.value < len && src[pos.value] === "/") { pos.value++; return; } } pos.value = len; } function skipLineComment(src, pos, len) { const lineEndPos = findLineEnding(src, pos.value, len); if (lineEndPos === -1) { pos.value = len; } else { pos.value = lineEndPos; skipLineEnding(src, pos, len); } } function skipLineCommentDetecting(src, pos, len, sequences) { const detected = []; const lineEndPos = findLineEnding(src, pos.value, len); const endPos = lineEndPos === -1 ? len : lineEndPos; for (const sequence of sequences) { const seqLen = sequence.length; let searchPos = pos.value; while (searchPos + seqLen <= endPos) { const foundPos = src.indexOf(sequence, searchPos); if (foundPos === -1 || foundPos >= endPos) { break; } detected.push({ sequence, offset: foundPos }); searchPos = foundPos + seqLen; } } if (lineEndPos === -1) { pos.value = len; } else { pos.value = lineEndPos; skipLineEnding(src, pos, len); } return detected; } function skipHeredoc(src, pos, len) { if (pos.value >= len) return; const isNowdoc = src[pos.value] === "'"; if (isNowdoc) { pos.value++; } const delimStart = pos.value; while (pos.value < len) { const ch = src.charCodeAt(pos.value); if (ch >= 65 && ch <= 90 || ch >= 97 && ch <= 122 || ch >= 48 && ch <= 57 || ch === 95) { pos.value++; } else { break; } } if (pos.value === delimStart) { pos.value = len; return; } const delimiter = src.slice(delimStart, pos.value); if (isNowdoc && pos.value < len && src[pos.value] === "'") { pos.value++; } const lineEndPos = findLineEnding(src, pos.value, len); if (lineEndPos === -1) { pos.value = len; return; } pos.value = lineEndPos; skipLineEnding(src, pos, len); const delimLen = delimiter.length; while (pos.value < len) { if (pos.value + delimLen <= len) { const potentialDelim = src.slice(pos.value, pos.value + delimLen); if (potentialDelim === delimiter) { const afterPos = pos.value + delimLen; if (afterPos >= len || src[afterPos] === "\n" || src[afterPos] === "\r" || src[afterPos] === ";") { pos.value = afterPos; if (pos.value < len && src[pos.value] === ";") { pos.value++; } skipLineEnding(src, pos, len); return; } } } const nextLineEnd = findLineEnding(src, pos.value, len); if (nextLineEnd === -1) { pos.value = len; return; } pos.value = nextLineEnd; skipLineEnding(src, pos, len); } pos.value = len; } function skipTemplateLiteral(src, pos, len) { while (pos.value < len) { const byte = src[pos.value]; if (byte === "`") { pos.value++; return; } else if (byte === "\\") { pos.value += 2; } else if (byte === "$" && pos.value + 1 < len && src[pos.value + 1] === "{") { pos.value += 2; skipTemplateExpression(src, pos, len); } else { pos.value++; } } } function skipTemplateExpression(src, pos, len) { let depth = 1; while (depth > 0 && pos.value < len) { const byte = src[pos.value]; if (byte === "{") { depth++; pos.value++; } else if (byte === "}") { depth--; pos.value++; } else if (byte === "'" || byte === '"') { pos.value++; skipQuotedString(src, pos, len, byte); } else if (byte === "`") { pos.value++; skipTemplateLiteral(src, pos, len); } else if (byte === "/" && pos.value + 1 < len) { const next = src[pos.value + 1]; if (next === "/") { pos.value += 2; skipLineComment(src, pos, len); } else if (next === "*") { pos.value += 2; skipBlockComment(src, pos, len); } else { pos.value++; } } else { pos.value++; } } } function skipBacktickString(src, pos, len) { skipQuotedString(src, pos, len, "`"); } // src/lexer/lexer.ts var RAWTEXT_ELEMENTS = /* @__PURE__ */ new Set(["script", "style"]); var ATTR_LIKE_AT_NAME_CONTINUATION = /* @__PURE__ */ new Set([".", "-", ":", "[", "]"]); var Lexer = class { src; pos = 0; len; state = 0 /* Data */; returnState = 0 /* Data */; tokens = []; errors = []; verbatim = false; verbatimReturnState = null; verbatimStartTokenIndex = null; phpBlock = false; phpBlockStartTokenIndex = null; phpTag = false; attrPhpDirectiveDepth = 0; rawtextTagName = ""; currentTagName = ""; currentTagStart = -1; ignoreRanges; nextIgnoreRangeIndex = 0; ignoreRangeCollector; pendingHtmlCommentOriginState = null; pendingHtmlCommentTagStart = null; pendingBladeCommentOriginState = null; pendingBladeCommentTagStart = null; isAtAttributeCandidate(nameLower, afterNamePos) { if (afterNamePos >= this.len) { return isFrontendEventStyleAtName(nameLower); } const immediate = this.src[afterNamePos]; if (ATTR_LIKE_AT_NAME_CONTINUATION.has(immediate)) { return true; } let pos = afterNamePos; while (pos < this.len && isSpace(this.src.charCodeAt(pos))) { pos++; } if (pos < this.len && this.src[pos] === "=") { return true; } if (!isFrontendEventStyleAtName(nameLower)) { return false; } return pos >= this.len || this.src[pos] === ">" || this.src[pos] === "/" || isSpace(immediate.charCodeAt(0)); } isClosingTag = false; continuedTagName = false; inXmlDeclaration = false; _directives; verbatimStartDirectives = /* @__PURE__ */ new Set(["verbatim"]); verbatimEndDirectives = /* @__PURE__ */ new Set(["endverbatim"]); constructor(source, directives, rawBlockConfig) { this.src = source; this.len = source.length; this._directives = directives ?? Directives.acceptAll(); this.ignoreRanges = rawBlockConfig?.ignoreRanges ?? []; this.ignoreRangeCollector = rawBlockConfig?.ignoreRangeCollector ?? null; for (const directive of rawBlockConfig?.verbatimStartDirectives ?? []) { const normalized = normalizeDirectiveName(directive); if (normalized) this.verbatimStartDirectives.add(normalized); } for (const directive of rawBlockConfig?.verbatimEndDirectives ?? []) { const normalized = normalizeDirectiveName(directive); if (normalized) this.verbatimEndDirectives.add(normalized); } } directives() { return this._directives; } tokenize() { while (this.pos < this.len) { if (this.tryEmitIgnoreRange()) { continue; } switch (this.state) { case 0 /* Data */: this.scanData(); break; case 9 /* RawText */: this.scanRawtext(); break; case 11 /* BladeComment */: this.scanBladeCommentContent(); break; case 10 /* Comment */: this.scanComment(); break; case 2 /* TagName */: this.scanTagName(); break; case 3 /* BeforeAttrName */: this.scanBeforeAttrName(); break; case 4 /* AttrName */: this.scanAttrName(); break; case 5 /* AfterAttrName */: this.scanAfterAttrName(); break; case 6 /* BeforeAttrValue */: this.scanBeforeAttrValue(); break; case 7 /* AttrValueQuoted */: this.scanAttrValueQuoted(); break; case 8 /* AttrValueUnquoted */: this.scanAttrValueUnquoted(); break; default: break; } } if (this.state === 2 /* TagName */ || this.state === 3 /* BeforeAttrName */ || this.state === 4 /* AttrName */ || this.state === 5 /* AfterAttrName */ || this.state === 6 /* BeforeAttrValue */ || this.state === 7 /* AttrValueQuoted */ || this.state === 8 /* AttrValueUnquoted */) { this.emit(32 /* SyntheticClose */, this.pos, this.pos); } this.ignoreRangeCollector?.finish(this.snapshotIgnoreRangeResumeState(), this.len); return { tokens: this.tokens, errors: this.errors }; } emit(type, start, end) { this.tokens.push({ type, start, end }); } snapshotIgnoreRangeResumeState() { return { state: this.state, returnState: this.returnState, rawtextTagName: this.rawtextTagName, currentTagName: this.currentTagName, isClosingTag: this.isClosingTag, continuedTagName: this.continuedTagName, inXmlDeclaration: this.inXmlDeclaration, verbatim: this.verbatim, verbatimReturnState: this.verbatimReturnState, verbatimStartTokenIndex: this.verbatimStartTokenIndex, phpBlock: this.phpBlock, phpBlockStartTokenIndex: this.phpBlockStartTokenIndex, phpTag: this.phpTag, attrPhpDirectiveDepth: this.attrPhpDirectiveDepth }; } restoreIgnoreRangeResumeState(resume) { this.state = resume.state; this.returnState = resume.returnState; this.rawtextTagName = resume.rawtextTagName; this.currentTagName = resume.currentTagName; this.isClosingTag = resume.isClosingTag; this.continuedTagName = resume.continuedTagName; this.inXmlDeclaration = resume.inXmlDeclaration; this.verbatim = resume.verbatim; this.verbatimReturnState = resume.verbatimReturnState; this.verbatimStartTokenIndex = resume.verbatimStartTokenIndex ?? null; this.phpBlock = resume.phpBlock; this.phpBlockStartTokenIndex = resume.phpBlockStartTokenIndex ?? null; this.phpTag = resume.phpTag; this.attrPhpDirectiveDepth = resume.attrPhpDirectiveDepth; } tryEmitIgnoreRange() { const range = this.ignoreRanges[this.nextIgnoreRangeIndex]; if (!range || this.pos !== range.start) { return false; } this.emit(51 /* IgnoreRange */, range.start, range.end); this.pos = range.end; this.restoreIgnoreRangeResumeState(range.resume); this.nextIgnoreRangeIndex++; return true; } nextIgnoreRangeStart() { const range = this.ignoreRanges[this.nextIgnoreRangeIndex]; return range ? range.start : null; } recordBladeComment(start, end, originState, tagStart) { this.ignoreRangeCollector?.handleBladeComment( start, end, originState, tagStart, this.snapshotIgnoreRangeResumeState() ); } recordHtmlComment(start, end, originState, tagStart) { this.ignoreRangeCollector?.handleHtmlComment( start, end, originState, tagStart, this.snapshotIgnoreRangeResumeState() ); } beginBladeCommentCapture(originState, tagStart) { this.pendingBladeCommentOriginState = originState; this.pendingBladeCommentTagStart = tagStart; } beginHtmlCommentCapture(originState, tagStart) { this.pendingHtmlCommentOriginState = originState; this.pendingHtmlCommentTagStart = tagStart; } logError(reason, offset) { this.errors.push({ reason, offset }); } peek() { return this.pos < this.len ? this.src[this.pos] : null; } peekAhead(n) { const p = this.pos + n; return p < this.len ? this.src[p] : null; } skipWhitespace() { while (this.pos < this.len && isSpace(this.src.charCodeAt(this.pos))) { this.pos++; } } skipAndEmitWhitespace() { const start = this.pos; while (this.pos < this.len && isSpace(this.src.charCodeAt(this.pos))) { this.pos++; } if (start < this.pos) { this.emit(9 /* Whitespace */, start, this.pos); } } scanAttributePhpDirectiveContent() { if (this.attrPhpDirectiveDepth <= 0) return false; const start = this.pos; while (this.pos < this.len) { const ch = this.src[this.pos]; if (ch === '"' || ch === "'") { this.pos++; this.skipQuotedStringPrim(ch); continue; } if (ch === "`") { this.pos++; this.skipBacktickStringPrim(); continue; } if (ch === "/" && this.pos + 1 < this.len) { const next = this.src[this.pos + 1]; if (next === "/") { this.pos += 2; while (this.pos < this.len) { const c = this.src[this.pos]; if (c === "\n" || c === "\r") { this.pos++; break; } this.pos++; } continue; } if (next === "*") { this.pos += 2; this.skipBlockCommentPrim(); continue; } } if (ch === "#") { this.pos++; while (this.pos < this.len) { const c = this.src[this.pos]; if (c === "\n" || c === "\r") { this.pos++; break; } this.pos++; } continue; } if (ch === "<" && this.pos + 2 < this.len && this.src[this.pos + 1] === "<" && this.src[this.pos + 2] === "<") { this.pos += 3; this.skipHeredocPrim(); continue; } if (ch === "@" && !this.verbatim && !this.phpTag) { let nameEnd = this.pos + 1; while (nameEnd < this.len) { const cc = this.src.charCodeAt(nameEnd); if (isAsciiAlnum(cc) || cc === 95) { nameEnd++; } else { break; } } if (nameEnd > this.pos + 1) { const name = this.src.slice(this.pos + 1, nameEnd).toLowerCase(); if (name === "php" || name === "endphp") { if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } this.returnState = this.state; this.scanDirective(); return true; } } } this.pos++; } if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } return true; } posRef() { return { value: this.pos }; } syncPos(ref) { this.pos = ref.value; } skipQuotedStringPrim(quote) { const ref = this.posRef(); skipQuotedString(this.src, ref, this.len, quote); this.syncPos(ref); } skipBlockCommentPrim() { const ref = this.posRef(); skipBlockComment(this.src, ref, this.len); this.syncPos(ref); } skipHeredocPrim() { const ref = this.posRef(); skipHeredoc(this.src, ref, this.len); this.syncPos(ref); } skipBacktickStringPrim() { const ref = this.posRef(); skipBacktickString(this.src, ref, this.len); this.syncPos(ref); } skipTemplateLiteralPrim() { const ref = this.posRef(); skipTemplateLiteral(this.src, ref, this.len); this.syncPos(ref); } /** * Skip line comment with warnings about ?> inside comments. */ skipLineCommentWithWarnings() { const ref = this.posRef(); const detected = skipLineCommentDetecting(this.src, ref, this.len, ["?>"]); this.syncPos(ref); for (const d of detected) { this.logError(2 /* PhpCloseTagInComment */, d.offset); } } /** * Detect if the current position starts a Blade/PHP construct. * Used for construct collision detection inside echos. */ detectConstruct() { if (this.pos >= this.len) return false; const byte = this.src[this.pos]; if (byte === "{" && this.peekAhead(1) === "{" && this.peekAhead(2) === "{") { return true; } if (byte === "{" && this.peekAhead(1) === "{" && this.peekAhead(2) === "-" && this.peekAhead(3) === "-") { return true; } if (byte === "{" && this.peekAhead(1) === "!" && this.peekAhead(2) === "!") { return true; } if (byte === "{" && this.peekAhead(1) === "{") { return true; } if (byte === "@" && this.pos + 1 < this.len) { const next = this.src[this.pos + 1]; if (isAsciiAlpha(next.charCodeAt(0))) { return true; } } if (byte === "<" && this.peekAhead(1) === "?") { if (this.phpTagStartLength(this.pos) > 0) { return true; } } return false; } /** * Case-insensitive ASCII word match at a source offset. */ hasAsciiWordAt(pos, word) { if (pos + word.length > this.len) return false; for (let i = 0; i < word.length; i++) { let code = this.src.charCodeAt(pos + i); if (code >= 65 && code <= 90) code += 32; if (code !== word.charCodeAt(i)) { return false; } } return true; } /** * Fast-forward to next character relevant to TSX generic scanning. */ nextInterestingPosForTsxGeneric(fromPos) { for (let i = fromPos; i < this.len; i++) { switch (this.src.charCodeAt(i)) { case 60: // < case 62: // > case 34: // " case 39: // ' case 96: // ` case 64: // @ case 10: // \n case 13: return i; } } return this.len; } /** * Fast-forward to next character relevant to balanced JS-like scanning. */ nextInterestingPosForBalancedJsLike(fromPos) { for (let i = fromPos; i < this.len; i++) { switch (this.src.charCodeAt(i)) { case 123: // { case 125: // } case 40: // ( case 41: // ) case 34: // " case 39: // ' case 96: // ` case 47: // / case 64: // @ case 10: // \n case 13: return i; } } return this.len; } scanData() { const start = this.pos; while (this.pos < this.len) { const nextIgnoreRangeStart = this.nextIgnoreRangeStart(); if (nextIgnoreRangeStart !== null && this.pos === nextIgnoreRangeStart) { if (start < this.pos) { if (this.phpBlock) { this.emit(31 /* PhpBlock */, start, this.pos); } else if (this.phpTag) { this.emit(41 /* PhpContent */, start, this.pos); } else { this.emit(0 /* Text */, start, this.pos); } } return; } const byte = this.src[this.pos]; if (this.phpBlock) { if (byte === "'" || byte === '"') { this.pos++; this.skipQuotedStringPrim(byte); continue; } if (byte === "`") { this.pos++; this.skipBacktickStringPrim(); continue; } if (byte === "/" && this.pos + 1 < this.len) { const next = this.src[this.pos + 1]; if (next === "/") { this.pos += 2; while (this.pos < this.len) { const ch = this.src[this.pos]; if (ch === "\n" || ch === "\r") { this.pos++; break; } this.pos++; } continue; } if (next === "*") { this.pos += 2; this.skipBlockCommentPrim(); continue; } } if (byte === "#") { this.pos++; while (this.pos < this.len) { const ch = this.src[this.pos]; if (ch === "\n" || ch === "\r") { this.pos++; break; } this.pos++; } continue; } if (byte === "<" && this.pos + 2 < this.len && this.src[this.pos + 1] === "<" && this.src[this.pos + 2] === "<") { this.pos += 3; this.skipHeredocPrim(); continue; } if (byte === "@" && this.isEndphpAt(this.pos)) { if (start < this.pos) { this.emit(31 /* PhpBlock */, start, this.pos); } this.scanDirective(); return; } this.pos++; continue; } if (byte === "{" && !this.verbatim && !this.phpBlock && !this.phpTag) { const prevChar = this.pos > 0 ? this.src[this.pos - 1] : null; if (prevChar === "@") { this.pos++; continue; } const next1 = this.peekAhead(1); if (next1 === "{") { const next2 = this.peekAhead(2); const next3 = this.peekAhead(3); if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } if (next2 === "-" && next3 === "-") { this.beginBladeCommentCapture(0 /* Data */, null); this.scanBladeCommentStart(); return; } this.scanEcho(); return; } else if (next1 === "!" && this.pos + 2 < this.len && this.src[this.pos + 2] === "!") { if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } this.scanRawEcho(); return; } else { this.pos++; } } else if (this.verbatim && this.verbatimReturnState === 9 /* RawText */ && this.rawtextTagName.length > 0 && byte === "<" && this.isRawtextClosingTagAt(this.pos, this.rawtextTagName, this.rawtextTagName.length)) { this.logError(0 /* UnexpectedEof */, this.pos); this.recoverUnclosedRawtextVerbatimBlock(start); return; } else if (byte === "@") { if (this.verbatim) { if (this.isVerbatimTerminatorAt(this.pos)) { if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } this.scanDirective(); return; } this.pos++; continue; } if (this.phpTag) { this.pos++; continue; } const canStart = canStartBladeDirectiveAt(this.src, this.pos); if (canStart) { const nextPos = this.pos + 1; let isEscaped = false; if (nextPos < this.len) { const nextByte = this.src[nextPos]; if (nextByte === "@") { isEscaped = true; } else if (nextByte === "{" && nextPos + 1 < this.len) { const afterBrace = this.src[nextPos + 1]; if (afterBrace === "{" || afterBrace === "!") { isEscaped = true; } } } if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } if (isEscaped) { this.emit(17 /* AtSign */, this.pos, this.pos + 1); this.pos++; return; } this.scanDirective(); return; } else { this.pos++; } } else if (byte === "<" && !this.verbatim && !this.phpBlock && !this.phpTag) { if (this.pos + 9 <= this.len && this.src[this.pos + 1] === "!" && this.src.slice(this.pos + 2, this.pos + 9).toLowerCase() === "doctype") { if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } this.scanDoctype(); return; } if (this.pos + 3 <= this.len && this.src[this.pos + 1] === "!" && this.src[this.pos + 2] === "[") { if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } if (this.tryScanCdata()) { return; } this.tryScanConditionalCommentEnd(); return; } if (this.pos + 4 <= this.len && this.src[this.pos + 1] === "!" && this.src[this.pos + 2] === "-" && this.src[this.pos + 3] === "-") { if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } if (this.tryScanConditionalComment()) { return; } this.state = 10 /* Comment */; return; } if (this.pos + 3 <= this.len && this.src[this.pos + 1] === "!" && this.src[this.pos + 2] === "-" && (this.pos + 3 >= this.len || this.src[this.pos + 3] !== "-")) { if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } const bogusStart = this.pos; this.pos += 3; while (this.pos < this.len) { if (this.src[this.pos] === ">") { this.pos++; this.emit(36 /* BogusComment */, bogusStart, this.pos); return; } if (this.pos + 1 < this.len && this.src[this.pos] === "-" && this.src[this.pos + 1] === ">") { this.pos += 2; this.emit(36 /* BogusComment */, bogusStart, this.pos); return; } this.pos++; } this.emit(36 /* BogusComment */, bogusStart, this.pos); return; } if (this.pos + 1 < this.len && this.src[this.pos + 1] === "?") { const scanStart = this.pos; const tokensBefore = this.tokens.length; if (this.tryScanDecl() || this.tryScanProcessingInstruction() || this.tryScanBogusComment()) { if (start < scanStart) { this.tokens.splice(tokensBefore, 0, { type: 0 /* Text */, start, end: scanStart }); } return; } if (this.phpTagStartLength(this.pos) > 0) { if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } this.tryScanPhpTag(); return; } this.pos++; continue; } if (this.pos + 1 < this.len && this.src[this.pos + 1] === "-" && (this.pos + 2 >= this.len || this.src[this.pos + 2] !== "-")) { const scanStart = this.pos; const tokensBefore = this.tokens.length; if (this.tryScanBogusComment()) { if (start < scanStart) { this.tokens.splice(tokensBefore, 0, { type: 0 /* Text */, start, end: scanStart }); } return; } this.pos++; continue; } const nextChar = this.pos + 1 < this.len ? this.src[this.pos + 1] : null; const isValidTagStart = nextChar !== null && (isAsciiAlpha(nextChar.charCodeAt(0)) || nextChar === "/" || nextChar === "_" || nextChar === ">" || nextChar === "{" || nextChar === "@"); if (isValidTagStart) { if (start < this.pos) { this.emit(0 /* Text */, start, this.pos); } this.scanTagOpen(); return; } else { this.pos++; } } else if (byte === "?" && this.phpTag) { if (this.isPhpTagEndAt(this.pos)) { if (start < this.pos) { this.emit(41 /* PhpContent */, start, this.pos); } this.emit(40 /* PhpTagEnd */, this.pos, this.pos + 2); this.pos += 2; this.phpTag = false; return; } this.pos++; } else { this.pos++; } } if (start < this.pos) { if (this.phpBlock) { this.logError(0 /* UnexpectedEof */, this.pos); this.emit(31 /* PhpBlock */, start, this.pos); } else if (this.phpTag) { this.emit(41 /* PhpContent */, start, this.pos); } else if (this.verbatim) { this.logError(0 /* UnexpectedEof */, this.pos); this.emit(0 /* Text */, start, this.pos); } else { this.emit(0 /* Text */, start, this.pos); } } } scanComment() { const start = this.pos; const originState = this.pendingHtmlCommentOriginState ?? 0 /* Data */; const tagStart = this.pendingHtmlCommentTagStart; this.pendingHtmlCommentOriginState = null; this.pendingHtmlCommentTagStart = null; this.emit(10 /* CommentStart */, start, start + 4); this.pos += 4; const contentStart = this.pos; const closePos = this.src.indexOf("-->", this.pos); if (closePos === -1) { if (contentStart < this.len) { this.emit(0 /* Text */, contentStart, this.len); } this.logError(0 /* UnexpectedEof */, this.len); this.pos = this.len; this.state = 0 /* Data */; this.recordHtmlComment(start, this.pos, originState, tagStart); return; } if (contentStart < closePos) { this.emit(0 /* Text */, contentStart, closePos); } this.emit(11 /* CommentEnd */, closePos, closePos + 3); this.pos = closePos + 3; this.state = 0 /* Data */; this.recordHtmlComment(start, this.pos, originState, tagStart); } tryScanBogusComment() { if (this.pos + 1 >= this.len) return false; const start = this.pos; if (this.src[this.pos] === "<" && this.src[this.pos + 1] === "?") { const tagLen = this.phpTagStartLength(this.pos); if (tagLen > 0) return false; this.pos += 2; while (this.pos < this.len) { if (this.src[this.pos] === ">") { this.pos++; this.emit(36 /* BogusComment */, start, this.pos); return true; } this.pos++; } this.pos = start; return false; } if (this.src[this.pos] === "<" && this.src[this.pos + 1] === "-") { if (this.pos + 2 < this.len && this.src[this.pos + 2] === "-") { return false; } this.pos += 2; while (this.pos + 1 < this.len) { if (this.src[this.pos] === "-" && this.src[this.pos + 1] === ">") { this.pos += 2; this.emit(36 /* BogusComment */, start, this.pos); return true; } this.pos++; } this.pos = start; return false; } return false; } scanBladeCommentStart() { const start = this.pos; this.pos += 4; this.emit(12 /* BladeCommentStart */, start, this.pos); this.state = 11 /* BladeComment */; } scanBladeCommentContent() { const start = this.pos; const commentStart = start - 4; const originState = this.pendingBladeCommentOriginState ?? this.returnState; const tagStart = this.pendingBladeCommentTagStart; this.pendingBladeCommentOriginState = null; this.pendingBladeCommentTagStart = null; while (this.pos < this.len) { const closePos = this.src.indexOf("--}}", this.pos); if (closePos === -1) { if (start < this.len) { this.emit(0 /* Text */, start, this.len); } this.pos = this.len; this.logError(0 /* UnexpectedEof */, this.len); this.state = this.returnState; this.returnState = 0 /* Data */; this.recordBladeComment(commentStart, this.pos, originState, tagStart); return; } if (closePos > start) { this.emit(0 /* Text */, start, closePos); } this.emit(13 /* BladeCommentEnd */, closePos, closePos + 4); this.pos = closePos + 4; this.state = this.returnState; this.returnState = 0 /* Data */; this.recordBladeComment(commentStart, this.pos, originState, tagStart); return; } this.state = this.returnState; this.returnState = 0 /* Data */; this.recordBladeComment(commentStart, this.pos, originState, tagStart); } tryScanConditionalComment() { if (this.pos + 5 >= this.len) return false; if (this.src.slice(this.pos, this.pos + 5) !== "<!--[") return false; const start = this.pos; let scanPos = this.pos + 5; while (scanPos + 1 < this.len) { if (this.src[scanPos] === "]" && this.src[scanPos + 1] === ">") { const afterMarker = scanPos + 2; if (afterMarker + 5 <= this.len && this.src.slice(afterMarker, afterMarker + 5) === "<!-->") { return false; } this.pos = afterMarker; this.emit(37 /* ConditionalCommentStart */, start, this.pos); return true; } scanPos++; } this.pos = this.len; this.emit(37 /* ConditionalCommentStart */, start, this.pos); return true; } tryScanConditionalCommentEnd() { if (this.pos + 3 > this.len) return false; if (this.src.slice(this.pos, this.pos + 3) !== "<![") return false; const start = this.pos; this.pos += 3; return this.scanConditionalClosing(start); } scanConditionalClosing(start) { while (this.pos + 3 < this.len) { if (this.src[this.pos] === "]" && this.src[this.pos + 1] === "-" && this.src[this.pos + 2] === "-" && this.src[this.pos + 3] === ">") { this.pos += 4; this.emit(38 /* ConditionalCommentEnd */, start, this.pos); return true; } this.pos++; } this.pos = this.len; this.emit(38 /* ConditionalCommentEnd */, start, this.pos); return true; } tryScanCdata() { if (this.pos + 9 > this.len) return false; if (this.src.slice(this.pos, this.pos + 9) !== "<![CDATA[") return false; const start = this.pos; this.emit(42 /* CdataStart */, start, start + 9); this.pos += 9; const contentStart = this.pos; const closePos = this.src.indexOf("]]>", this.pos); if (closePos === -1) { if (contentStart < this.len) { this.emit(0 /* Text */, contentStart, this.len); } this.logError(0 /* UnexpectedEof */, this.len); this.pos = this.len; return true; } if (contentStart < closePos) { this.emit(0 /* Text */, contentStart, closePos); } this.emit(43 /* CdataEnd */, closePos, closePos + 3); this.pos = closePos + 3; return true; } tryScanProcessingInstruction() { if (this.pos + 2 > this.len) return false; if (this.src[this.pos] !== "<" || this.src[this.pos + 1] !== "?") { return false; } if (this.pos + 2 >= this.len) return false; const thirdChar = this.src[this.pos + 2]; if (isSpace(thirdChar.charCodeAt(0))) return false; if (!isAsciiAlpha(thirdChar.charCodeAt(0))) return false; if (this.phpTagStartLength(this.pos) > 0) return false; const targetStart = this.pos + 2; let targetEnd = targetStart; while (targetEnd < this.len) { const c = this.src[targetEnd]; const cc = c.charCodeAt(0); if (isAsciiAlnum(cc) || c === "-" || c === "_" || c === ":") { targetEnd++; } else { break; } } const targetName = this.src.slice(targetStart, targetEnd); if (targetName.toLowerCase() === "xml") return false; const start = this.pos; const piStartEnd = targetEnd; this.emit(46 /* PIStart */, start, piStartEnd); this.pos = piStartEnd; const contentStart = this.pos; while (this.pos + 1 < this.len) { if (this.src[this.pos] === "?" && this.src[this.pos + 1] === ">") { if (contentStart < this.pos) { this.emit(0 /* Text */, contentStart, this.pos); } this.emit(47 /* PIEnd */, this.pos, this.pos + 2); this.pos += 2; return true; } this.pos++; } if (contentStart < this.len) { this.emit(0 /* Text */, contentStart, this.len); } this.pos = this.len; this.logError(0 /* UnexpectedEof */, this.len); return true; } tryScanDecl() { if (this.pos + 5 > this.len) return false; if (this.src[this.pos] !== "<" || this.src[this.pos + 1] !== "?") { return false; } const xmlMatch = this.hasAsciiWordAt(this.pos + 2, "xml"); if (!xmlMatch) return false; if (this.pos + 5 < this.len) { const next = this.src[this.pos + 5]; const cc = next.charCodeAt(0); if (isAsciiAlnum(cc) || next === "_" || next === "-" || next === ":") { return false; } } const start = this.pos; this.emit(44 /* DeclStart */, start, start + 5); this.pos += 5; this.inXmlDeclaration = true; this.state = 3 /* BeforeAttrName */; return true; } phpTagStartLength(pos) { if (pos + 2 > this.len || this.src[pos] !== "<" || this.src[pos + 1] !== "?") { return 0; } if (pos + 5 <= this.len) { const matchesPhp = this.hasAsciiWordAt(pos + 2, "php"); if (matchesPhp) { if (pos + 5 < this.len) { const next = this.src[pos + 5]; const cc = next.charCodeAt(0); if (isAsciiAlnum(cc) || next === "_") { return 0; } } return 5; } } if (pos + 3 <= this.len && this.src[pos + 2] === "=") { return 3; } return 0; } isPhpTagEndAt(pos) { return pos + 2 <= this.len && this.src[pos] === "?" && this.src[pos + 1] === ">"; } tryScanPhpTag() { if (this.pos + 1 >= this.len || this.src[this.pos] !== "<" || this.src[this.pos + 1] !== "?") { return false; } const start = this.pos; this.pos += 2; if (this.pos + 3 <= this.len) { const matchesPhp = this.hasAsciiWordAt(this.pos, "php"); if (matchesPhp) { if (this.pos + 3 < this.len) { const next = this.src[this.pos + 3]; const cc = next.charCodeAt(0); if (isAsciiAlnum(cc) || next === "_") { this.pos = start; return false; } } this.pos += 3; } else if (this.pos < this.len && this.src[this.pos] === "=") { this.pos++; } else { this.pos = start; return false; } } else if (this.pos < this.len && this.src[this.pos] === "=") { this.pos++; } else { this.pos = start; return false; } this.emit(39 /* PhpTagStart */, start, this.pos); this.scanPhpContent(); return true; } scanPhpContent() { const start = this.pos; while (true) { if (this.pos >= this.len) { if (this.pos > start) { this.emit(41 /* PhpContent */, start, this.pos); } return; } if (this.pos + 1 < this.len && this.src[this.pos] === "?" && this.src[this.pos + 1] === ">") { if (this.pos > start) { this.emit(41 /* PhpContent */, start, this.pos); } this.emit(40 /* PhpTagEnd */, this.pos, this.pos + 2); this.pos += 2; return; } const byte = this.src[this.pos]; if (byte === '"' || byte === "'") { this.pos++; this.skipQuotedStringPrim(byte); } else if (byte === "/" && this.pos + 1 < this.len) { const next = this.src[this.pos + 1]; if (next === "/") { this.pos += 2; while (this.pos < this.len) { const ch = this.src[this.pos]; if (ch === "\n" || ch === "\r") { this.pos++; break; } if (this.pos + 2 <= this.len && this.src[this.pos] === "?" && this.src[this.pos + 1] === ">") { break; } this.pos++; } } else if (next === "*") { this.pos += 2; this.skipBlockCommentPrim(); } else { this.pos++; } } else if (byte === "#") { this.pos++; while (this.pos < this.len) { const ch = this.src[this.pos]; if (ch === "\n" || ch === "\r") { this.pos++; break; } if (this.pos + 2 <= this.len && this.src[this.pos] === "?" && this.src[this.pos + 1] === ">") { break; } this.pos++; } } else if (byte === "<" && this.pos + 2 < this.len && this.src[this.pos + 1] === "<" && this.src[this.pos + 2] === "<") { this.pos += 3; this.skipHeredocPrim(); } else { this.pos++; } } } scanDoctype() { this.emit(14 /* DoctypeStart */, this.pos, this.pos + 9); this.pos += 9; this.skipAndEmitWhitespace(); const contentStart = this.pos; while (this.pos < this.len && this.src[this.pos] !== ">") { this.pos++; } if (contentStart < this.pos) { this.emit(15 /* Doctype */, contentStart, this.pos); } if (this.pos < this.len && this.src[this.pos] === ">") { this.emit(16 /* DoctypeEnd */, this.pos, this.pos + 1); this.pos++; } } scanTagOpen() { const start = this.pos; this.currentTagName = ""; this.currentTagStart = start; this.emit(1 /* LessThan */, start, start + 1); this.pos++; if (this.pos < this.len && this.src[this.pos] === "/") { this.emit(3 /* Slash */, this.pos, this.pos + 1); this.pos++; this.isClosingTag = true; } else { this.isClosingTag = false; } this.state = 2 /* TagName */; } checkRawtextMode() { if (this.isClosingTag) return 0 /* Data */; const tagNameLower = this.currentTagName.toLowerCase(); if (RAWTEXT_ELEMENTS.has(tagNameLower)) { this.rawtextTagName = tagNameLower; return 9 /* RawText */; } return 0 /* Data */; } scanTagName() { const start = this.pos; if (this.continuedTagName && this.pos < this.len && isSpace(this.src.charCodeAt(this.pos))) { this.continuedTagName = false; this.state = 3 /* BeforeAttrName */; return; } if (!this.continuedTagName) { this.skipWhitespace(); } this.continuedTagName = false; if (this.pos >= this.len || this.src[this.pos] === ">" || this.src[this.pos] === "/") { if (start < this.pos) {