@jigs.design/compiler 0.0.0-stage → 0.1.1
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.
- package/LICENSE +21 -0
- package/README.md +28 -2
- package/dist/ast.d.ts +78 -0
- package/dist/block-hoist.js +237 -0
- package/dist/check-code.d.ts +46 -0
- package/dist/check-code.js +700 -0
- package/dist/codegen.d.ts +8 -0
- package/dist/codegen.js +219 -0
- package/dist/compile.d.ts +177 -0
- package/dist/compile.js +950 -0
- package/dist/hoist.d.ts +20 -0
- package/dist/hoist.js +76 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +9 -0
- package/dist/offsets.d.ts +18 -0
- package/dist/offsets.js +40 -0
- package/dist/parse.d.ts +16 -0
- package/dist/parse.js +690 -0
- package/dist/patch.d.ts +55 -0
- package/dist/patch.js +140 -0
- package/dist/script.d.ts +14 -0
- package/dist/script.js +695 -0
- package/dist/signature.d.ts +5 -0
- package/dist/signature.js +25 -0
- package/dist/spec.d.ts +29 -0
- package/dist/spec.js +14 -0
- package/dist/suggest.d.ts +8 -0
- package/dist/suggest.js +53 -0
- package/package.json +27 -3
package/dist/hoist.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/hoist.d.ts
|
|
2
|
+
/** A document with the page's `{@body}` lines moved to the top. */
|
|
3
|
+
export interface Hoisted {
|
|
4
|
+
/** How many `{@body}` lines moved. */
|
|
5
|
+
moved: number;
|
|
6
|
+
text: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Moves the page's `{@body}` lines to the top of `source`: the top-level
|
|
10
|
+
* ones and those in tags that share the page's scope, not those in `<each>`,
|
|
11
|
+
* `<if>` or `<define>`. They go in `statementOrder`, each after the lines it
|
|
12
|
+
* reads, or as written when there is none. A finished document runs them all
|
|
13
|
+
* before it renders, so the program does the same; only the order a
|
|
14
|
+
* streaming page sees them in changes. A line that moved leaves a
|
|
15
|
+
* blank line behind where Markdown text would otherwise join across it, as
|
|
16
|
+
* the `{@body}` line kept it apart. Null when nothing would move, or when a
|
|
17
|
+
* line has text after its closing `}`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function hoistDeclarations(source: string): Hoisted | null;
|
|
20
|
+
//#endregion
|
package/dist/hoist.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { lineAt } from "./offsets.js";
|
|
2
|
+
import { statementOrder } from "./check-code.js";
|
|
3
|
+
import { parsePickle } from "./parse.js";
|
|
4
|
+
//#region src/hoist.ts
|
|
5
|
+
const blank = (line) => (line ?? "").trim() === "";
|
|
6
|
+
/** Tags whose `{@body}` lines are their own, not the page's. */
|
|
7
|
+
const OWN_SCOPE = /* @__PURE__ */ new Set([
|
|
8
|
+
"define",
|
|
9
|
+
"each",
|
|
10
|
+
"if"
|
|
11
|
+
]);
|
|
12
|
+
/** The page's `{@body}` lines inside tags that share its scope, such as `<card>`. */
|
|
13
|
+
function nested(nodes) {
|
|
14
|
+
return nodes.flatMap((node) => {
|
|
15
|
+
if (node.type === "statement") return [node];
|
|
16
|
+
return node.type === "element" && !OWN_SCOPE.has(node.name) ? nested(node.children) : [];
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Moves the page's `{@body}` lines to the top of `source`: the top-level
|
|
21
|
+
* ones and those in tags that share the page's scope, not those in `<each>`,
|
|
22
|
+
* `<if>` or `<define>`. They go in `statementOrder`, each after the lines it
|
|
23
|
+
* reads, or as written when there is none. A finished document runs them all
|
|
24
|
+
* before it renders, so the program does the same; only the order a
|
|
25
|
+
* streaming page sees them in changes. A line that moved leaves a
|
|
26
|
+
* blank line behind where Markdown text would otherwise join across it, as
|
|
27
|
+
* the `{@body}` line kept it apart. Null when nothing would move, or when a
|
|
28
|
+
* line has text after its closing `}`.
|
|
29
|
+
*/
|
|
30
|
+
function hoistDeclarations(source) {
|
|
31
|
+
const lines = source.split("\n");
|
|
32
|
+
/** Each statement's first and last line, 0-based. */
|
|
33
|
+
const spans = [];
|
|
34
|
+
const document = parsePickle(source);
|
|
35
|
+
const statements = [...document.statements, ...nested(document.children)].toSorted((a, b) => a.start - b.start);
|
|
36
|
+
for (const statement of statements) {
|
|
37
|
+
const last = lineAt(source, statement.end - 1) - 1;
|
|
38
|
+
const lineEnd = source.indexOf("\n", statement.end);
|
|
39
|
+
const after = source.slice(statement.end, lineEnd === -1 ? source.length : lineEnd);
|
|
40
|
+
if (!blank(after)) return null;
|
|
41
|
+
spans.push({
|
|
42
|
+
first: lineAt(source, statement.start) - 1,
|
|
43
|
+
last
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
let top = 0;
|
|
47
|
+
const kept = [];
|
|
48
|
+
for (const span of spans) {
|
|
49
|
+
while (top < span.first && blank(lines[top])) top++;
|
|
50
|
+
if (top !== span.first) break;
|
|
51
|
+
kept.push(span);
|
|
52
|
+
top = span.last + 1;
|
|
53
|
+
}
|
|
54
|
+
const moving = spans.slice(kept.length);
|
|
55
|
+
if (moving.length === 0) return null;
|
|
56
|
+
const head = (kept.at(-1)?.last ?? -1) + 1;
|
|
57
|
+
const order = statementOrder(statements) ?? statements.map((_, index) => index);
|
|
58
|
+
const moved = new Set(moving.flatMap((span) => Array.from({ length: span.last - span.first + 1 }, (_, offset) => span.first + offset)));
|
|
59
|
+
const rest = [];
|
|
60
|
+
for (let index = head; index < lines.length; index++) {
|
|
61
|
+
const line = lines[index];
|
|
62
|
+
if (!moved.has(index)) {
|
|
63
|
+
if (!(moved.has(index - 1) && blank(line) && blank(rest.at(-1)))) rest.push(line ?? "");
|
|
64
|
+
} else if (!moved.has(index + 1) && !blank(rest.at(-1)) && !blank(lines[index + 1])) rest.push("");
|
|
65
|
+
}
|
|
66
|
+
const hoisted = order.flatMap((index) => {
|
|
67
|
+
const span = spans[index];
|
|
68
|
+
return span === void 0 ? [] : lines.slice(span.first, span.last + 1);
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
moved: order.filter((index, place) => index >= kept.length || index !== place).length,
|
|
72
|
+
text: [...hoisted, ...rest].join("\n")
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { hoistDeclarations };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { columnAt, lineAt } from "./offsets.js";
|
|
2
|
+
import { PickleNode, Statement } from "./ast.js";
|
|
3
|
+
import { CompileCatalog, ComponentSpec, PropSpec } from "./spec.js";
|
|
4
|
+
import { ProgramOutput } from "./codegen.js";
|
|
5
|
+
import { CompileOptions, CompileResult, CompiledDefine, Diagnostic, Library, Program, Recipe, RenderNode, compileLibrary, compilePickle } from "./compile.js";
|
|
6
|
+
import { DataRead, ELEMENT, ProcedureUse } from "./check-code.js";
|
|
7
|
+
import { Hoisted, hoistDeclarations } from "./hoist.js";
|
|
8
|
+
import { parsePickle } from "./parse.js";
|
|
9
|
+
import { nearest } from "./suggest.js";
|
|
10
|
+
import { recipeSignature } from "./signature.js";
|
|
11
|
+
import { Edit, EditFailure, EditFailureReason, SEARCH_MARKER, applyEdits, editBreakage, isEditReply, parseEdits } from "./patch.js";
|
|
12
|
+
export { type CompileCatalog, type CompileOptions, type CompileResult, type CompiledDefine, type ComponentSpec, type DataRead, type Diagnostic, ELEMENT, type Edit, type EditFailure, type EditFailureReason, type Hoisted, type Library, type PickleNode, type ProcedureUse, type Program, type ProgramOutput, type PropSpec, type Recipe, type RenderNode, SEARCH_MARKER, type Statement, applyEdits, columnAt, compileLibrary, compilePickle, editBreakage, hoistDeclarations, isEditReply, lineAt, nearest, parseEdits, parsePickle, recipeSignature };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { columnAt, lineAt } from "./offsets.js";
|
|
2
|
+
import { nearest } from "./suggest.js";
|
|
3
|
+
import { ELEMENT } from "./check-code.js";
|
|
4
|
+
import { parsePickle } from "./parse.js";
|
|
5
|
+
import { hoistDeclarations } from "./hoist.js";
|
|
6
|
+
import { compileLibrary, compilePickle } from "./compile.js";
|
|
7
|
+
import { recipeSignature } from "./signature.js";
|
|
8
|
+
import { SEARCH_MARKER, applyEdits, editBreakage, isEditReply, parseEdits } from "./patch.js";
|
|
9
|
+
export { ELEMENT, SEARCH_MARKER, applyEdits, columnAt, compileLibrary, compilePickle, editBreakage, hoistDeclarations, isEditReply, lineAt, nearest, parseEdits, parsePickle, recipeSignature };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/offsets.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Where text the parser cut out sits in the source. Each entry is
|
|
4
|
+
* `[index, offset]`: from `index` in the text on, characters sit in the
|
|
5
|
+
* source from `offset`, until the next entry. The parser trims and joins
|
|
6
|
+
* lines, so text that spans lines has an entry per line.
|
|
7
|
+
*/
|
|
8
|
+
export type OffsetMap = ReadonlyArray<readonly [index: number, offset: number]>;
|
|
9
|
+
/** A source range: the offset of its first character, and just past its last. */
|
|
10
|
+
export interface Range {
|
|
11
|
+
end: number;
|
|
12
|
+
start: number;
|
|
13
|
+
}
|
|
14
|
+
/** The 1-based column of `offset` in `source`. */
|
|
15
|
+
export declare function columnAt(source: string, offset: number): number;
|
|
16
|
+
/** The 1-based line of `offset` in `source`. */
|
|
17
|
+
export declare function lineAt(source: string, offset: number): number;
|
|
18
|
+
//#endregion
|
package/dist/offsets.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/offsets.ts
|
|
2
|
+
/** Adds an entry to `map`, unless the text already continues there. */
|
|
3
|
+
function extendMap(map, index, offset) {
|
|
4
|
+
if (map.length === 0 || sourceOffset(map, index) !== offset) map.push([index, offset]);
|
|
5
|
+
}
|
|
6
|
+
/** The source offset of `index` in the text `map` describes. */
|
|
7
|
+
function sourceOffset(map, index) {
|
|
8
|
+
let found = map[0] ?? [0, 0];
|
|
9
|
+
for (const entry of map) {
|
|
10
|
+
if (entry[0] > index) break;
|
|
11
|
+
found = entry;
|
|
12
|
+
}
|
|
13
|
+
return found[1] + index - found[0];
|
|
14
|
+
}
|
|
15
|
+
/** The source range of `[from, to)` in the text `map` describes. */
|
|
16
|
+
function sourceRange(map, from, to) {
|
|
17
|
+
const start = sourceOffset(map, from);
|
|
18
|
+
return {
|
|
19
|
+
end: to > from ? sourceOffset(map, to - 1) + 1 : start,
|
|
20
|
+
start
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** The map of the text from `from` on, so its index 0 is `from` here. */
|
|
24
|
+
function sliceMap(map, from) {
|
|
25
|
+
const sliced = [[0, sourceOffset(map, from)]];
|
|
26
|
+
for (const [index, offset] of map) if (index > from) extendMap(sliced, index - from, offset);
|
|
27
|
+
return sliced;
|
|
28
|
+
}
|
|
29
|
+
/** The 1-based column of `offset` in `source`. */
|
|
30
|
+
function columnAt(source, offset) {
|
|
31
|
+
return offset === 0 ? 1 : offset - source.lastIndexOf("\n", offset - 1);
|
|
32
|
+
}
|
|
33
|
+
/** The 1-based line of `offset` in `source`. */
|
|
34
|
+
function lineAt(source, offset) {
|
|
35
|
+
let line = 1;
|
|
36
|
+
for (let newline = source.indexOf("\n"); newline !== -1 && newline < offset; newline = source.indexOf("\n", newline + 1)) line++;
|
|
37
|
+
return line;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { columnAt, extendMap, lineAt, sliceMap, sourceOffset, sourceRange };
|
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PickleDocument } from "./ast.js";
|
|
2
|
+
//#region src/parse.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Parses Pickle: Markdown blocks, catalog tags and `{@body ...}` statements.
|
|
5
|
+
*
|
|
6
|
+
* Never throws. Partial input (a response that is still being written) is
|
|
7
|
+
* repaired: open elements are closed and a trailing incomplete tag, expression
|
|
8
|
+
* or statement is dropped. Each fix is recorded in `repairs`.
|
|
9
|
+
*
|
|
10
|
+
* An unclosed tag, `{` or statement stops at the next tag line or `{@body`
|
|
11
|
+
* line rather than swallowing the rest of the document; a tag also stops at a
|
|
12
|
+
* blank line, unless one of its attributes has a `{` still open. Since that text can't be
|
|
13
|
+
* finished by more streaming, it is reported in `errors`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parsePickle(source: string): PickleDocument;
|
|
16
|
+
//#endregion
|