@lucidkiln/core 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"format-CJoqhehX.js","names":[],"sources":["../src/formatter/delimiters.ts","../src/formatter/format.ts"],"sourcesContent":["import type { DelimitedPart, Token } from \"../lexer/lexer.js\";\n\n/** A token as the formatter prints it: its source text, unchanged. An interpolated string is one piece. */\ntype Piece = {\n\ttype: Token[\"type\"];\n\ttext: string;\n\tstart: number;\n\tend: number;\n};\n\n/**\n * What a piece does, where that changes its spacing.\n *\n * - `word`: a name, number or string.\n * - `keyword`: a tag's name, or a name such as `and` or `in`. A bracket after it groups rather than calls.\n * - `unary`: a sign that makes a number negative or positive, written against its operand.\n * - `tight`: `.`, `?.`, `..` and a filter's `|`, with no space either side.\n * - `key`: the colon after a key or named argument, or `?` and `?:` marking an optional key in `props`.\n * - `call`: a bracket that calls or indexes the piece before it.\n * - `open`, `close`: other brackets.\n * - `spaced`: operators with a space either side.\n */\ntype Role =\n\t| \"word\"\n\t| \"keyword\"\n\t| \"unary\"\n\t| \"tight\"\n\t| \"key\"\n\t| \"call\"\n\t| \"open\"\n\t| \"close\"\n\t| \"comma\"\n\t| \"spread\"\n\t| \"spaced\";\n\nconst KEYWORDS = new Set([\n\t\"and\",\n\t\"or\",\n\t\"not\",\n\t\"in\",\n\t\"is\",\n\t\"starts\",\n\t\"ends\",\n\t\"with\",\n\t\"as\",\n\t\"import\",\n\t\"ignore\",\n\t\"missing\",\n]);\n\n/** Joins each interpolated string's tokens, from `stringStart` to its matching `stringEnd`, into one piece. */\nconst toPieces = (source: string, tokens: readonly Token[]): Piece[] => {\n\tconst pieces: Piece[] = [];\n\tfor (let index = 0; index < tokens.length; index++) {\n\t\tconst token = tokens[index] as Token;\n\t\tlet end = token.end;\n\t\tif (token.type === \"stringStart\") {\n\t\t\tlet depth = 0;\n\t\t\tfor (; index < tokens.length; index++) {\n\t\t\t\tconst inner = tokens[index] as Token;\n\t\t\t\tif (inner.type === \"stringStart\") depth++;\n\t\t\t\telse if (inner.type === \"stringEnd\" && --depth === 0) break;\n\t\t\t}\n\t\t\tend = (tokens[Math.min(index, tokens.length - 1)] as Token).end;\n\t\t}\n\t\tpieces.push({\n\t\t\ttype: token.type === \"stringStart\" ? \"string\" : token.type,\n\t\t\ttext: source.slice(token.start, end),\n\t\t\tstart: token.start,\n\t\t\tend,\n\t\t});\n\t}\n\treturn pieces;\n};\n\n/** Works out each piece's role. `isTag` means the first piece is a tag's name. */\nconst rolesOf = (pieces: readonly Piece[], isTag: boolean): Role[] => {\n\tconst roles: Role[] = [];\n\t// In `props`, `|` joins types and `?:` marks an optional key, until `=` starts a default value.\n\tconst props = isTag && pieces[0]?.text === \"props\";\n\tlet inDefault = false;\n\t// Open `?`s at each bracket depth, so a conditional's `:` can be told from a key's.\n\tconst conditionals: number[] = [0];\n\n\tfor (const [index, piece] of pieces.entries()) {\n\t\tconst previous = roles[index - 1];\n\t\tconst depth = conditionals.length - 1;\n\t\tconst types = props && !inDefault;\n\t\tlet role: Role;\n\n\t\tif (piece.type !== \"punct\") {\n\t\t\trole =\n\t\t\t\t(isTag && index === 0) ||\n\t\t\t\t(piece.type === \"name\" && KEYWORDS.has(piece.text))\n\t\t\t\t\t? \"keyword\"\n\t\t\t\t\t: \"word\";\n\t\t} else {\n\t\t\tswitch (piece.text) {\n\t\t\t\tcase \"(\":\n\t\t\t\tcase \"[\":\n\t\t\t\t\trole = previous === \"word\" || previous === \"close\" ? \"call\" : \"open\";\n\t\t\t\t\tconditionals.push(0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"{\":\n\t\t\t\t\trole = \"open\";\n\t\t\t\t\tconditionals.push(0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \")\":\n\t\t\t\tcase \"]\":\n\t\t\t\tcase \"}\":\n\t\t\t\t\trole = \"close\";\n\t\t\t\t\tif (conditionals.length > 1) conditionals.pop();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \",\":\n\t\t\t\t\trole = \"comma\";\n\t\t\t\t\tif (props && depth === 0) inDefault = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"...\":\n\t\t\t\t\trole = \"spread\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \".\":\n\t\t\t\tcase \"?.\":\n\t\t\t\tcase \"..\":\n\t\t\t\t\trole = \"tight\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"|\":\n\t\t\t\t\trole = types ? \"spaced\" : \"tight\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"-\":\n\t\t\t\tcase \"+\":\n\t\t\t\t\trole =\n\t\t\t\t\t\tprevious === undefined ||\n\t\t\t\t\t\t(previous !== \"word\" && previous !== \"close\")\n\t\t\t\t\t\t\t? \"unary\"\n\t\t\t\t\t\t\t: \"spaced\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"?\":\n\t\t\t\tcase \"?:\":\n\t\t\t\t\trole = types ? \"key\" : \"spaced\";\n\t\t\t\t\tif (piece.text === \"?\" && !types) {\n\t\t\t\t\t\tconditionals[depth] = (conditionals[depth] ?? 0) + 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \":\":\n\t\t\t\t\tif ((conditionals[depth] ?? 0) > 0) {\n\t\t\t\t\t\tconditionals[depth] = (conditionals[depth] ?? 0) - 1;\n\t\t\t\t\t\trole = \"spaced\";\n\t\t\t\t\t} else role = \"key\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\trole = \"spaced\";\n\t\t\t\t\tif (props && depth === 0 && piece.text === \"=\") inDefault = true;\n\t\t\t}\n\t\t}\n\t\troles.push(role);\n\t}\n\treturn roles;\n};\n\n/** The gap between two pieces: a space or nothing. */\nconst gapBetween = (\n\tprevious: Piece,\n\tpreviousRole: Role,\n\tpiece: Piece,\n\trole: Role,\n): string => {\n\tif (role === \"comma\" || role === \"tight\" || role === \"key\" || role === \"call\")\n\t\treturn \"\";\n\tif (role === \"close\") {\n\t\treturn piece.text === \"}\" && previous.text !== \"{\" ? \" \" : \"\";\n\t}\n\tif (previousRole === \"tight\" || previousRole === \"unary\") return \"\";\n\tif (previousRole === \"spread\") return \"\";\n\tif (previousRole === \"open\" || previousRole === \"call\") {\n\t\treturn previous.text === \"{\" ? \" \" : \"\";\n\t}\n\treturn \" \";\n};\n\n/**\n * Prints one output or tag with normalised spacing. Line breaks between tokens stay; each line after one is indented from `indent`, one step further or to match the brackets it sits inside, using `unit` per step.\n *\n * Returns null for a delimiter that never closed, which the formatter leaves as it is.\n */\nexport const printDelimiter = (\n\tsource: string,\n\tpart: DelimitedPart,\n\tindent: string,\n\tunit: string,\n): string | null => {\n\tconst tokens = part.tokens;\n\tconst open = tokens[0];\n\tconst close = tokens[tokens.length - 1];\n\tif (!open || !close || close.value === \"\" || tokens.length < 3) return null;\n\n\tconst pieces = toPieces(source, tokens.slice(1, -1));\n\tconst roles = rolesOf(pieces, part.kind === \"tag\");\n\tlet out = open.value;\n\tlet previousEnd = part.start + open.value.length;\n\t// The line each open bracket started on. Brackets opened on the same line add one step between them.\n\tconst openedOn: number[] = [];\n\tlet line = 0;\n\t/** Steps of indentation for a new line, once `closers` brackets at its start have closed. */\n\tconst steps = (closers: number) =>\n\t\tnew Set(openedOn.slice(0, openedOn.length - closers)).size;\n\n\tfor (const [index, piece] of pieces.entries()) {\n\t\tconst role = roles[index] as Role;\n\t\tconst gap = source.slice(previousEnd, piece.start);\n\t\tif (gap.includes(\"\\n\")) {\n\t\t\tline++;\n\t\t\t// Closing brackets at the start of a line line up with the line that opened them. Only those on this line count.\n\t\t\tlet closers = 0;\n\t\t\twhile (\n\t\t\t\troles[index + closers] === \"close\" &&\n\t\t\t\t(closers === 0 ||\n\t\t\t\t\t!source\n\t\t\t\t\t\t.slice(\n\t\t\t\t\t\t\t(pieces[index + closers - 1] as Piece).end,\n\t\t\t\t\t\t\t(pieces[index + closers] as Piece).start,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.includes(\"\\n\"))\n\t\t\t) {\n\t\t\t\tclosers++;\n\t\t\t}\n\t\t\tout += `\\n${indent}${unit.repeat(closers > 0 ? steps(closers) : Math.max(steps(0), 1))}`;\n\t\t} else if (index === 0) out += \" \";\n\t\telse {\n\t\t\tout += gapBetween(\n\t\t\t\tpieces[index - 1] as Piece,\n\t\t\t\troles[index - 1] as Role,\n\t\t\t\tpiece,\n\t\t\t\trole,\n\t\t\t);\n\t\t}\n\t\tout += piece.text;\n\t\tif (role === \"open\" || role === \"call\") openedOn.push(line);\n\t\telse if (role === \"close\") openedOn.pop();\n\t\tpreviousEnd = piece.end;\n\t}\n\n\tconst closeStart = part.end - close.value.length;\n\tout += source.slice(previousEnd, closeStart).includes(\"\\n\")\n\t\t? `\\n${indent}`\n\t\t: \" \";\n\treturn out + close.value;\n};\n","import { builtinTests } from \"../builtins/functions.js\";\nimport { KilnSyntaxError } from \"../errors.js\";\nimport { createDiagnostic } from \"../errors.js\";\nimport { type DelimitedPart, type Part, scanParts } from \"../lexer/lexer.js\";\nimport { parse } from \"../parser/parser.js\";\nimport { Source } from \"../source.js\";\nimport { printDelimiter } from \"./delimiters.js\";\n\nexport type FormatOptions = {\n\t/** Indent with tabs. Default `true`. */\n\tuseTabs?: boolean;\n\t/** Spaces per step without tabs, and the width of a tab when moving lines. A whole number of at least 1. Default `4`. */\n\ttabWidth?: number;\n\t/** Only format lines that overlap this range of offsets. */\n\trange?: { start: number; end: number };\n};\n\n/** Replace `start` to `end` of the original source with `text`. */\nexport type FormatEdit = { start: number; end: number; text: string };\n\n/** Thrown when formatting would have changed what a template renders. That is a bug in the formatter. */\nexport class KilnFormatError extends Error {\n\toverride name = \"KilnFormatError\";\n}\n\n/**\n * How the formatter treats one line.\n *\n * - `indent`: set its indentation to `level` steps.\n * - `shift`: move it by as much as line `anchor` moved, keeping its own indentation relative to it. For scripts, styles and comments.\n * - `keep`: leave it alone, for `<pre>`, `<textarea>`, `verbatim`, text inside attribute values, and the body of a `set` or `apply` whose tag shares its line.\n * - `inside`: it starts inside an output or tag, which `printDelimiter` indents.\n */\ntype Line =\n\t| { kind: \"indent\"; start: number; level: number }\n\t| { kind: \"shift\"; start: number; anchor: number }\n\t| { kind: \"keep\" | \"inside\"; start: number };\n\ntype Frame = { kind: \"kiln\" | \"html\"; name: string };\n\n/**\n * Where the scanner is in the HTML.\n *\n * - `tag`: inside `<name ...>` or `</name>`, which started on line `line`. `inner` counts block tags open inside it.\n * - `raw`: inside `<script>` or `<style>`, or kept as written inside `<pre>`, `<textarea>` and the like.\n * - `kept`: inside the body of block tag `name`, output as written: `verbatim`, or a `set` or `apply` whose tag shares\n * its line, where Kiln doesn't remove the body's indentation. `depth` counts the tags of that name open.\n */\ntype Mode =\n\t| { kind: \"data\" }\n\t| {\n\t\t\tkind: \"tag\";\n\t\t\tname: string;\n\t\t\tclosing: boolean;\n\t\t\tquote: string | null;\n\t\t\tline: number;\n\t\t\tatLineStart: boolean;\n\t\t\tinner: number;\n\t }\n\t| { kind: \"comment\"; anchor: number; level: number }\n\t| { kind: \"raw\"; name: string; keep: boolean; anchor: number; level: number }\n\t| { kind: \"kept\"; name: string; depth: number; level: number };\n\nconst VOID_ELEMENTS = new Set([\n\t\"area\",\n\t\"base\",\n\t\"br\",\n\t\"col\",\n\t\"embed\",\n\t\"hr\",\n\t\"img\",\n\t\"input\",\n\t\"link\",\n\t\"meta\",\n\t\"source\",\n\t\"track\",\n\t\"wbr\",\n]);\n\n/** Elements written in `<script>` or `<style>`'s own language, whose lines move together. */\nconst RAW_ELEMENTS = new Set([\"script\", \"style\"]);\n/** Elements whose whitespace shows on the page. Nothing closes `<plaintext>`. */\nconst KEPT_ELEMENTS = new Set([\n\t\"pre\",\n\t\"textarea\",\n\t\"xmp\",\n\t\"listing\",\n\t\"plaintext\",\n]);\n\n/** Whether `</name` starts at `index`, as a whole tag name. */\nconst closesElement = (text: string, index: number, name: string): boolean => {\n\tif (name === \"plaintext\") return false;\n\tconst end = index + 2 + name.length;\n\treturn (\n\t\ttext.slice(index, end).toLowerCase() === `</${name}` &&\n\t\t(end === text.length || /[\\s/>]/.test(text[end] as string))\n\t);\n};\n\n/**\n * Whether a line starting with `trimmed` begins with the tag that ends the outermost `kept` body, and the formatter\n * may indent it. It may when Kiln removes the whitespace before the tag: a `-` trim marker, or a tag alone on its line.\n */\nconst endsKept = (\n\ttrimmed: string,\n\tmode: Extract<Mode, { kind: \"kept\" }>,\n): boolean => {\n\tconst match = /^\\{%(-?)\\s*end([A-Za-z]+)\\s*-?%\\}([^\\n]*)/.exec(trimmed);\n\treturn (\n\t\tmatch !== null &&\n\t\tmatch[2] === mode.name &&\n\t\tmode.depth === 1 &&\n\t\t(match[1] === \"-\" || /^[ \\t\\r]*$/.test(match[3] as string))\n\t);\n};\n\n/** Elements that the next of these siblings closes, such as a `<li>` without `</li>`. */\nconst CLOSED_BY: Record<string, readonly string[]> = {\n\tli: [\"li\"],\n\tdt: [\"dt\", \"dd\"],\n\tdd: [\"dt\", \"dd\"],\n\tp: [\"p\"],\n\toption: [\"option\"],\n\ttr: [\"tr\"],\n\ttd: [\"td\", \"th\"],\n\tth: [\"td\", \"th\"],\n\tthead: [\"tbody\", \"tfoot\"],\n\ttbody: [\"tbody\", \"tfoot\"],\n};\n\n/** Block tags that hold a body, and so indent what follows. */\nconst BLOCK_TAGS = new Set([\n\t\"if\",\n\t\"for\",\n\t\"set\",\n\t\"block\",\n\t\"component\",\n\t\"fill\",\n\t\"slot\",\n\t\"macro\",\n\t\"with\",\n\t\"cache\",\n\t\"island\",\n\t\"add\",\n\t\"apply\",\n\t\"autoescape\",\n\t\"verbatim\",\n]);\n\n/** What a tag does to indentation: opens a body, starts another branch of one, closes one, or nothing. */\nconst roleOf = (\n\tpart: DelimitedPart,\n): { kind: \"open\" | \"close\"; name: string } | { kind: \"branch\" } | null => {\n\tconst name = part.tokens[1];\n\tif (name?.type !== \"name\") return null;\n\tif (BLOCK_TAGS.has(name.value)) {\n\t\tif (\n\t\t\tname.value === \"set\" &&\n\t\t\tpart.tokens.some((token) => token.type === \"punct\" && token.value === \"=\")\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\t// `{% block title page.title %}` is the short form, with no body.\n\t\tif (name.value === \"block\" && part.tokens.length > 4) return null;\n\t\treturn { kind: \"open\", name: name.value };\n\t}\n\tif (name.value === \"else\" || name.value === \"elseif\")\n\t\treturn { kind: \"branch\" };\n\tif (name.value.startsWith(\"end\")) {\n\t\treturn { kind: \"close\", name: name.value.slice(3) };\n\t}\n\treturn null;\n};\n\nconst isLetter = (char: string | undefined) =>\n\tchar !== undefined && /[A-Za-z]/.test(char);\n\nconst isTest = (name: string) => Object.hasOwn(builtinTests, name);\n\n/** The start offset of every line. */\nconst lineStarts = (text: string): number[] => {\n\tconst starts = [0];\n\tfor (let index = text.indexOf(\"\\n\"); index !== -1;) {\n\t\tstarts.push(index + 1);\n\t\tindex = text.indexOf(\"\\n\", index + 1);\n\t}\n\treturn starts;\n};\n\n/**\n * Walks the template once, tracking open block tags and HTML elements, and decides how to treat each line.\n *\n * Returns the lines, which lines may lose trailing whitespace, and the outputs and tags to print.\n */\nconst planLines = (text: string, parts: readonly Part[]) => {\n\tconst starts = lineStarts(text);\n\tconst lines: Line[] = [];\n\tconst trimEnd: boolean[] = [];\n\tconst stack: Frame[] = [];\n\tlet mode: Mode = { kind: \"data\" };\n\tlet atLineStart = true;\n\t// A line that starts where the next part does, recorded when that part begins.\n\tlet pendingLine = -1;\n\n\tconst lineText = (index: number) =>\n\t\ttext.slice(starts[index], starts[index + 1] ?? text.length);\n\t/** Whether only spaces and tabs come between `offset` and the end of its line. */\n\tconst endsLine = (offset: number) => {\n\t\tlet index = offset;\n\t\twhile (index < text.length && \" \\t\\r\".includes(text[index] as string)) {\n\t\t\tindex++;\n\t\t}\n\t\treturn index === text.length || text[index] === \"\\n\";\n\t};\n\tconst current = () => lines.length - 1;\n\n\t/** Lowers the current line's level when a closing tag comes before anything else on it. */\n\tconst outdent = (line: number, level: number, first: boolean) => {\n\t\tconst info = lines[line];\n\t\tif (first && info?.kind === \"indent\") {\n\t\t\tinfo.level = Math.min(info.level, level);\n\t\t}\n\t};\n\n\tconst levelOf = (line: number): number => {\n\t\tconst info = lines[line];\n\t\treturn info?.kind === \"indent\" ? info.level : stack.length;\n\t};\n\n\t/** Records how to treat the line that starts at `start`, from the current state. */\n\tconst beginLine = (start: number) => {\n\t\tconst index = lines.length;\n\t\tconst trimmed = lineText(index).trimStart();\n\t\tatLineStart = true;\n\t\tswitch (mode.kind) {\n\t\t\tcase \"data\":\n\t\t\t\tlines.push({ kind: \"indent\", start, level: stack.length });\n\t\t\t\treturn;\n\t\t\tcase \"tag\": {\n\t\t\t\tif (mode.quote !== null) {\n\t\t\t\t\tlines.push({ kind: \"keep\", start });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst base = levelOf(mode.line);\n\t\t\t\tconst level =\n\t\t\t\t\ttrimmed.startsWith(\">\") || trimmed.startsWith(\"/>\")\n\t\t\t\t\t\t? base\n\t\t\t\t\t\t: base + 1 + mode.inner;\n\t\t\t\tlines.push({ kind: \"indent\", start, level });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"comment\":\n\t\t\t\tlines.push(\n\t\t\t\t\ttrimmed.startsWith(\"-->\")\n\t\t\t\t\t\t? { kind: \"indent\", start, level: mode.level }\n\t\t\t\t\t\t: { kind: \"shift\", start, anchor: mode.anchor },\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase \"raw\":\n\t\t\t\tif (mode.keep) lines.push({ kind: \"keep\", start });\n\t\t\t\telse if (closesElement(trimmed, 0, mode.name)) {\n\t\t\t\t\tlines.push({ kind: \"indent\", start, level: mode.level });\n\t\t\t\t} else lines.push({ kind: \"shift\", start, anchor: mode.anchor });\n\t\t\t\treturn;\n\t\t\tcase \"kept\":\n\t\t\t\tlines.push(\n\t\t\t\t\tendsKept(trimmed, mode)\n\t\t\t\t\t\t? { kind: \"indent\", start, level: mode.level }\n\t\t\t\t\t\t: { kind: \"keep\", start },\n\t\t\t\t);\n\t\t}\n\t};\n\n\t/** Whether trailing whitespace on a line ending here can go. Not inside attribute values or kept elements. */\n\tconst canTrimHere = () =>\n\t\t!(mode.kind === \"tag\" && mode.quote !== null) &&\n\t\t!(mode.kind === \"raw\" && mode.keep) &&\n\t\tmode.kind !== \"kept\";\n\n\tconst finishTag = (\n\t\ttag: Extract<Mode, { kind: \"tag\" }>,\n\t\tselfClosing: boolean,\n\t) => {\n\t\tmode = { kind: \"data\" };\n\t\tconst name = tag.name;\n\t\tif (tag.closing) {\n\t\t\t// Close the nearest open element of this name, without crossing a block tag.\n\t\t\tfor (let index = stack.length - 1; index >= 0; index--) {\n\t\t\t\tconst frame = stack[index] as Frame;\n\t\t\t\tif (frame.kind === \"kiln\") break;\n\t\t\t\tif (frame.name === name) {\n\t\t\t\t\tstack.length = index;\n\t\t\t\t\toutdent(tag.line, index, tag.atLineStart);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (name.startsWith(\"!\") || selfClosing || VOID_ELEMENTS.has(name)) return;\n\t\tif (RAW_ELEMENTS.has(name) || KEPT_ELEMENTS.has(name)) {\n\t\t\tmode = {\n\t\t\t\tkind: \"raw\",\n\t\t\t\tname,\n\t\t\t\tkeep: KEPT_ELEMENTS.has(name),\n\t\t\t\tanchor: tag.line,\n\t\t\t\tlevel: stack.length,\n\t\t\t};\n\t\t\treturn;\n\t\t}\n\t\tconst top = stack[stack.length - 1];\n\t\tif (top?.kind === \"html\" && CLOSED_BY[top.name]?.includes(name)) {\n\t\t\tstack.pop();\n\t\t\toutdent(tag.line, stack.length, tag.atLineStart);\n\t\t}\n\t\tstack.push({ kind: \"html\", name });\n\t};\n\n\t/** Starts a tag at `index` if one starts there, and returns where scanning continues. */\n\tconst startTag = (index: number, closing: boolean): number => {\n\t\tconst nameStart = index + (closing ? 2 : 1);\n\t\tlet nameEnd = nameStart;\n\t\twhile (\n\t\t\tnameEnd < text.length &&\n\t\t\t/[A-Za-z0-9:!_-]/.test(text[nameEnd] as string)\n\t\t) {\n\t\t\tnameEnd++;\n\t\t}\n\t\tmode = {\n\t\t\tkind: \"tag\",\n\t\t\tname: text.slice(nameStart, nameEnd).toLowerCase(),\n\t\t\tclosing,\n\t\t\tquote: null,\n\t\t\tline: current(),\n\t\t\tatLineStart,\n\t\t\tinner: 0,\n\t\t};\n\t\tatLineStart = false;\n\t\treturn nameEnd;\n\t};\n\n\t/** Moves through a text part one character at a time. */\n\tconst scanText = (start: number, end: number) => {\n\t\tfor (let index = start; index < end; index++) {\n\t\t\tconst char = text[index] as string;\n\t\t\tif (char === \"\\n\") {\n\t\t\t\ttrimEnd[current()] = canTrimHere();\n\t\t\t\tif (index + 1 < end) beginLine(index + 1);\n\t\t\t\telse if (index + 1 === text.length) {\n\t\t\t\t\t// The file ends with a line break: there is no further line.\n\t\t\t\t} else pendingLine = index + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tswitch (mode.kind) {\n\t\t\t\tcase \"data\":\n\t\t\t\t\tif (char === \" \" || char === \"\\t\" || char === \"\\r\") break;\n\t\t\t\t\tif (char === \"<\") {\n\t\t\t\t\t\tif (text.startsWith(\"<!--\", index)) {\n\t\t\t\t\t\t\tmode = {\n\t\t\t\t\t\t\t\tkind: \"comment\",\n\t\t\t\t\t\t\t\tanchor: current(),\n\t\t\t\t\t\t\t\tlevel: levelOf(current()),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tatLineStart = false;\n\t\t\t\t\t\t\tindex += 3;\n\t\t\t\t\t\t} else if (text[index + 1] === \"/\" && isLetter(text[index + 2])) {\n\t\t\t\t\t\t\tindex = startTag(index, true) - 1;\n\t\t\t\t\t\t} else if (isLetter(text[index + 1]) || text[index + 1] === \"!\") {\n\t\t\t\t\t\t\tindex = startTag(index, false) - 1;\n\t\t\t\t\t\t} else atLineStart = false;\n\t\t\t\t\t} else atLineStart = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tag\":\n\t\t\t\t\tif (mode.quote !== null) {\n\t\t\t\t\t\tif (char === mode.quote) mode.quote = null;\n\t\t\t\t\t} else if (char === '\"' || char === \"'\") mode.quote = char;\n\t\t\t\t\telse if (char === \">\") finishTag(mode, text[index - 1] === \"/\");\n\t\t\t\t\telse if (char !== \" \" && char !== \"\\t\" && char !== \"\\r\") {\n\t\t\t\t\t\tatLineStart = false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"comment\":\n\t\t\t\t\tif (text.startsWith(\"-->\", index)) {\n\t\t\t\t\t\tmode = { kind: \"data\" };\n\t\t\t\t\t\tindex += 2;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"raw\":\n\t\t\t\t\tif (char === \"<\" && closesElement(text, index, mode.name)) {\n\t\t\t\t\t\tindex = startTag(index, true) - 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"kept\":\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t};\n\n\t/** Applies a block tag to the stack, or to the count of block tags open inside an HTML tag. */\n\tconst applyTag = (part: DelimitedPart) => {\n\t\tconst role = roleOf(part);\n\t\tconst first = atLineStart;\n\t\tatLineStart = false;\n\t\tif (!role) return;\n\t\tif (mode.kind === \"tag\") {\n\t\t\tconst base = levelOf(mode.line) + 1;\n\t\t\tif (role.kind === \"open\") mode.inner++;\n\t\t\telse if (role.kind === \"close\") {\n\t\t\t\tmode.inner = Math.max(0, mode.inner - 1);\n\t\t\t\toutdent(current(), base + mode.inner, first);\n\t\t\t} else outdent(current(), base + mode.inner - 1, first);\n\t\t\treturn;\n\t\t}\n\t\tif (mode.kind === \"kept\") {\n\t\t\tif (role.kind === \"open\" && role.name === mode.name) mode.depth++;\n\t\t\telse if (\n\t\t\t\trole.kind === \"close\" &&\n\t\t\t\trole.name === mode.name &&\n\t\t\t\t--mode.depth === 0\n\t\t\t) {\n\t\t\t\tconst index = stack.findLastIndex((frame) => frame.kind === \"kiln\");\n\t\t\t\tif (index !== -1) stack.length = index;\n\t\t\t\tmode = { kind: \"data\" };\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (mode.kind !== \"data\") return;\n\t\tif (role.kind === \"open\") {\n\t\t\tstack.push({ kind: \"kiln\", name: role.name });\n\t\t\tif (\n\t\t\t\trole.name === \"verbatim\" ||\n\t\t\t\t((role.name === \"set\" || role.name === \"apply\") &&\n\t\t\t\t\t!(first && endsLine(part.end)))\n\t\t\t) {\n\t\t\t\tmode = {\n\t\t\t\t\tkind: \"kept\",\n\t\t\t\t\tname: role.name,\n\t\t\t\t\tdepth: 1,\n\t\t\t\t\tlevel: stack.length - 1,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tconst index =\n\t\t\trole.kind === \"branch\"\n\t\t\t\t? stack.findLastIndex((frame) => frame.kind === \"kiln\")\n\t\t\t\t: stack.findLastIndex(\n\t\t\t\t\t\t(frame) => frame.kind === \"kiln\" && frame.name === role.name,\n\t\t\t\t\t);\n\t\tif (index === -1) return;\n\t\t// Elements opened in this body or branch end with it.\n\t\tstack.length = role.kind === \"branch\" ? index + 1 : index;\n\t\toutdent(current(), index, first);\n\t};\n\n\tbeginLine(0);\n\tconst delimiters: { part: DelimitedPart; line: number }[] = [];\n\n\tfor (const part of parts) {\n\t\tif (pendingLine !== -1) {\n\t\t\tbeginLine(pendingLine);\n\t\t\tpendingLine = -1;\n\t\t}\n\t\tif (part.kind === \"text\") {\n\t\t\tscanText(part.start, part.end);\n\t\t\tcontinue;\n\t\t}\n\t\tconst line = current();\n\t\tif (part.kind === \"comment\") atLineStart = false;\n\t\telse {\n\t\t\tdelimiters.push({ part, line });\n\t\t\tif (part.kind === \"tag\") applyTag(part);\n\t\t\telse atLineStart = false;\n\t\t}\n\t\t// Lines that start inside this delimiter.\n\t\tfor (\n\t\t\tlet index = text.indexOf(\"\\n\", part.start);\n\t\t\tindex !== -1 && index < part.end;\n\t\t\tindex = text.indexOf(\"\\n\", index + 1)\n\t\t) {\n\t\t\ttrimEnd[current()] = part.kind === \"comment\";\n\t\t\tlines.push(\n\t\t\t\tpart.kind === \"comment\"\n\t\t\t\t\t? { kind: \"shift\", start: index + 1, anchor: line }\n\t\t\t\t\t: { kind: \"inside\", start: index + 1 },\n\t\t\t);\n\t\t}\n\t}\n\ttrimEnd[current()] ??= canTrimHere();\n\treturn { lines, trimEnd, delimiters };\n};\n\nconst widthOf = (whitespace: string, tabWidth: number): number => {\n\tlet width = 0;\n\tfor (const char of whitespace) width += char === \"\\t\" ? tabWidth : 1;\n\treturn width;\n};\n\n/**\n * Works out the edits that format a template, without checking them. See `formatEdits`.\n */\nconst planEdits = (\n\ttext: string,\n\tparts: readonly Part[],\n\toptions: Required<Omit<FormatOptions, \"range\">>,\n): FormatEdit[] => {\n\tconst { useTabs, tabWidth } = options;\n\tconst unit = useTabs ? \"\\t\" : \" \".repeat(tabWidth);\n\tconst indentOfWidth = (width: number) =>\n\t\tuseTabs\n\t\t\t? \"\\t\".repeat(Math.floor(width / tabWidth)) + \" \".repeat(width % tabWidth)\n\t\t\t: \" \".repeat(width);\n\tconst { lines, trimEnd, delimiters } = planLines(text, parts);\n\n\tconst oldIndent = (line: number) => {\n\t\tconst start = lines[line]?.start ?? 0;\n\t\tlet end = start;\n\t\twhile (text[end] === \" \" || text[end] === \"\\t\") end++;\n\t\treturn text.slice(start, end);\n\t};\n\t/** Where a line's content ends: at its line break, or the end of the file. */\n\tconst lineEnd = (line: number) => {\n\t\tconst next = lines[line + 1]?.start;\n\t\tif (next !== undefined) return next - 1;\n\t\treturn text.endsWith(\"\\n\") ? text.length - 1 : text.length;\n\t};\n\tconst isBlank = (line: number) => {\n\t\tconst info = lines[line] as Line;\n\t\treturn (\n\t\t\tinfo.kind !== \"keep\" &&\n\t\t\tinfo.kind !== \"inside\" &&\n\t\t\ttext.slice(info.start, lineEnd(line)).trim() === \"\"\n\t\t);\n\t};\n\n\t// Whitespace before the first content and after the last is output as written wherever the template is placed, so\n\t// it stays as it is, as does a file of only whitespace.\n\tlet firstContent = 0;\n\twhile (firstContent < lines.length && isBlank(firstContent)) firstContent++;\n\tlet lastContent = lines.length - 1;\n\twhile (lastContent >= 0 && isBlank(lastContent)) lastContent--;\n\n\tconst newIndents = new Map<number, string | null>();\n\tconst newIndent = (line: number): string | null => {\n\t\tif (newIndents.has(line)) return newIndents.get(line) ?? null;\n\t\tconst info = lines[line];\n\t\tlet indent: string | null = null;\n\t\tif (info?.kind === \"indent\") {\n\t\t\t// The first content line keeps its indentation. See `firstContent`.\n\t\t\tindent =\n\t\t\t\tline === firstContent ? oldIndent(line) : unit.repeat(info.level);\n\t\t} else if (info?.kind === \"shift\") {\n\t\t\tconst anchor = newIndent(info.anchor);\n\t\t\tconst moved =\n\t\t\t\tanchor === null\n\t\t\t\t\t? 0\n\t\t\t\t\t: widthOf(anchor, tabWidth) -\n\t\t\t\t\t\twidthOf(oldIndent(info.anchor), tabWidth);\n\t\t\tindent = indentOfWidth(\n\t\t\t\tMath.max(0, widthOf(oldIndent(line), tabWidth) + moved),\n\t\t\t);\n\t\t}\n\t\tnewIndents.set(line, indent);\n\t\treturn indent;\n\t};\n\n\tconst edits: FormatEdit[] = [];\n\t// Blank lines go where the line before is blank too, so runs become one.\n\tlet previousBlank = false;\n\n\tfor (const [index, info] of lines.entries()) {\n\t\tif (index > lastContent) break;\n\t\tif (index < firstContent) continue;\n\t\tconst start = info.start;\n\t\tconst end = lineEnd(index);\n\t\tif (isBlank(index)) {\n\t\t\tif (info.kind === \"indent\" && previousBlank) {\n\t\t\t\tedits.push({ start, end: end + 1, text: \"\" });\n\t\t\t} else if (end > start) edits.push({ start, end, text: \"\" });\n\t\t\tpreviousBlank = true;\n\t\t\tcontinue;\n\t\t}\n\t\tpreviousBlank = false;\n\n\t\tconst indent = newIndent(index);\n\t\tif (indent !== null) {\n\t\t\tconst current = oldIndent(index);\n\t\t\tif (current !== indent) {\n\t\t\t\tedits.push({ start, end: start + current.length, text: indent });\n\t\t\t}\n\t\t}\n\t\tif (trimEnd[index] && info.kind !== \"keep\" && index !== lastContent) {\n\t\t\tlet from = end;\n\t\t\twhile (from > start && /[ \\t\\r]/.test(text[from - 1] as string)) from--;\n\t\t\tif (from < end && !insideDelimiter(delimiters, from)) {\n\t\t\t\tedits.push({ start: from, end, text: \"\" });\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const { part, line } of delimiters) {\n\t\tconst indent = newIndent(line) ?? oldIndent(line);\n\t\tconst printed = printDelimiter(text, part, indent, unit);\n\t\tif (printed !== null && printed !== text.slice(part.start, part.end)) {\n\t\t\tedits.push({ start: part.start, end: part.end, text: printed });\n\t\t}\n\t}\n\n\treturn edits.sort((a, b) => a.start - b.start || a.end - b.end);\n};\n\nconst insideDelimiter = (\n\tdelimiters: readonly { part: DelimitedPart }[],\n\toffset: number,\n) => delimiters.some(({ part }) => offset > part.start && offset < part.end);\n\nconst applyEdits = (text: string, edits: readonly FormatEdit[]): string => {\n\tlet out = \"\";\n\tlet position = 0;\n\tfor (const edit of edits) {\n\t\tout += text.slice(position, edit.start) + edit.text;\n\t\tposition = edit.end;\n\t}\n\treturn out + text.slice(position);\n};\n\n/** Removes whitespace at the start and end of lines, and blank lines, which the formatter may change. */\nconst normaliseText = (value: string) =>\n\tvalue\n\t\t.replace(/[ \\t\\r\\f\\v]+$/gm, \"\")\n\t\t.replace(/^[ \\t\\f\\v]+/gm, \"\")\n\t\t.replace(/\\n{2,}/g, \"\\n\");\n\n/** A copy of a syntax tree without positions or layout, with text normalised and text left empty by that removed. */\nconst normaliseTree = (value: unknown): unknown => {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t\t\t.map(normaliseTree)\n\t\t\t.filter(\n\t\t\t\t(item) =>\n\t\t\t\t\t!(\n\t\t\t\t\t\ttypeof item === \"object\" &&\n\t\t\t\t\t\titem !== null &&\n\t\t\t\t\t\t(item as { type?: unknown }).type === \"Text\" &&\n\t\t\t\t\t\t(item as { value?: unknown }).value === \"\"\n\t\t\t\t\t),\n\t\t\t);\n\t}\n\tif (typeof value !== \"object\" || value === null) return value;\n\tconst copy: Record<string, unknown> = {};\n\tfor (const [key, item] of Object.entries(value)) {\n\t\tif (\n\t\t\tkey === \"span\" ||\n\t\t\tkey === \"nameSpan\" ||\n\t\t\tkey === \"lines\" ||\n\t\t\tkey === \"indent\"\n\t\t)\n\t\t\tcontinue;\n\t\tcopy[key] =\n\t\t\tkey === \"value\" && (value as { type?: unknown }).type === \"Text\"\n\t\t\t\t? normaliseText(item as string)\n\t\t\t\t: normaliseTree(item);\n\t}\n\treturn copy;\n};\n\n/** The syntax tree as JSON, without the whitespace the formatter may change, so two trees can be compared. */\nconst shape = (text: string): string =>\n\tJSON.stringify(normaliseTree(parse(text, { isTest }).ast));\n\n/**\n * Works out the edits that format a template, in source order and never overlapping. Editors apply these so the cursor and undo history stay put. See `format` for what it throws.\n */\nexport const formatEdits = (\n\ttext: string,\n\toptions: FormatOptions = {},\n): FormatEdit[] => {\n\tconst parsed = parse(text, { isTest });\n\tif (parsed.problems.length > 0) {\n\t\tconst source = new Source(\"template.kiln\", text);\n\t\tthrow new KilnSyntaxError(\n\t\t\tparsed.problems.map((problem) =>\n\t\t\t\tcreateDiagnostic(source, problem.code, problem.message, problem.span),\n\t\t\t),\n\t\t\tsource,\n\t\t);\n\t}\n\n\tconst tabWidth = options.tabWidth ?? 4;\n\tif (!Number.isInteger(tabWidth) || tabWidth < 1) {\n\t\tthrow new RangeError(\n\t\t\t`tabWidth must be a whole number of at least 1, but got ${tabWidth}`,\n\t\t);\n\t}\n\tlet edits = planEdits(text, scanParts(text).parts, {\n\t\tuseTabs: options.useTabs ?? true,\n\t\ttabWidth,\n\t});\n\tconst range = options.range;\n\tif (range) {\n\t\tedits = edits.filter(\n\t\t\t(edit) => edit.end >= range.start && edit.start <= range.end,\n\t\t);\n\t}\n\n\tconst formatted = applyEdits(text, edits);\n\tconst changed = parse(formatted, { isTest });\n\tif (changed.problems.length > 0 || shape(formatted) !== shape(text)) {\n\t\tthrow new KilnFormatError(\n\t\t\t\"Formatting would have changed what this template renders, so nothing was changed. This is a bug in Kiln's formatter.\",\n\t\t);\n\t}\n\treturn edits;\n};\n\n/**\n * Formats a template: indents it to match its structure, and tidies spacing inside outputs and tags. Never changes what it renders.\n *\n * Throws `KilnSyntaxError` when the template has syntax errors, `KilnFormatError` if the safety check fails, and\n * `RangeError` for a `tabWidth` that isn't a whole number of at least 1.\n *\n * ```ts\n * const formatted = format(source, { useTabs: false, tabWidth: 2 });\n * ```\n */\nexport const format = (text: string, options: FormatOptions = {}): string =>\n\tapplyEdits(text, formatEdits(text, options));\n"],"mappings":"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"}
1
+ {"version":3,"file":"format-Di6-GUtH.js","names":[],"sources":["../src/formatter/delimiters.ts","../src/formatter/format.ts"],"sourcesContent":["import type { DelimitedPart, Token } from \"../lexer/lexer.js\";\n\n/** A token as the formatter prints it: its source text, unchanged. An interpolated string is one piece. */\ntype Piece = {\n\ttype: Token[\"type\"];\n\ttext: string;\n\tstart: number;\n\tend: number;\n};\n\n/**\n * What a piece does, where that changes its spacing.\n *\n * - `word`: a name, number or string.\n * - `keyword`: a tag's name, or a name such as `and` or `in`. A bracket after it groups rather than calls.\n * - `unary`: a sign that makes a number negative or positive, written against its operand.\n * - `tight`: `.`, `?.`, `..` and a filter's `|`, with no space either side.\n * - `key`: the colon after a key or named argument, or `?` and `?:` marking an optional key in `props`.\n * - `call`: a bracket that calls or indexes the piece before it.\n * - `open`, `close`: other brackets.\n * - `spaced`: operators with a space either side.\n */\ntype Role =\n\t| \"word\"\n\t| \"keyword\"\n\t| \"unary\"\n\t| \"tight\"\n\t| \"key\"\n\t| \"call\"\n\t| \"open\"\n\t| \"close\"\n\t| \"comma\"\n\t| \"spread\"\n\t| \"spaced\";\n\nconst KEYWORDS = new Set([\n\t\"and\",\n\t\"or\",\n\t\"not\",\n\t\"in\",\n\t\"is\",\n\t\"starts\",\n\t\"ends\",\n\t\"with\",\n\t\"as\",\n\t\"import\",\n\t\"ignore\",\n\t\"missing\",\n]);\n\n/** Joins each interpolated string's tokens, from `stringStart` to its matching `stringEnd`, into one piece. */\nconst toPieces = (source: string, tokens: readonly Token[]): Piece[] => {\n\tconst pieces: Piece[] = [];\n\tfor (let index = 0; index < tokens.length; index++) {\n\t\tconst token = tokens[index] as Token;\n\t\tlet end = token.end;\n\t\tif (token.type === \"stringStart\") {\n\t\t\tlet depth = 0;\n\t\t\tfor (; index < tokens.length; index++) {\n\t\t\t\tconst inner = tokens[index] as Token;\n\t\t\t\tif (inner.type === \"stringStart\") depth++;\n\t\t\t\telse if (inner.type === \"stringEnd\" && --depth === 0) break;\n\t\t\t}\n\t\t\tend = (tokens[Math.min(index, tokens.length - 1)] as Token).end;\n\t\t}\n\t\tpieces.push({\n\t\t\ttype: token.type === \"stringStart\" ? \"string\" : token.type,\n\t\t\ttext: source.slice(token.start, end),\n\t\t\tstart: token.start,\n\t\t\tend,\n\t\t});\n\t}\n\treturn pieces;\n};\n\n/** Works out each piece's role. `isTag` means the first piece is a tag's name. */\nconst rolesOf = (pieces: readonly Piece[], isTag: boolean): Role[] => {\n\tconst roles: Role[] = [];\n\t// In `props`, `|` joins types and `?:` marks an optional key, until `=` starts a default value.\n\tconst props = isTag && pieces[0]?.text === \"props\";\n\tlet inDefault = false;\n\t// Open `?`s at each bracket depth, so a conditional's `:` can be told from a key's.\n\tconst conditionals: number[] = [0];\n\n\tfor (const [index, piece] of pieces.entries()) {\n\t\tconst previous = roles[index - 1];\n\t\tconst depth = conditionals.length - 1;\n\t\tconst types = props && !inDefault;\n\t\tlet role: Role;\n\n\t\tif (piece.type !== \"punct\") {\n\t\t\trole =\n\t\t\t\t(isTag && index === 0) ||\n\t\t\t\t(piece.type === \"name\" && KEYWORDS.has(piece.text))\n\t\t\t\t\t? \"keyword\"\n\t\t\t\t\t: \"word\";\n\t\t} else {\n\t\t\tswitch (piece.text) {\n\t\t\t\tcase \"(\":\n\t\t\t\tcase \"[\":\n\t\t\t\t\trole = previous === \"word\" || previous === \"close\" ? \"call\" : \"open\";\n\t\t\t\t\tconditionals.push(0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"{\":\n\t\t\t\t\trole = \"open\";\n\t\t\t\t\tconditionals.push(0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \")\":\n\t\t\t\tcase \"]\":\n\t\t\t\tcase \"}\":\n\t\t\t\t\trole = \"close\";\n\t\t\t\t\tif (conditionals.length > 1) conditionals.pop();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \",\":\n\t\t\t\t\trole = \"comma\";\n\t\t\t\t\tif (props && depth === 0) inDefault = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"...\":\n\t\t\t\t\trole = \"spread\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \".\":\n\t\t\t\tcase \"?.\":\n\t\t\t\tcase \"..\":\n\t\t\t\t\trole = \"tight\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"|\":\n\t\t\t\t\trole = types ? \"spaced\" : \"tight\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"-\":\n\t\t\t\tcase \"+\":\n\t\t\t\t\trole =\n\t\t\t\t\t\tprevious === undefined ||\n\t\t\t\t\t\t(previous !== \"word\" && previous !== \"close\")\n\t\t\t\t\t\t\t? \"unary\"\n\t\t\t\t\t\t\t: \"spaced\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"?\":\n\t\t\t\tcase \"?:\":\n\t\t\t\t\trole = types ? \"key\" : \"spaced\";\n\t\t\t\t\tif (piece.text === \"?\" && !types) {\n\t\t\t\t\t\tconditionals[depth] = (conditionals[depth] ?? 0) + 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \":\":\n\t\t\t\t\tif ((conditionals[depth] ?? 0) > 0) {\n\t\t\t\t\t\tconditionals[depth] = (conditionals[depth] ?? 0) - 1;\n\t\t\t\t\t\trole = \"spaced\";\n\t\t\t\t\t} else role = \"key\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\trole = \"spaced\";\n\t\t\t\t\tif (props && depth === 0 && piece.text === \"=\") inDefault = true;\n\t\t\t}\n\t\t}\n\t\troles.push(role);\n\t}\n\treturn roles;\n};\n\n/** The gap between two pieces: a space or nothing. */\nconst gapBetween = (\n\tprevious: Piece,\n\tpreviousRole: Role,\n\tpiece: Piece,\n\trole: Role,\n): string => {\n\tif (role === \"comma\" || role === \"tight\" || role === \"key\" || role === \"call\")\n\t\treturn \"\";\n\tif (role === \"close\") {\n\t\treturn piece.text === \"}\" && previous.text !== \"{\" ? \" \" : \"\";\n\t}\n\tif (previousRole === \"tight\" || previousRole === \"unary\") return \"\";\n\tif (previousRole === \"spread\") return \"\";\n\tif (previousRole === \"open\" || previousRole === \"call\") {\n\t\treturn previous.text === \"{\" ? \" \" : \"\";\n\t}\n\treturn \" \";\n};\n\n/**\n * Prints one output or tag with normalised spacing. Line breaks between tokens stay; each line after one is indented from `indent`, one step further or to match the brackets it sits inside, using `unit` per step.\n *\n * Returns null for a delimiter that never closed, which the formatter leaves as it is.\n */\nexport const printDelimiter = (\n\tsource: string,\n\tpart: DelimitedPart,\n\tindent: string,\n\tunit: string,\n): string | null => {\n\tconst tokens = part.tokens;\n\tconst open = tokens[0];\n\tconst close = tokens[tokens.length - 1];\n\tif (!open || !close || close.value === \"\" || tokens.length < 3) return null;\n\n\tconst pieces = toPieces(source, tokens.slice(1, -1));\n\tconst roles = rolesOf(pieces, part.kind === \"tag\");\n\tlet out = open.value;\n\tlet previousEnd = part.start + open.value.length;\n\t// The line each open bracket started on. Brackets opened on the same line add one step between them.\n\tconst openedOn: number[] = [];\n\tlet line = 0;\n\t/** Steps of indentation for a new line, once `closers` brackets at its start have closed. */\n\tconst steps = (closers: number) =>\n\t\tnew Set(openedOn.slice(0, openedOn.length - closers)).size;\n\n\tfor (const [index, piece] of pieces.entries()) {\n\t\tconst role = roles[index] as Role;\n\t\tconst gap = source.slice(previousEnd, piece.start);\n\t\tif (gap.includes(\"\\n\")) {\n\t\t\tline++;\n\t\t\t// Closing brackets at the start of a line line up with the line that opened them. Only those on this line count.\n\t\t\tlet closers = 0;\n\t\t\twhile (\n\t\t\t\troles[index + closers] === \"close\" &&\n\t\t\t\t(closers === 0 ||\n\t\t\t\t\t!source\n\t\t\t\t\t\t.slice(\n\t\t\t\t\t\t\t(pieces[index + closers - 1] as Piece).end,\n\t\t\t\t\t\t\t(pieces[index + closers] as Piece).start,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.includes(\"\\n\"))\n\t\t\t) {\n\t\t\t\tclosers++;\n\t\t\t}\n\t\t\tout += `\\n${indent}${unit.repeat(closers > 0 ? steps(closers) : Math.max(steps(0), 1))}`;\n\t\t} else if (index === 0) out += \" \";\n\t\telse {\n\t\t\tout += gapBetween(\n\t\t\t\tpieces[index - 1] as Piece,\n\t\t\t\troles[index - 1] as Role,\n\t\t\t\tpiece,\n\t\t\t\trole,\n\t\t\t);\n\t\t}\n\t\tout += piece.text;\n\t\tif (role === \"open\" || role === \"call\") openedOn.push(line);\n\t\telse if (role === \"close\") openedOn.pop();\n\t\tpreviousEnd = piece.end;\n\t}\n\n\tconst closeStart = part.end - close.value.length;\n\tout += source.slice(previousEnd, closeStart).includes(\"\\n\")\n\t\t? `\\n${indent}`\n\t\t: \" \";\n\treturn out + close.value;\n};\n","import { builtinTests } from \"../builtins/functions.js\";\nimport { KilnSyntaxError } from \"../errors.js\";\nimport { createDiagnostic } from \"../errors.js\";\nimport { type DelimitedPart, type Part, scanParts } from \"../lexer/lexer.js\";\nimport { parse } from \"../parser/parser.js\";\nimport { Source } from \"../source.js\";\nimport { printDelimiter } from \"./delimiters.js\";\n\nexport type FormatOptions = {\n\t/** Indent with tabs. Default `true`. */\n\tuseTabs?: boolean;\n\t/** Spaces per step without tabs, and the width of a tab when moving lines. A whole number of at least 1. Default `4`. */\n\ttabWidth?: number;\n\t/** Only format lines that overlap this range of offsets. */\n\trange?: { start: number; end: number };\n};\n\n/** Replace `start` to `end` of the original source with `text`. */\nexport type FormatEdit = { start: number; end: number; text: string };\n\n/** Thrown when formatting would have changed what a template renders. That is a bug in the formatter. */\nexport class KilnFormatError extends Error {\n\toverride name = \"KilnFormatError\";\n}\n\n/**\n * How the formatter treats one line.\n *\n * - `indent`: set its indentation to `level` steps.\n * - `shift`: move it by as much as line `anchor` moved, keeping its own indentation relative to it. For scripts, styles and comments.\n * - `keep`: leave it alone, for `<pre>`, `<textarea>`, `verbatim`, text inside attribute values, and the body of a `set` or `apply` whose tag shares its line.\n * - `inside`: it starts inside an output or tag, which `printDelimiter` indents.\n */\ntype Line =\n\t| { kind: \"indent\"; start: number; level: number }\n\t| { kind: \"shift\"; start: number; anchor: number }\n\t| { kind: \"keep\" | \"inside\"; start: number };\n\ntype Frame = { kind: \"kiln\" | \"html\"; name: string };\n\n/**\n * Where the scanner is in the HTML.\n *\n * - `tag`: inside `<name ...>` or `</name>`, which started on line `line`. `inner` counts block tags open inside it.\n * - `raw`: inside `<script>` or `<style>`, or kept as written inside `<pre>`, `<textarea>` and the like.\n * - `kept`: inside the body of block tag `name`, output as written: `verbatim`, or a `set` or `apply` whose tag shares\n * its line, where Kiln doesn't remove the body's indentation. `depth` counts the tags of that name open.\n */\ntype Mode =\n\t| { kind: \"data\" }\n\t| {\n\t\t\tkind: \"tag\";\n\t\t\tname: string;\n\t\t\tclosing: boolean;\n\t\t\tquote: string | null;\n\t\t\tline: number;\n\t\t\tatLineStart: boolean;\n\t\t\tinner: number;\n\t }\n\t| { kind: \"comment\"; anchor: number; level: number }\n\t| { kind: \"raw\"; name: string; keep: boolean; anchor: number; level: number }\n\t| { kind: \"kept\"; name: string; depth: number; level: number };\n\nconst VOID_ELEMENTS = new Set([\n\t\"area\",\n\t\"base\",\n\t\"br\",\n\t\"col\",\n\t\"embed\",\n\t\"hr\",\n\t\"img\",\n\t\"input\",\n\t\"link\",\n\t\"meta\",\n\t\"source\",\n\t\"track\",\n\t\"wbr\",\n]);\n\n/** Elements written in `<script>` or `<style>`'s own language, whose lines move together. */\nconst RAW_ELEMENTS = new Set([\"script\", \"style\"]);\n/** Elements whose whitespace shows on the page. Nothing closes `<plaintext>`. */\nconst KEPT_ELEMENTS = new Set([\n\t\"pre\",\n\t\"textarea\",\n\t\"xmp\",\n\t\"listing\",\n\t\"plaintext\",\n]);\n\n/** Whether `</name` starts at `index`, as a whole tag name. */\nconst closesElement = (text: string, index: number, name: string): boolean => {\n\tif (name === \"plaintext\") return false;\n\tconst end = index + 2 + name.length;\n\treturn (\n\t\ttext.slice(index, end).toLowerCase() === `</${name}` &&\n\t\t(end === text.length || /[\\s/>]/.test(text[end] as string))\n\t);\n};\n\n/**\n * Whether a line starting with `trimmed` begins with the tag that ends the outermost `kept` body, and the formatter\n * may indent it. It may when Kiln removes the whitespace before the tag: a `-` trim marker, or a tag alone on its line.\n */\nconst endsKept = (\n\ttrimmed: string,\n\tmode: Extract<Mode, { kind: \"kept\" }>,\n): boolean => {\n\tconst match = /^\\{%(-?)\\s*end([A-Za-z]+)\\s*-?%\\}([^\\n]*)/.exec(trimmed);\n\treturn (\n\t\tmatch !== null &&\n\t\tmatch[2] === mode.name &&\n\t\tmode.depth === 1 &&\n\t\t(match[1] === \"-\" || /^[ \\t\\r]*$/.test(match[3] as string))\n\t);\n};\n\n/** Elements that the next of these siblings closes, such as a `<li>` without `</li>`. */\nconst CLOSED_BY: Record<string, readonly string[]> = {\n\tli: [\"li\"],\n\tdt: [\"dt\", \"dd\"],\n\tdd: [\"dt\", \"dd\"],\n\tp: [\"p\"],\n\toption: [\"option\"],\n\ttr: [\"tr\"],\n\ttd: [\"td\", \"th\"],\n\tth: [\"td\", \"th\"],\n\tthead: [\"tbody\", \"tfoot\"],\n\ttbody: [\"tbody\", \"tfoot\"],\n};\n\n/** Block tags that hold a body, and so indent what follows. */\nconst BLOCK_TAGS = new Set([\n\t\"if\",\n\t\"for\",\n\t\"set\",\n\t\"block\",\n\t\"component\",\n\t\"fill\",\n\t\"slot\",\n\t\"macro\",\n\t\"with\",\n\t\"cache\",\n\t\"island\",\n\t\"add\",\n\t\"apply\",\n\t\"autoescape\",\n\t\"verbatim\",\n]);\n\n/** What a tag does to indentation: opens a body, starts another branch of one, closes one, or nothing. */\nconst roleOf = (\n\tpart: DelimitedPart,\n): { kind: \"open\" | \"close\"; name: string } | { kind: \"branch\" } | null => {\n\tconst name = part.tokens[1];\n\tif (name?.type !== \"name\") return null;\n\tif (BLOCK_TAGS.has(name.value)) {\n\t\tif (\n\t\t\tname.value === \"set\" &&\n\t\t\tpart.tokens.some((token) => token.type === \"punct\" && token.value === \"=\")\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\t// `{% block title page.title %}` is the short form, with no body.\n\t\tif (name.value === \"block\" && part.tokens.length > 4) return null;\n\t\treturn { kind: \"open\", name: name.value };\n\t}\n\tif (name.value === \"else\" || name.value === \"elseif\")\n\t\treturn { kind: \"branch\" };\n\tif (name.value.startsWith(\"end\")) {\n\t\treturn { kind: \"close\", name: name.value.slice(3) };\n\t}\n\treturn null;\n};\n\nconst isLetter = (char: string | undefined) =>\n\tchar !== undefined && /[A-Za-z]/.test(char);\n\nconst isTest = (name: string) => Object.hasOwn(builtinTests, name);\n\n/** The start offset of every line. */\nconst lineStarts = (text: string): number[] => {\n\tconst starts = [0];\n\tfor (let index = text.indexOf(\"\\n\"); index !== -1;) {\n\t\tstarts.push(index + 1);\n\t\tindex = text.indexOf(\"\\n\", index + 1);\n\t}\n\treturn starts;\n};\n\n/**\n * Walks the template once, tracking open block tags and HTML elements, and decides how to treat each line.\n *\n * Returns the lines, which lines may lose trailing whitespace, and the outputs and tags to print.\n */\nconst planLines = (text: string, parts: readonly Part[]) => {\n\tconst starts = lineStarts(text);\n\tconst lines: Line[] = [];\n\tconst trimEnd: boolean[] = [];\n\tconst stack: Frame[] = [];\n\tlet mode: Mode = { kind: \"data\" };\n\tlet atLineStart = true;\n\t// A line that starts where the next part does, recorded when that part begins.\n\tlet pendingLine = -1;\n\n\tconst lineText = (index: number) =>\n\t\ttext.slice(starts[index], starts[index + 1] ?? text.length);\n\t/** Whether only spaces and tabs come between `offset` and the end of its line. */\n\tconst endsLine = (offset: number) => {\n\t\tlet index = offset;\n\t\twhile (index < text.length && \" \\t\\r\".includes(text[index] as string)) {\n\t\t\tindex++;\n\t\t}\n\t\treturn index === text.length || text[index] === \"\\n\";\n\t};\n\tconst current = () => lines.length - 1;\n\n\t/** Lowers the current line's level when a closing tag comes before anything else on it. */\n\tconst outdent = (line: number, level: number, first: boolean) => {\n\t\tconst info = lines[line];\n\t\tif (first && info?.kind === \"indent\") {\n\t\t\tinfo.level = Math.min(info.level, level);\n\t\t}\n\t};\n\n\tconst levelOf = (line: number): number => {\n\t\tconst info = lines[line];\n\t\treturn info?.kind === \"indent\" ? info.level : stack.length;\n\t};\n\n\t/** Records how to treat the line that starts at `start`, from the current state. */\n\tconst beginLine = (start: number) => {\n\t\tconst index = lines.length;\n\t\tconst trimmed = lineText(index).trimStart();\n\t\tatLineStart = true;\n\t\tswitch (mode.kind) {\n\t\t\tcase \"data\":\n\t\t\t\tlines.push({ kind: \"indent\", start, level: stack.length });\n\t\t\t\treturn;\n\t\t\tcase \"tag\": {\n\t\t\t\tif (mode.quote !== null) {\n\t\t\t\t\tlines.push({ kind: \"keep\", start });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst base = levelOf(mode.line);\n\t\t\t\tconst level =\n\t\t\t\t\ttrimmed.startsWith(\">\") || trimmed.startsWith(\"/>\")\n\t\t\t\t\t\t? base\n\t\t\t\t\t\t: base + 1 + mode.inner;\n\t\t\t\tlines.push({ kind: \"indent\", start, level });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"comment\":\n\t\t\t\tlines.push(\n\t\t\t\t\ttrimmed.startsWith(\"-->\")\n\t\t\t\t\t\t? { kind: \"indent\", start, level: mode.level }\n\t\t\t\t\t\t: { kind: \"shift\", start, anchor: mode.anchor },\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase \"raw\":\n\t\t\t\tif (mode.keep) lines.push({ kind: \"keep\", start });\n\t\t\t\telse if (closesElement(trimmed, 0, mode.name)) {\n\t\t\t\t\tlines.push({ kind: \"indent\", start, level: mode.level });\n\t\t\t\t} else lines.push({ kind: \"shift\", start, anchor: mode.anchor });\n\t\t\t\treturn;\n\t\t\tcase \"kept\":\n\t\t\t\tlines.push(\n\t\t\t\t\tendsKept(trimmed, mode)\n\t\t\t\t\t\t? { kind: \"indent\", start, level: mode.level }\n\t\t\t\t\t\t: { kind: \"keep\", start },\n\t\t\t\t);\n\t\t}\n\t};\n\n\t/** Whether trailing whitespace on a line ending here can go. Not inside attribute values or kept elements. */\n\tconst canTrimHere = () =>\n\t\t!(mode.kind === \"tag\" && mode.quote !== null) &&\n\t\t!(mode.kind === \"raw\" && mode.keep) &&\n\t\tmode.kind !== \"kept\";\n\n\tconst finishTag = (\n\t\ttag: Extract<Mode, { kind: \"tag\" }>,\n\t\tselfClosing: boolean,\n\t) => {\n\t\tmode = { kind: \"data\" };\n\t\tconst name = tag.name;\n\t\tif (tag.closing) {\n\t\t\t// Close the nearest open element of this name, without crossing a block tag.\n\t\t\tfor (let index = stack.length - 1; index >= 0; index--) {\n\t\t\t\tconst frame = stack[index] as Frame;\n\t\t\t\tif (frame.kind === \"kiln\") break;\n\t\t\t\tif (frame.name === name) {\n\t\t\t\t\tstack.length = index;\n\t\t\t\t\toutdent(tag.line, index, tag.atLineStart);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (name.startsWith(\"!\") || selfClosing || VOID_ELEMENTS.has(name)) return;\n\t\tif (RAW_ELEMENTS.has(name) || KEPT_ELEMENTS.has(name)) {\n\t\t\tmode = {\n\t\t\t\tkind: \"raw\",\n\t\t\t\tname,\n\t\t\t\tkeep: KEPT_ELEMENTS.has(name),\n\t\t\t\tanchor: tag.line,\n\t\t\t\tlevel: stack.length,\n\t\t\t};\n\t\t\treturn;\n\t\t}\n\t\tconst top = stack[stack.length - 1];\n\t\tif (top?.kind === \"html\" && CLOSED_BY[top.name]?.includes(name)) {\n\t\t\tstack.pop();\n\t\t\toutdent(tag.line, stack.length, tag.atLineStart);\n\t\t}\n\t\tstack.push({ kind: \"html\", name });\n\t};\n\n\t/** Starts a tag at `index` if one starts there, and returns where scanning continues. */\n\tconst startTag = (index: number, closing: boolean): number => {\n\t\tconst nameStart = index + (closing ? 2 : 1);\n\t\tlet nameEnd = nameStart;\n\t\twhile (\n\t\t\tnameEnd < text.length &&\n\t\t\t/[A-Za-z0-9:!_-]/.test(text[nameEnd] as string)\n\t\t) {\n\t\t\tnameEnd++;\n\t\t}\n\t\tmode = {\n\t\t\tkind: \"tag\",\n\t\t\tname: text.slice(nameStart, nameEnd).toLowerCase(),\n\t\t\tclosing,\n\t\t\tquote: null,\n\t\t\tline: current(),\n\t\t\tatLineStart,\n\t\t\tinner: 0,\n\t\t};\n\t\tatLineStart = false;\n\t\treturn nameEnd;\n\t};\n\n\t/** Moves through a text part one character at a time. */\n\tconst scanText = (start: number, end: number) => {\n\t\tfor (let index = start; index < end; index++) {\n\t\t\tconst char = text[index] as string;\n\t\t\tif (char === \"\\n\") {\n\t\t\t\ttrimEnd[current()] = canTrimHere();\n\t\t\t\tif (index + 1 < end) beginLine(index + 1);\n\t\t\t\telse if (index + 1 === text.length) {\n\t\t\t\t\t// The file ends with a line break: there is no further line.\n\t\t\t\t} else pendingLine = index + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tswitch (mode.kind) {\n\t\t\t\tcase \"data\":\n\t\t\t\t\tif (char === \" \" || char === \"\\t\" || char === \"\\r\") break;\n\t\t\t\t\tif (char === \"<\") {\n\t\t\t\t\t\tif (text.startsWith(\"<!--\", index)) {\n\t\t\t\t\t\t\tmode = {\n\t\t\t\t\t\t\t\tkind: \"comment\",\n\t\t\t\t\t\t\t\tanchor: current(),\n\t\t\t\t\t\t\t\tlevel: levelOf(current()),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tatLineStart = false;\n\t\t\t\t\t\t\tindex += 3;\n\t\t\t\t\t\t} else if (text[index + 1] === \"/\" && isLetter(text[index + 2])) {\n\t\t\t\t\t\t\tindex = startTag(index, true) - 1;\n\t\t\t\t\t\t} else if (isLetter(text[index + 1]) || text[index + 1] === \"!\") {\n\t\t\t\t\t\t\tindex = startTag(index, false) - 1;\n\t\t\t\t\t\t} else atLineStart = false;\n\t\t\t\t\t} else atLineStart = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tag\":\n\t\t\t\t\tif (mode.quote !== null) {\n\t\t\t\t\t\tif (char === mode.quote) mode.quote = null;\n\t\t\t\t\t} else if (char === '\"' || char === \"'\") mode.quote = char;\n\t\t\t\t\telse if (char === \">\") finishTag(mode, text[index - 1] === \"/\");\n\t\t\t\t\telse if (char !== \" \" && char !== \"\\t\" && char !== \"\\r\") {\n\t\t\t\t\t\tatLineStart = false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"comment\":\n\t\t\t\t\tif (text.startsWith(\"-->\", index)) {\n\t\t\t\t\t\tmode = { kind: \"data\" };\n\t\t\t\t\t\tindex += 2;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"raw\":\n\t\t\t\t\tif (char === \"<\" && closesElement(text, index, mode.name)) {\n\t\t\t\t\t\tindex = startTag(index, true) - 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"kept\":\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t};\n\n\t/** Applies a block tag to the stack, or to the count of block tags open inside an HTML tag. */\n\tconst applyTag = (part: DelimitedPart) => {\n\t\tconst role = roleOf(part);\n\t\tconst first = atLineStart;\n\t\tatLineStart = false;\n\t\tif (!role) return;\n\t\tif (mode.kind === \"tag\") {\n\t\t\tconst base = levelOf(mode.line) + 1;\n\t\t\tif (role.kind === \"open\") mode.inner++;\n\t\t\telse if (role.kind === \"close\") {\n\t\t\t\tmode.inner = Math.max(0, mode.inner - 1);\n\t\t\t\toutdent(current(), base + mode.inner, first);\n\t\t\t} else outdent(current(), base + mode.inner - 1, first);\n\t\t\treturn;\n\t\t}\n\t\tif (mode.kind === \"kept\") {\n\t\t\tif (role.kind === \"open\" && role.name === mode.name) mode.depth++;\n\t\t\telse if (\n\t\t\t\trole.kind === \"close\" &&\n\t\t\t\trole.name === mode.name &&\n\t\t\t\t--mode.depth === 0\n\t\t\t) {\n\t\t\t\tconst index = stack.findLastIndex((frame) => frame.kind === \"kiln\");\n\t\t\t\tif (index !== -1) stack.length = index;\n\t\t\t\tmode = { kind: \"data\" };\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (mode.kind !== \"data\") return;\n\t\tif (role.kind === \"open\") {\n\t\t\tstack.push({ kind: \"kiln\", name: role.name });\n\t\t\tif (\n\t\t\t\trole.name === \"verbatim\" ||\n\t\t\t\t((role.name === \"set\" || role.name === \"apply\") &&\n\t\t\t\t\t!(first && endsLine(part.end)))\n\t\t\t) {\n\t\t\t\tmode = {\n\t\t\t\t\tkind: \"kept\",\n\t\t\t\t\tname: role.name,\n\t\t\t\t\tdepth: 1,\n\t\t\t\t\tlevel: stack.length - 1,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tconst index =\n\t\t\trole.kind === \"branch\"\n\t\t\t\t? stack.findLastIndex((frame) => frame.kind === \"kiln\")\n\t\t\t\t: stack.findLastIndex(\n\t\t\t\t\t\t(frame) => frame.kind === \"kiln\" && frame.name === role.name,\n\t\t\t\t\t);\n\t\tif (index === -1) return;\n\t\t// Elements opened in this body or branch end with it.\n\t\tstack.length = role.kind === \"branch\" ? index + 1 : index;\n\t\toutdent(current(), index, first);\n\t};\n\n\tbeginLine(0);\n\tconst delimiters: { part: DelimitedPart; line: number }[] = [];\n\n\tfor (const part of parts) {\n\t\tif (pendingLine !== -1) {\n\t\t\tbeginLine(pendingLine);\n\t\t\tpendingLine = -1;\n\t\t}\n\t\tif (part.kind === \"text\") {\n\t\t\tscanText(part.start, part.end);\n\t\t\tcontinue;\n\t\t}\n\t\tconst line = current();\n\t\tif (part.kind === \"comment\") atLineStart = false;\n\t\telse {\n\t\t\tdelimiters.push({ part, line });\n\t\t\tif (part.kind === \"tag\") applyTag(part);\n\t\t\telse atLineStart = false;\n\t\t}\n\t\t// Lines that start inside this delimiter.\n\t\tfor (\n\t\t\tlet index = text.indexOf(\"\\n\", part.start);\n\t\t\tindex !== -1 && index < part.end;\n\t\t\tindex = text.indexOf(\"\\n\", index + 1)\n\t\t) {\n\t\t\ttrimEnd[current()] = part.kind === \"comment\";\n\t\t\tlines.push(\n\t\t\t\tpart.kind === \"comment\"\n\t\t\t\t\t? { kind: \"shift\", start: index + 1, anchor: line }\n\t\t\t\t\t: { kind: \"inside\", start: index + 1 },\n\t\t\t);\n\t\t}\n\t}\n\ttrimEnd[current()] ??= canTrimHere();\n\treturn { lines, trimEnd, delimiters };\n};\n\nconst widthOf = (whitespace: string, tabWidth: number): number => {\n\tlet width = 0;\n\tfor (const char of whitespace) width += char === \"\\t\" ? tabWidth : 1;\n\treturn width;\n};\n\n/**\n * Works out the edits that format a template, without checking them. See `formatEdits`.\n */\nconst planEdits = (\n\ttext: string,\n\tparts: readonly Part[],\n\toptions: Required<Omit<FormatOptions, \"range\">>,\n): FormatEdit[] => {\n\tconst { useTabs, tabWidth } = options;\n\tconst unit = useTabs ? \"\\t\" : \" \".repeat(tabWidth);\n\tconst indentOfWidth = (width: number) =>\n\t\tuseTabs\n\t\t\t? \"\\t\".repeat(Math.floor(width / tabWidth)) + \" \".repeat(width % tabWidth)\n\t\t\t: \" \".repeat(width);\n\tconst { lines, trimEnd, delimiters } = planLines(text, parts);\n\n\tconst oldIndent = (line: number) => {\n\t\tconst start = lines[line]?.start ?? 0;\n\t\tlet end = start;\n\t\twhile (text[end] === \" \" || text[end] === \"\\t\") end++;\n\t\treturn text.slice(start, end);\n\t};\n\t/** Where a line's content ends: at its line break, or the end of the file. */\n\tconst lineEnd = (line: number) => {\n\t\tconst next = lines[line + 1]?.start;\n\t\tif (next !== undefined) return next - 1;\n\t\treturn text.endsWith(\"\\n\") ? text.length - 1 : text.length;\n\t};\n\tconst isBlank = (line: number) => {\n\t\tconst info = lines[line] as Line;\n\t\treturn (\n\t\t\tinfo.kind !== \"keep\" &&\n\t\t\tinfo.kind !== \"inside\" &&\n\t\t\ttext.slice(info.start, lineEnd(line)).trim() === \"\"\n\t\t);\n\t};\n\n\t// Whitespace before the first content and after the last is output as written wherever the template is placed, so\n\t// it stays as it is, as does a file of only whitespace.\n\tlet firstContent = 0;\n\twhile (firstContent < lines.length && isBlank(firstContent)) firstContent++;\n\tlet lastContent = lines.length - 1;\n\twhile (lastContent >= 0 && isBlank(lastContent)) lastContent--;\n\n\tconst newIndents = new Map<number, string | null>();\n\tconst newIndent = (line: number): string | null => {\n\t\tif (newIndents.has(line)) return newIndents.get(line) ?? null;\n\t\tconst info = lines[line];\n\t\tlet indent: string | null = null;\n\t\tif (info?.kind === \"indent\") {\n\t\t\t// The first content line keeps its indentation. See `firstContent`.\n\t\t\tindent =\n\t\t\t\tline === firstContent ? oldIndent(line) : unit.repeat(info.level);\n\t\t} else if (info?.kind === \"shift\") {\n\t\t\tconst anchor = newIndent(info.anchor);\n\t\t\tconst moved =\n\t\t\t\tanchor === null\n\t\t\t\t\t? 0\n\t\t\t\t\t: widthOf(anchor, tabWidth) -\n\t\t\t\t\t\twidthOf(oldIndent(info.anchor), tabWidth);\n\t\t\tindent = indentOfWidth(\n\t\t\t\tMath.max(0, widthOf(oldIndent(line), tabWidth) + moved),\n\t\t\t);\n\t\t}\n\t\tnewIndents.set(line, indent);\n\t\treturn indent;\n\t};\n\n\tconst edits: FormatEdit[] = [];\n\t// Blank lines go where the line before is blank too, so runs become one.\n\tlet previousBlank = false;\n\n\tfor (const [index, info] of lines.entries()) {\n\t\tif (index > lastContent) break;\n\t\tif (index < firstContent) continue;\n\t\tconst start = info.start;\n\t\tconst end = lineEnd(index);\n\t\tif (isBlank(index)) {\n\t\t\tif (info.kind === \"indent\" && previousBlank) {\n\t\t\t\tedits.push({ start, end: end + 1, text: \"\" });\n\t\t\t} else if (end > start) edits.push({ start, end, text: \"\" });\n\t\t\tpreviousBlank = true;\n\t\t\tcontinue;\n\t\t}\n\t\tpreviousBlank = false;\n\n\t\tconst indent = newIndent(index);\n\t\tif (indent !== null) {\n\t\t\tconst current = oldIndent(index);\n\t\t\tif (current !== indent) {\n\t\t\t\tedits.push({ start, end: start + current.length, text: indent });\n\t\t\t}\n\t\t}\n\t\tif (trimEnd[index] && info.kind !== \"keep\" && index !== lastContent) {\n\t\t\tlet from = end;\n\t\t\twhile (from > start && /[ \\t\\r]/.test(text[from - 1] as string)) from--;\n\t\t\tif (from < end && !insideDelimiter(delimiters, from)) {\n\t\t\t\tedits.push({ start: from, end, text: \"\" });\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const { part, line } of delimiters) {\n\t\tconst indent = newIndent(line) ?? oldIndent(line);\n\t\tconst printed = printDelimiter(text, part, indent, unit);\n\t\tif (printed !== null && printed !== text.slice(part.start, part.end)) {\n\t\t\tedits.push({ start: part.start, end: part.end, text: printed });\n\t\t}\n\t}\n\n\treturn edits.sort((a, b) => a.start - b.start || a.end - b.end);\n};\n\nconst insideDelimiter = (\n\tdelimiters: readonly { part: DelimitedPart }[],\n\toffset: number,\n) => delimiters.some(({ part }) => offset > part.start && offset < part.end);\n\nconst applyEdits = (text: string, edits: readonly FormatEdit[]): string => {\n\tlet out = \"\";\n\tlet position = 0;\n\tfor (const edit of edits) {\n\t\tout += text.slice(position, edit.start) + edit.text;\n\t\tposition = edit.end;\n\t}\n\treturn out + text.slice(position);\n};\n\n/** Removes whitespace at the start and end of lines, and blank lines, which the formatter may change. */\nconst normaliseText = (value: string) =>\n\tvalue\n\t\t.replace(/[ \\t\\r\\f\\v]+$/gm, \"\")\n\t\t.replace(/^[ \\t\\f\\v]+/gm, \"\")\n\t\t.replace(/\\n{2,}/g, \"\\n\");\n\n/** A copy of a syntax tree without positions or layout, with text normalised and text left empty by that removed. */\nconst normaliseTree = (value: unknown): unknown => {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t\t\t.map(normaliseTree)\n\t\t\t.filter(\n\t\t\t\t(item) =>\n\t\t\t\t\t!(\n\t\t\t\t\t\ttypeof item === \"object\" &&\n\t\t\t\t\t\titem !== null &&\n\t\t\t\t\t\t(item as { type?: unknown }).type === \"Text\" &&\n\t\t\t\t\t\t(item as { value?: unknown }).value === \"\"\n\t\t\t\t\t),\n\t\t\t);\n\t}\n\tif (typeof value !== \"object\" || value === null) return value;\n\tconst copy: Record<string, unknown> = {};\n\tfor (const [key, item] of Object.entries(value)) {\n\t\tif (key === \"span\" || key === \"nameSpan\" || key === \"indent\") continue;\n\t\tcopy[key] =\n\t\t\tkey === \"value\" && (value as { type?: unknown }).type === \"Text\"\n\t\t\t\t? normaliseText(item as string)\n\t\t\t\t: normaliseTree(item);\n\t}\n\treturn copy;\n};\n\n/** The syntax tree as JSON, without the whitespace the formatter may change, so two trees can be compared. */\nconst shape = (text: string): string =>\n\tJSON.stringify(normaliseTree(parse(text, { isTest }).ast));\n\n/**\n * Works out the edits that format a template, in source order and never overlapping. Editors apply these so the cursor and undo history stay put. See `format` for what it throws.\n */\nexport const formatEdits = (\n\ttext: string,\n\toptions: FormatOptions = {},\n): FormatEdit[] => {\n\tconst parsed = parse(text, { isTest });\n\tif (parsed.problems.length > 0) {\n\t\tconst source = new Source(\"template.kiln\", text);\n\t\tthrow new KilnSyntaxError(\n\t\t\tparsed.problems.map((problem) =>\n\t\t\t\tcreateDiagnostic(source, problem.code, problem.message, problem.span),\n\t\t\t),\n\t\t\tsource,\n\t\t);\n\t}\n\n\tconst tabWidth = options.tabWidth ?? 4;\n\tif (!Number.isInteger(tabWidth) || tabWidth < 1) {\n\t\tthrow new RangeError(\n\t\t\t`tabWidth must be a whole number of at least 1, but got ${tabWidth}`,\n\t\t);\n\t}\n\tlet edits = planEdits(text, scanParts(text).parts, {\n\t\tuseTabs: options.useTabs ?? true,\n\t\ttabWidth,\n\t});\n\tconst range = options.range;\n\tif (range) {\n\t\tedits = edits.filter(\n\t\t\t(edit) => edit.end >= range.start && edit.start <= range.end,\n\t\t);\n\t}\n\n\tconst formatted = applyEdits(text, edits);\n\tconst changed = parse(formatted, { isTest });\n\tif (changed.problems.length > 0 || shape(formatted) !== shape(text)) {\n\t\tthrow new KilnFormatError(\n\t\t\t\"Formatting would have changed what this template renders, so nothing was changed. This is a bug in Kiln's formatter.\",\n\t\t);\n\t}\n\treturn edits;\n};\n\n/**\n * Formats a template: indents it to match its structure, and tidies spacing inside outputs and tags. Never changes what it renders.\n *\n * Throws `KilnSyntaxError` when the template has syntax errors, `KilnFormatError` if the safety check fails, and\n * `RangeError` for a `tabWidth` that isn't a whole number of at least 1.\n *\n * ```ts\n * const formatted = format(source, { useTabs: false, tabWidth: 2 });\n * ```\n */\nexport const format = (text: string, options: FormatOptions = {}): string =>\n\tapplyEdits(text, formatEdits(text, options));\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","names":[],"sources":["../src/formatter/format.ts"],"mappings":";KAQY;;EAEX;;EAEA;;EAEA;IAAU;IAAe;;;;KAId;EAAe;EAAe;EAAa;;;qBAG1C,wBAAwB;EAC3B;;;;;qBA2oBG,cAAW,cACX,UACH,kBACP;;;;;;;;;;;qBAiDU,SAAM,cAAgB,UAAW"}
1
+ {"version":3,"file":"format.d.ts","names":[],"sources":["../src/formatter/format.ts"],"mappings":";KAQY;;EAEX;;EAEA;;EAEA;IAAU;IAAe;;;;KAId;EAAe;EAAe;EAAa;;;qBAG1C,wBAAwB;EAC3B;;;;;qBAqoBG,cAAW,cACX,UACH,kBACP;;;;;;;;;;;qBAiDU,SAAM,cAAgB,UAAW"}
package/dist/format.js CHANGED
@@ -1 +1 @@
1
- export { t as KilnFormatError, n as format, r as formatEdits } from "./format-CJoqhehX.js";
1
+ export { t as KilnFormatError, n as format, r as formatEdits } from "./format-Di6-GUtH.js";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { A as IslandEntry, B as ShapeKey, C as AnalysedProp, D as KilnCache, E as AbortSignalLike, F as deferIslands, G as TemplateLocation, H as Diagnostic, I as islandScript, K as Position, L as streamIslands, M as IslandMode, N as JsonValue, O as RenderInfo, P as StreamedIslands, R as EscapeStrategy, S as PrecompiledTemplate, T as TemplateReference, U as KilnRenderError, V as TypeName, W as KilnSyntaxError, _ as memoryLoader, a as KilnTemplates, b as HtmlCheckLevel, c as Loader, d as TemplateBlock, f as TemplateContext, g as memoryCache, h as createKiln, i as KilnSchema, j as IslandManifest, k as DeferredIslands, l as RenderAware, m as TypeChecks, n as KilnCallable, o as KilnTypes, p as TemplateName, r as KilnOptions, s as Limits, t as Kiln, u as RenderOptions, v as withRender, w as Analysis, x as HtmlChecks, y as HtmlCheck, z as PropType } from "./environment-BlQ8V4tX.js";
1
+ import { c as PropType, l as ShapeKey, m as Position, p as TypeName, r as EscapeStrategy } from "./ast-DDhHuwaY.js";
2
+ import { A as AbortSignalLike, B as islandScript, C as HtmlCheck, D as AnalysedProp, E as PrecompiledTemplate, F as IslandManifest, G as TemplateLocation, H as Diagnostic, I as IslandMode, L as JsonValue, M as RenderInfo, N as DeferredIslands, O as Analysis, P as IslandEntry, R as StreamedIslands, S as withRender, T as HtmlChecks, U as KilnRenderError, V as streamIslands, W as KilnSyntaxError, _ as TemplateName, a as KilnGlobals, b as memoryCache, c as KilnTemplates, d as Limits, f as Loader, g as TemplateContext, h as TemplateBlock, i as KilnFunctions, j as KilnCache, k as TemplateReference, l as KilnTests, m as RenderOptions, n as KilnCallable, o as KilnOptions, p as RenderAware, r as KilnFilters, s as KilnSchema, t as Kiln, u as KilnTypes, v as TypeChecks, w as HtmlCheckLevel, x as memoryLoader, y as createKiln, z as deferIslands } from "./environment-DBvRrOn0.js";
2
3
  //#region src/runtime/markup.d.ts
3
4
  /**
4
5
  * A string that is already safe to output with one escaping strategy.
@@ -86,5 +87,5 @@ export declare const generateManifest: <State, Schema extends KilnSchema>(kiln:
86
87
  */
87
88
  export declare const generateTypes: (kiln: Pick<Kiln, "analyze">, names: Iterable<string>) => Promise<string>;
88
89
  //#endregion
89
- export { type AbortSignalLike, type AnalysedProp, type Analysis, type DeferredIslands, type Diagnostic, type EscapeStrategy, type HtmlCheck, type HtmlCheckLevel, type HtmlChecks, type IslandEntry, type IslandManifest, type IslandMode, type JsonValue, type Kiln, type KilnCache, type KilnCallable, type KilnManifest, type KilnOptions, KilnRenderError, type KilnSchema, KilnSyntaxError, type KilnTemplates, type KilnTypes, type Limits, type Loader, type Position, type PrecompiledTemplate, type PropType, type RenderAware, type RenderInfo, type RenderOptions, type ShapeKey, type StreamedIslands, type TemplateBlock, type TemplateContext, type TemplateLocation, type TemplateName, type TemplateReference, type TypeChecks, type TypeName, createKiln, deferIslands, islandScript, memoryCache, memoryLoader, streamIslands, withRender };
90
+ export { type AbortSignalLike, type AnalysedProp, type Analysis, type DeferredIslands, type Diagnostic, type EscapeStrategy, type HtmlCheck, type HtmlCheckLevel, type HtmlChecks, type IslandEntry, type IslandManifest, type IslandMode, type JsonValue, type Kiln, type KilnCache, type KilnCallable, type KilnFilters, type KilnFunctions, type KilnGlobals, type KilnManifest, type KilnOptions, KilnRenderError, type KilnSchema, KilnSyntaxError, type KilnTemplates, type KilnTests, type KilnTypes, type Limits, type Loader, type Position, type PrecompiledTemplate, type PropType, type RenderAware, type RenderInfo, type RenderOptions, type ShapeKey, type StreamedIslands, type TemplateBlock, type TemplateContext, type TemplateLocation, type TemplateName, type TemplateReference, type TypeChecks, type TypeName, createKiln, deferIslands, islandScript, memoryCache, memoryLoader, streamIslands, withRender };
90
91
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/runtime/markup.ts","../src/runtime/exposure.ts","../src/html/scanner.ts","../src/manifest.ts","../src/typegen.ts"],"mappings":";;;;;;;qBAOa;WAEF;WACA,UAAU;EAFpB,YACU,eACA,UAAU;EAGpB;;;;;;;qBAUY,OAAI,eACH,WACH,mBACR;;qBAGU,SAAM,mBAAqB,SAAS;;;;;;;;;;;;qBCjBpC;;;;;;;;;;qBAWA;;;;;;;qBCsCA,eAAY;;;;KCvDb;;EAEX;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAAY;;;;;;;;;qBAgBA,mBAAoB,OAAO,eAAe,YAAU,MAC1D,KAAK,OAAO,SAAO;EACd;;;;;;;;;;;;;;qBCsCC,gBAAa,MACnB,KAAK,kBAAgB,OACpB,qBACL"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/runtime/markup.ts","../src/runtime/exposure.ts","../src/html/scanner.ts","../src/manifest.ts","../src/typegen.ts"],"mappings":";;;;;;;;qBAOa;WAEF;WACA,UAAU;EAFpB,YACU,eACA,UAAU;EAGpB;;;;;;;qBAUY,OAAI,eACH,WACH,mBACR;;qBAGU,SAAM,mBAAqB,SAAS;;;;;;;;;;;;qBCjBpC;;;;;;;;;;qBAWA;;;;;;;qBCsCA,eAAY;;;;KCvDb;;EAEX;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAAY;;;;;;;;;qBAgBA,mBAAoB,OAAO,eAAe,YAAU,MAC1D,KAAK,OAAO,SAAO;EACd;;;;;;;;;;;;;;qBCsCC,gBAAa,MACnB,KAAK,kBAAgB,OACpB,qBACL"}