@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/patch.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/patch.d.ts
|
|
2
|
+
export interface Edit {
|
|
3
|
+
replace: string;
|
|
4
|
+
search: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const SEARCH_MARKER = "<<<<<<< SEARCH";
|
|
7
|
+
/**
|
|
8
|
+
* Whether a reply is a set of edits rather than a full document. Null while
|
|
9
|
+
* too little has arrived to tell (Pickle can also start with `<`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function isEditReply(text: string): boolean | null;
|
|
12
|
+
/** Complete SEARCH/REPLACE blocks in `text`; a block still being written is skipped. */
|
|
13
|
+
export declare function parseEdits(text: string): Array<Edit>;
|
|
14
|
+
/**
|
|
15
|
+
* Why an edit was rejected. `missing`: its SEARCH is not in the document.
|
|
16
|
+
* `ambiguous`: its SEARCH is there more than once. `malformed`: the block
|
|
17
|
+
* itself is broken. `broken`: it applied, but the result needs parser repairs
|
|
18
|
+
* the document did not (see `editBreakage`); the server reports this one.
|
|
19
|
+
*/
|
|
20
|
+
export type EditFailureReason = "ambiguous" | "broken" | "malformed" | "missing";
|
|
21
|
+
export interface EditFailure {
|
|
22
|
+
/** For `broken`: the repairs the edited document needs and the base did not. */
|
|
23
|
+
breakage?: ReadonlyArray<{
|
|
24
|
+
line: number;
|
|
25
|
+
message: string;
|
|
26
|
+
}>;
|
|
27
|
+
failed: Edit;
|
|
28
|
+
/** For `ambiguous`: the line each match starts on. */
|
|
29
|
+
lines?: ReadonlyArray<number>;
|
|
30
|
+
reason: EditFailureReason;
|
|
31
|
+
}
|
|
32
|
+
export type ApplyResult = (EditFailure & {
|
|
33
|
+
ok: false;
|
|
34
|
+
}) | {
|
|
35
|
+
ok: true;
|
|
36
|
+
text: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Applies edits in order. Each SEARCH must appear in the document exactly
|
|
40
|
+
* once, exactly or ignoring indentation; an empty SEARCH appends. Stops at
|
|
41
|
+
* the first edit that doesn't match, matches more than once, or whose
|
|
42
|
+
* REPLACE still contains edit markers.
|
|
43
|
+
*/
|
|
44
|
+
export declare function applyEdits(base: string, edits: ReadonlyArray<Edit>): ApplyResult;
|
|
45
|
+
/**
|
|
46
|
+
* The parser repairs an edited document needs that its base did not: an
|
|
47
|
+
* edit that leaves a tag unclosed or closes one twice. Diagnostics are
|
|
48
|
+
* matched by code and message, not line, since an edit moves lines; a
|
|
49
|
+
* repair the base already needed is not new, however many times it moves.
|
|
50
|
+
*/
|
|
51
|
+
export declare function editBreakage<T extends {
|
|
52
|
+
code: string;
|
|
53
|
+
message: string;
|
|
54
|
+
}>(base: ReadonlyArray<T>, edited: ReadonlyArray<T>): Array<T>;
|
|
55
|
+
//#endregion
|
package/dist/patch.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
//#region src/patch.ts
|
|
2
|
+
const SEARCH_MARKER = "<<<<<<< SEARCH";
|
|
3
|
+
const DIVIDER = "=======";
|
|
4
|
+
const REPLACE_MARKER = ">>>>>>> REPLACE";
|
|
5
|
+
const FENCE = /^`{3}[\w-]*$/;
|
|
6
|
+
/**
|
|
7
|
+
* Whether a reply is a set of edits rather than a full document. Null while
|
|
8
|
+
* too little has arrived to tell (Pickle can also start with `<`).
|
|
9
|
+
*/
|
|
10
|
+
function isEditReply(text) {
|
|
11
|
+
const start = text.trimStart().replace(/^`{3}[\w-]*\n/, "");
|
|
12
|
+
if (start.startsWith("<<<<<<< SEARCH")) return true;
|
|
13
|
+
return "<<<<<<< SEARCH".startsWith(start) ? null : false;
|
|
14
|
+
}
|
|
15
|
+
/** Complete SEARCH/REPLACE blocks in `text`; a block still being written is skipped. */
|
|
16
|
+
function parseEdits(text) {
|
|
17
|
+
const edits = [];
|
|
18
|
+
let state = "outside";
|
|
19
|
+
let search = [];
|
|
20
|
+
let replace = [];
|
|
21
|
+
for (const line of text.split("\n")) {
|
|
22
|
+
const marker = line.trim();
|
|
23
|
+
if (state === "outside") {
|
|
24
|
+
if (marker === "<<<<<<< SEARCH") {
|
|
25
|
+
state = "search";
|
|
26
|
+
search = [];
|
|
27
|
+
}
|
|
28
|
+
} else if (state === "search") {
|
|
29
|
+
if (marker === DIVIDER) {
|
|
30
|
+
state = "replace";
|
|
31
|
+
replace = [];
|
|
32
|
+
} else search.push(line);
|
|
33
|
+
} else if (marker === REPLACE_MARKER) {
|
|
34
|
+
edits.push({
|
|
35
|
+
replace: replace.join("\n"),
|
|
36
|
+
search: search.join("\n")
|
|
37
|
+
});
|
|
38
|
+
state = "outside";
|
|
39
|
+
} else if (!FENCE.test(marker)) replace.push(line);
|
|
40
|
+
}
|
|
41
|
+
return edits;
|
|
42
|
+
}
|
|
43
|
+
/** Starts of every line range in `lines` matching `search` when leading and trailing whitespace is ignored. */
|
|
44
|
+
function findLoose(lines, search) {
|
|
45
|
+
const target = search.map((line) => line.trim());
|
|
46
|
+
const starts = [];
|
|
47
|
+
for (let start = 0; start + target.length <= lines.length; start++) if (target.every((line, offset) => lines[start + offset]?.trim() === line)) starts.push(start);
|
|
48
|
+
return starts;
|
|
49
|
+
}
|
|
50
|
+
/** Every index at which `search` occurs in `text`, without overlaps. */
|
|
51
|
+
function findExact(text, search) {
|
|
52
|
+
const indexes = [];
|
|
53
|
+
for (let index = text.indexOf(search); index !== -1; index = text.indexOf(search, index + search.length)) indexes.push(index);
|
|
54
|
+
return indexes;
|
|
55
|
+
}
|
|
56
|
+
const lineAt = (text, index) => text.slice(0, index).split("\n").length;
|
|
57
|
+
const MARKERS = /* @__PURE__ */ new Set([
|
|
58
|
+
DIVIDER,
|
|
59
|
+
REPLACE_MARKER,
|
|
60
|
+
SEARCH_MARKER
|
|
61
|
+
]);
|
|
62
|
+
const hasMarker = (text) => text.split("\n").some((line) => MARKERS.has(line.trim()));
|
|
63
|
+
/**
|
|
64
|
+
* Applies edits in order. Each SEARCH must appear in the document exactly
|
|
65
|
+
* once, exactly or ignoring indentation; an empty SEARCH appends. Stops at
|
|
66
|
+
* the first edit that doesn't match, matches more than once, or whose
|
|
67
|
+
* REPLACE still contains edit markers.
|
|
68
|
+
*/
|
|
69
|
+
function applyEdits(base, edits) {
|
|
70
|
+
let text = base;
|
|
71
|
+
for (const edit of edits) {
|
|
72
|
+
if (hasMarker(edit.replace)) return {
|
|
73
|
+
failed: edit,
|
|
74
|
+
ok: false,
|
|
75
|
+
reason: "malformed"
|
|
76
|
+
};
|
|
77
|
+
if (edit.search.trim() === "") {
|
|
78
|
+
text = `${text.trimEnd()}\n${edit.replace}`;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const exact = findExact(text, edit.search);
|
|
82
|
+
if (exact.length > 1) return {
|
|
83
|
+
failed: edit,
|
|
84
|
+
lines: exact.map((index) => lineAt(text, index)),
|
|
85
|
+
ok: false,
|
|
86
|
+
reason: "ambiguous"
|
|
87
|
+
};
|
|
88
|
+
const [index] = exact;
|
|
89
|
+
if (index !== void 0) {
|
|
90
|
+
text = text.slice(0, index) + edit.replace + text.slice(index + edit.search.length);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const lines = text.split("\n");
|
|
94
|
+
const searchLines = edit.search.split("\n");
|
|
95
|
+
const starts = findLoose(lines, searchLines);
|
|
96
|
+
if (starts.length > 1) return {
|
|
97
|
+
failed: edit,
|
|
98
|
+
lines: starts.map((start) => start + 1),
|
|
99
|
+
ok: false,
|
|
100
|
+
reason: "ambiguous"
|
|
101
|
+
};
|
|
102
|
+
const [start] = starts;
|
|
103
|
+
if (start === void 0) return {
|
|
104
|
+
failed: edit,
|
|
105
|
+
ok: false,
|
|
106
|
+
reason: "missing"
|
|
107
|
+
};
|
|
108
|
+
lines.splice(start, searchLines.length, ...edit.replace.split("\n"));
|
|
109
|
+
text = lines.join("\n");
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
ok: true,
|
|
113
|
+
text
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** Diagnostic codes the parser reports when it changed the source to make it parse. */
|
|
117
|
+
const PARSER_REPAIRS = /* @__PURE__ */ new Set(["repaired", "unclosed"]);
|
|
118
|
+
/**
|
|
119
|
+
* The parser repairs an edited document needs that its base did not: an
|
|
120
|
+
* edit that leaves a tag unclosed or closes one twice. Diagnostics are
|
|
121
|
+
* matched by code and message, not line, since an edit moves lines; a
|
|
122
|
+
* repair the base already needed is not new, however many times it moves.
|
|
123
|
+
*/
|
|
124
|
+
function editBreakage(base, edited) {
|
|
125
|
+
const had = /* @__PURE__ */ new Map();
|
|
126
|
+
for (const diagnostic of base) if (PARSER_REPAIRS.has(diagnostic.code)) {
|
|
127
|
+
const key = `${diagnostic.code}\n${diagnostic.message}`;
|
|
128
|
+
had.set(key, (had.get(key) ?? 0) + 1);
|
|
129
|
+
}
|
|
130
|
+
return edited.filter((diagnostic) => {
|
|
131
|
+
if (!PARSER_REPAIRS.has(diagnostic.code)) return false;
|
|
132
|
+
const key = `${diagnostic.code}\n${diagnostic.message}`;
|
|
133
|
+
const left = had.get(key) ?? 0;
|
|
134
|
+
if (left === 0) return true;
|
|
135
|
+
had.set(key, left - 1);
|
|
136
|
+
return false;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
export { SEARCH_MARKER, applyEdits, editBreakage, isEditReply, parseEdits };
|
package/dist/script.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/script.d.ts
|
|
2
|
+
/** A `useQuery(...)`, `useMutation(...)` or stray `useAction(...)` call, wherever it sits. */
|
|
3
|
+
export interface ProcedureCall {
|
|
4
|
+
/** Just past it, counted as `offset` is. */
|
|
5
|
+
end: number;
|
|
6
|
+
/** `useAction` is not a hook; it is recorded so the server can say to write `useMutation`. */
|
|
7
|
+
hook: "useAction" | "useMutation" | "useQuery";
|
|
8
|
+
/** The first argument when it is a plain string; null when it is computed or missing. */
|
|
9
|
+
name: string | null;
|
|
10
|
+
offset: number;
|
|
11
|
+
/** The first argument is an object: TanStack Query's options form. */
|
|
12
|
+
tanstack: boolean;
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|