prettier-plugin-blade
Version:
Prettier plugin for Laravel Blade templates
1,758 lines (1,753 loc) • 554 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
default: () => index_default,
languages: () => languages,
options: () => options,
parsers: () => parsers,
printers: () => printers2
});
module.exports = __toCommonJS(index_exports);
// 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 /* DoctypeSt