@nml-lang/compiler-ts 2.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.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @nml/compiler-ts — public API
3
+ *
4
+ * Exports the CompilerAdapter interface so consumers (CLI, Vite plugin,
5
+ * Cloudflare Worker, MCP server) always program against the interface,
6
+ * never a concrete class. A future @nml/compiler-wasm package will export
7
+ * a WasmCompiler satisfying the same interface.
8
+ */
9
+ import { buildAst, renderVariables, NMLParserError, findComponentRootNode, parseLineRaw, isTruthy, postProcessConditionalsPass } from "./parser.js";
10
+ import { generateHtml, type RenderOptions } from "./renderer.js";
11
+ import type { ASTNode, ComponentMap, GlobalStyles } from "./parser.js";
12
+ export interface CompilerAdapter {
13
+ /**
14
+ * Compile a raw NML string to an HTML string.
15
+ * Returns a Promise — async to support @include directives that read
16
+ * files from the filesystem, Cloudflare R2, D1, or in-memory stores.
17
+ *
18
+ * @param input - Raw NML source
19
+ * @param context - Optional template variable context
20
+ * @param options - Optional components, global styles, readFile, basePath
21
+ */
22
+ render(input: string, context?: Record<string, unknown>, options?: CompileOptions): Promise<string>;
23
+ }
24
+ export interface CompileOptions {
25
+ components?: ComponentMap;
26
+ globalStyles?: GlobalStyles;
27
+ /** Async function to read included files. Required when NML uses @include. */
28
+ readFile?: (path: string) => Promise<string>;
29
+ /** Absolute path of the NML file being rendered — resolves relative @include paths. */
30
+ basePath?: string;
31
+ }
32
+ export declare const nmlCompiler: CompilerAdapter;
33
+ export { buildAst, generateHtml, renderVariables, NMLParserError, findComponentRootNode, parseLineRaw, isTruthy, postProcessConditionalsPass };
34
+ export type { RenderOptions };
35
+ export type { ASTNode, ComponentMap, GlobalStyles };
36
+ export { NMLLexerError } from "./lexer.js";
37
+ export type { SourceLocation, Token, TokenKind } from "./lexer.js";
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,qBAAqB,EAAE,YAAY,EAAE,QAAQ,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AACpJ,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMvE,MAAM,WAAW,eAAe;IAC9B;;;;;;;;OAQG;IACH,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAgED,eAAO,MAAM,WAAW,EAAE,eAAkC,CAAC;AAM7D,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,qBAAqB,EAAE,YAAY,EAAE,QAAQ,EAAE,2BAA2B,EAAE,CAAC;AAC/I,YAAY,EAAE,aAAa,EAAE,CAAC;AAC9B,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @nml/compiler-ts — public API
3
+ *
4
+ * Exports the CompilerAdapter interface so consumers (CLI, Vite plugin,
5
+ * Cloudflare Worker, MCP server) always program against the interface,
6
+ * never a concrete class. A future @nml/compiler-wasm package will export
7
+ * a WasmCompiler satisfying the same interface.
8
+ */
9
+ import { buildAst, renderVariables, NMLParserError, findComponentRootNode, parseLineRaw, isTruthy, postProcessConditionalsPass } from "./parser.js";
10
+ import { generateHtml } from "./renderer.js";
11
+ // ---------------------------------------------------------------------------
12
+ // Default TypeScript engine
13
+ // ---------------------------------------------------------------------------
14
+ class TSCompiler {
15
+ async render(input, context = {}, options = {}) {
16
+ const components = options.components ?? {};
17
+ const globalStyles = options.globalStyles ?? {};
18
+ const renderOpts = {
19
+ readFile: options.readFile,
20
+ basePath: options.basePath,
21
+ };
22
+ const ast = buildAst(input, { components, globalStyles });
23
+ let html = await generateHtml(ast, 0, context, renderOpts);
24
+ // Inject scoped styles if any are present
25
+ if (Object.keys(globalStyles).length > 0) {
26
+ const usedScopeIds = collectScopeIds(ast);
27
+ const usedStyles = [];
28
+ for (const sid of usedScopeIds) {
29
+ if (sid in globalStyles) {
30
+ usedStyles.push(globalStyles[sid]);
31
+ }
32
+ }
33
+ if (usedStyles.length > 0) {
34
+ const styleTag = `<style data-nml-scoped-styles>\n${usedStyles.join("\n")}\n</style>`;
35
+ if (html.includes("</head>")) {
36
+ html = html.replace("</head>", `${styleTag}\n</head>`);
37
+ }
38
+ else {
39
+ html = styleTag + "\n" + html;
40
+ }
41
+ }
42
+ }
43
+ return html;
44
+ }
45
+ }
46
+ function collectScopeIds(nodes) {
47
+ const ids = new Set();
48
+ function walk(items) {
49
+ for (const n of items) {
50
+ for (const k of Object.keys(n.attributes)) {
51
+ if (k.startsWith("nml-c-"))
52
+ ids.add(k);
53
+ }
54
+ if (n.children.length > 0)
55
+ walk(n.children);
56
+ }
57
+ }
58
+ walk(nodes);
59
+ return ids;
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Singleton default export — V8-optimised TS engine
63
+ // ---------------------------------------------------------------------------
64
+ export const nmlCompiler = new TSCompiler();
65
+ // ---------------------------------------------------------------------------
66
+ // Re-exports for advanced consumers
67
+ // ---------------------------------------------------------------------------
68
+ export { buildAst, generateHtml, renderVariables, NMLParserError, findComponentRootNode, parseLineRaw, isTruthy, postProcessConditionalsPass };
69
+ export { NMLLexerError } from "./lexer.js";
70
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,qBAAqB,EAAE,YAAY,EAAE,QAAQ,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AACpJ,OAAO,EAAE,YAAY,EAAsB,MAAM,eAAe,CAAC;AAiCjE,8EAA8E;AAC9E,4BAA4B;AAC5B,8EAA8E;AAE9E,MAAM,UAAU;IACd,KAAK,CAAC,MAAM,CACV,KAAa,EACb,UAAmC,EAAE,EACrC,UAA0B,EAAE;QAE5B,MAAM,UAAU,GAAiB,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;QAC1D,MAAM,YAAY,GAAiB,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;QAE9D,MAAM,UAAU,GAAkB;YAChC,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC;QAEF,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;QAC1D,IAAI,IAAI,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAE3D,0CAA0C;QAC1C,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzC,MAAM,YAAY,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YAC1C,MAAM,UAAU,GAAa,EAAE,CAAC;YAChC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;gBAC/B,IAAI,GAAG,IAAI,YAAY,EAAE,CAAC;oBACxB,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC;YACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,QAAQ,GAAG,mCAAmC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC;gBACtF,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,QAAQ,WAAW,CAAC,CAAC;gBACzD,CAAC;qBAAM,CAAC;oBACN,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;gBAChC,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,SAAS,eAAe,CAAC,KAAgB;IACvC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,SAAS,IAAI,CAAC,KAAgB;QAC5B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC1C,IAAI,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC;oBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACzC,CAAC;YACD,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;gBAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,CAAC;IACZ,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8EAA8E;AAC9E,oDAAoD;AACpD,8EAA8E;AAE9E,MAAM,CAAC,MAAM,WAAW,GAAoB,IAAI,UAAU,EAAE,CAAC;AAE7D,8EAA8E;AAC9E,oCAAoC;AACpC,8EAA8E;AAE9E,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,qBAAqB,EAAE,YAAY,EAAE,QAAQ,EAAE,2BAA2B,EAAE,CAAC;AAG/I,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * NML Lexer
3
+ * Tokenizes raw NML source text into a stream of typed tokens,
4
+ * each carrying precise source location (line, column) for error
5
+ * reporting and Vite overlay / MCP diagnostics.
6
+ */
7
+ export interface SourceLocation {
8
+ line: number;
9
+ column: number;
10
+ }
11
+ export type TokenKind = "INDENT" | "ELEMENT" | "ATTR_CHAIN" | "MULTILINE_START" | "MULTILINE_LINE" | "COMMENT" | "BLANK" | "EOF";
12
+ export interface Token {
13
+ kind: TokenKind;
14
+ value: string;
15
+ loc: SourceLocation;
16
+ /** For INDENT tokens: indentation level (0-based, each level = 4 spaces) */
17
+ level?: number;
18
+ }
19
+ /**
20
+ * Tokenize NML source into a flat array of line-level tokens.
21
+ * Each "line" of NML produces at most one primary token; multiline
22
+ * blocks consume multiple raw lines and emit MULTILINE_LINE tokens
23
+ * for each.
24
+ */
25
+ export declare function tokenize(source: string): Token[];
26
+ export declare class NMLLexerError extends Error {
27
+ loc: SourceLocation;
28
+ constructor(message: string, loc: SourceLocation);
29
+ }
30
+ //# sourceMappingURL=lexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../src/lexer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,SAAS,GACT,YAAY,GACZ,iBAAiB,GACjB,gBAAgB,GAChB,SAAS,GACT,OAAO,GACP,KAAK,CAAC;AAEV,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,cAAc,CAAC;IACpB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CA8GhD;AA8BD,qBAAa,aAAc,SAAQ,KAAK;IACtC,GAAG,EAAE,cAAc,CAAC;gBACR,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc;CAKjD"}
package/dist/lexer.js ADDED
@@ -0,0 +1,132 @@
1
+ /**
2
+ * NML Lexer
3
+ * Tokenizes raw NML source text into a stream of typed tokens,
4
+ * each carrying precise source location (line, column) for error
5
+ * reporting and Vite overlay / MCP diagnostics.
6
+ */
7
+ const INDENT_WIDTH = 4;
8
+ /**
9
+ * Tokenize NML source into a flat array of line-level tokens.
10
+ * Each "line" of NML produces at most one primary token; multiline
11
+ * blocks consume multiple raw lines and emit MULTILINE_LINE tokens
12
+ * for each.
13
+ */
14
+ export function tokenize(source) {
15
+ const tokens = [];
16
+ const rawLines = source.split("\n");
17
+ let lineIdx = 0; // 0-based index into rawLines
18
+ while (lineIdx < rawLines.length) {
19
+ const rawLine = rawLines[lineIdx];
20
+ const currentLine = lineIdx + 1; // 1-based
21
+ const stripped = rawLine.trimEnd();
22
+ // Blank line
23
+ if (stripped.trim() === "") {
24
+ tokens.push({ kind: "BLANK", value: "", loc: { line: currentLine, column: 0 } });
25
+ lineIdx++;
26
+ continue;
27
+ }
28
+ // Comment
29
+ if (stripped.trimStart().startsWith("//")) {
30
+ const col = stripped.length - stripped.trimStart().length;
31
+ tokens.push({ kind: "COMMENT", value: stripped.trimStart(), loc: { line: currentLine, column: col } });
32
+ lineIdx++;
33
+ continue;
34
+ }
35
+ // Validate and measure indentation
36
+ const leadingSpaces = stripped.length - stripped.trimStart().length;
37
+ if (stripped.startsWith("\t")) {
38
+ throw new NMLLexerError(`Indentation error on line ${currentLine}: Please use 4 spaces for indentation, not tabs.`, { line: currentLine, column: 0 });
39
+ }
40
+ if (leadingSpaces % INDENT_WIDTH !== 0) {
41
+ throw new NMLLexerError(`Indentation error on line ${currentLine}: Non-standard indentation. Use 4 spaces per level.`, { line: currentLine, column: 0 });
42
+ }
43
+ const level = leadingSpaces / INDENT_WIDTH;
44
+ const content = stripped.trimStart();
45
+ // Check if this line is a multiline trigger (ends with ':' outside quotes)
46
+ const isMultilineTrigger = isLineMultilineTrigger(content);
47
+ // Emit INDENT + ELEMENT token for this line
48
+ tokens.push({
49
+ kind: "INDENT",
50
+ value: "",
51
+ level,
52
+ loc: { line: currentLine, column: 0 },
53
+ });
54
+ tokens.push({
55
+ kind: isMultilineTrigger ? "MULTILINE_START" : "ELEMENT",
56
+ value: content,
57
+ loc: { line: currentLine, column: leadingSpaces },
58
+ });
59
+ lineIdx++;
60
+ // If multiline, consume subsequent indented lines
61
+ if (isMultilineTrigger) {
62
+ const blockStartLevel = level + 1;
63
+ const blockStartCol = blockStartLevel * INDENT_WIDTH;
64
+ while (lineIdx < rawLines.length) {
65
+ const mlRaw = rawLines[lineIdx];
66
+ const mlLine = lineIdx + 1;
67
+ const mlStripped = mlRaw.trimEnd();
68
+ // Blank lines are part of the block
69
+ if (mlStripped.trim() === "") {
70
+ tokens.push({ kind: "MULTILINE_LINE", value: "", loc: { line: mlLine, column: 0 } });
71
+ lineIdx++;
72
+ continue;
73
+ }
74
+ const mlSpaces = mlStripped.length - mlStripped.trimStart().length;
75
+ // Block ends when indentation returns to block level or less
76
+ if (mlSpaces < blockStartCol) {
77
+ break;
78
+ }
79
+ // Validate indentation within block
80
+ if (mlSpaces % INDENT_WIDTH !== 0) {
81
+ throw new NMLLexerError(`Indentation error on line ${mlLine}: Non-standard indentation. Use 4 spaces per level.`, { line: mlLine, column: 0 });
82
+ }
83
+ // Relative indent (strip the block-start indentation)
84
+ const relativeIndent = " ".repeat(mlSpaces - blockStartCol);
85
+ const mlContent = relativeIndent + mlStripped.trimStart();
86
+ tokens.push({
87
+ kind: "MULTILINE_LINE",
88
+ value: mlContent,
89
+ loc: { line: mlLine, column: mlSpaces },
90
+ });
91
+ lineIdx++;
92
+ }
93
+ }
94
+ }
95
+ tokens.push({ kind: "EOF", value: "", loc: { line: rawLines.length + 1, column: 0 } });
96
+ return tokens;
97
+ }
98
+ /**
99
+ * Returns true if the NML line (already stripped of indent) ends with ':'
100
+ * outside of any quoted string — indicating a multiline content block.
101
+ */
102
+ function isLineMultilineTrigger(content) {
103
+ let inQuote = false;
104
+ let quoteChar = "";
105
+ let i = 0;
106
+ // Strip trailing whitespace before checking
107
+ const trimmed = content.trimEnd();
108
+ while (i < trimmed.length) {
109
+ const ch = trimmed[i];
110
+ if (inQuote) {
111
+ if (ch === quoteChar)
112
+ inQuote = false;
113
+ }
114
+ else {
115
+ if (ch === '"' || ch === "'") {
116
+ inQuote = true;
117
+ quoteChar = ch;
118
+ }
119
+ }
120
+ i++;
121
+ }
122
+ return !inQuote && trimmed.endsWith(":");
123
+ }
124
+ export class NMLLexerError extends Error {
125
+ loc;
126
+ constructor(message, loc) {
127
+ super(message);
128
+ this.name = "NMLLexerError";
129
+ this.loc = loc;
130
+ }
131
+ }
132
+ //# sourceMappingURL=lexer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lexer.js","sourceRoot":"","sources":["../src/lexer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * NML Parser
3
+ * Ports nml_parse.py's build_ast, _expand_components_pass,
4
+ * _inject_slot, and related helpers to TypeScript.
5
+ *
6
+ * Every ASTNode carries loc: { line, column } sourced from the lexer.
7
+ */
8
+ import { type SourceLocation } from "./lexer.js";
9
+ export interface SourceLocation2 extends SourceLocation {
10
+ }
11
+ export interface ASTNode {
12
+ element: string;
13
+ attributes: Record<string, string | boolean | string[]>;
14
+ content: string;
15
+ children: ASTNode[];
16
+ multiline_trigger: boolean;
17
+ multiline_content: string[];
18
+ loc: SourceLocation;
19
+ /** Internal: node-level context override (props) */
20
+ __context__?: Record<string, unknown>;
21
+ /** Set by postProcessConditionalsPass on @if nodes: the else-branch children */
22
+ elseBranch?: ASTNode[];
23
+ }
24
+ export type ComponentMap = Record<string, ASTNode[]>;
25
+ export type GlobalStyles = Record<string, string>;
26
+ export declare class NMLParserError extends Error {
27
+ loc: SourceLocation;
28
+ constructor(message: string, loc?: SourceLocation);
29
+ }
30
+ export declare function parseLine(rawContent: string, loc: SourceLocation): ASTNode;
31
+ /**
32
+ * The Python parser supports `h1("Hello")` as shorthand for content.
33
+ * In NML the element line is like `h1.class("blue", "Hello")` or `h1("Hello")`.
34
+ * We need to detect when the "attribute name" equals the element name
35
+ * and treat the value as content instead.
36
+ */
37
+ export declare function parseLineRaw(rawLine: string, loc: SourceLocation): ASTNode;
38
+ export declare function buildAst(source: string, options?: {
39
+ components?: ComponentMap;
40
+ globalStyles?: GlobalStyles;
41
+ }): ASTNode[];
42
+ export declare function findComponentRootNode(ast: ASTNode[]): ASTNode | null;
43
+ export declare function isTruthy(val: unknown): boolean;
44
+ export declare function renderVariables(template: string, context: Record<string, unknown>): string;
45
+ /**
46
+ * Because NML is indentation-based, @else / @endif appear as SIBLINGS of @if
47
+ * in the parent array (not as children). This pass:
48
+ * - Finds @if nodes at index i
49
+ * - Looks ahead for @else at i+1 → moves its children into node.elseBranch
50
+ * - Removes the @else and @endif siblings
51
+ * - Removes @endeach siblings (children already captured by indentation)
52
+ * - Recurses into every node's children array
53
+ */
54
+ export declare function postProcessConditionalsPass(nodes: ASTNode[]): ASTNode[];
55
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAO1E,MAAM,WAAW,eAAgB,SAAQ,cAAc;CAAG;AAE1D,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,GAAG,EAAE,cAAc,CAAC;IACpB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AACrD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElD,qBAAa,cAAe,SAAQ,KAAK;IACvC,GAAG,EAAE,cAAc,CAAC;gBACR,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,cAAuC;CAK1E;AAqCD,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAoH1E;AAoGD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAsE1E;AAMD,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;CACxB,GACL,OAAO,EAAE,CA2HX;AAwMD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,OAAO,GAAG,IAAI,CAOpE;AAiDD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAM9C;AA+BD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,CAmCR;AAuGD;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAmEvE"}