@jigs.design/compiler 0.1.1 → 0.1.2
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/README.md +3 -3
- package/dist/ast.d.ts +4 -4
- package/dist/check-code.js +5 -5
- package/dist/codegen.js +24 -24
- package/dist/compile.d.ts +3 -3
- package/dist/compile.js +8 -8
- package/dist/hoist.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/parse.d.ts +3 -3
- package/dist/parse.js +5 -5
- package/dist/patch.d.ts +1 -1
- package/dist/patch.js +1 -1
- package/dist/script.js +5 -5
- package/dist/signature.js +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @jigs.design/compiler
|
|
2
2
|
|
|
3
|
-
Today's
|
|
3
|
+
Today's Jigs compiler: the parser, the checker against a catalog, the code generator, recipe libraries, and the SEARCH/REPLACE edit helpers. `@jigs.design/server` and `@jigs.design/prompt` use it. You import it for tooling, not to run a session.
|
|
4
4
|
|
|
5
5
|
It parses embedded code with [acorn](https://github.com/acornjs/acorn) in strict mode, the mode programs run in, and checks names and the JavaScript subset. It has no built-in catalog. Every caller passes one, and its prop kinds are boolean, enum, handler, number, rows and string.
|
|
6
6
|
|
|
@@ -10,8 +10,8 @@ The [language RFC](../../docs/language-v2/rfc.md) plans its next changes. It evo
|
|
|
10
10
|
|
|
11
11
|
| Export | What it is |
|
|
12
12
|
| ---------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
13
|
+
| `parseJigs` | Source to a document tree. Tolerates a prefix cut off mid-stream |
|
|
14
|
+
| `compileJigs` | Checks a document against a catalog and generates its program |
|
|
15
15
|
| `compileLibrary` | Compiles recipes into a library a document can use |
|
|
16
16
|
| `recipeSignature` | A recipe's props and slot, as the prompt lists them |
|
|
17
17
|
| `isEditReply`, `parseEdits`, `applyEdits`, `SEARCH_MARKER` | Recognise and apply a reply made of SEARCH/REPLACE edits |
|
package/dist/ast.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface PropNode {
|
|
|
28
28
|
}
|
|
29
29
|
/** `start` and `end` cover the opening tag, or the Markdown that made the element. */
|
|
30
30
|
export interface ElementNode extends Range {
|
|
31
|
-
children: Array<
|
|
31
|
+
children: Array<JigsNode>;
|
|
32
32
|
/** Just past its closing tag, or its own end when self-closing; absent when the parser closed it. */
|
|
33
33
|
closeEnd?: number;
|
|
34
34
|
line: number;
|
|
@@ -59,7 +59,7 @@ export interface Statement extends Range {
|
|
|
59
59
|
interface StatementNode extends Statement {
|
|
60
60
|
type: "statement";
|
|
61
61
|
}
|
|
62
|
-
export type
|
|
62
|
+
export type JigsNode = ElementNode | ExprNode | StatementNode | TextNode;
|
|
63
63
|
/** Something the parser fixed or dropped so a partial document still renders. */
|
|
64
64
|
export interface Repair {
|
|
65
65
|
end?: number;
|
|
@@ -67,8 +67,8 @@ export interface Repair {
|
|
|
67
67
|
message: string;
|
|
68
68
|
start?: number;
|
|
69
69
|
}
|
|
70
|
-
export interface
|
|
71
|
-
children: Array<
|
|
70
|
+
export interface JigsDocument {
|
|
71
|
+
children: Array<JigsNode>;
|
|
72
72
|
/** Text that could not be read even though the input is complete there. */
|
|
73
73
|
errors: Array<Repair>;
|
|
74
74
|
repairs: Array<Repair>;
|
package/dist/check-code.js
CHANGED
|
@@ -8,7 +8,7 @@ const ELEMENT = "[]";
|
|
|
8
8
|
/**
|
|
9
9
|
* Names that do not throw ReferenceError in the sandbox: the language's
|
|
10
10
|
* built-ins, the worker's own globals that `LOCKED_GLOBALS` in
|
|
11
|
-
* `@jigs.design/runtime/frame` leaves in place, and
|
|
11
|
+
* `@jigs.design/runtime/frame` leaves in place, and Jigs's bindings. Kept by
|
|
12
12
|
* hand, since the compiler runs on the server; `checks.test.ts` fails if a
|
|
13
13
|
* name here is one the worker locks, or exists in no JavaScript runtime.
|
|
14
14
|
* `Function`, `Proxy`, `Reflect`, `WeakRef`, `FinalizationRegistry` and
|
|
@@ -45,7 +45,7 @@ const KNOWN_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
45
45
|
"NaN",
|
|
46
46
|
"Number",
|
|
47
47
|
"Object",
|
|
48
|
-
"
|
|
48
|
+
"Jigs",
|
|
49
49
|
"Promise",
|
|
50
50
|
"RangeError",
|
|
51
51
|
"ReferenceError",
|
|
@@ -66,9 +66,9 @@ const KNOWN_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
66
66
|
"Uint8ClampedArray",
|
|
67
67
|
"WeakMap",
|
|
68
68
|
"WeakSet",
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
69
|
+
"__jigs",
|
|
70
|
+
"__jigsConstants",
|
|
71
|
+
"__jigsSafe",
|
|
72
72
|
"arguments",
|
|
73
73
|
"atob",
|
|
74
74
|
"btoa",
|
package/dist/codegen.js
CHANGED
|
@@ -2,7 +2,7 @@ import { analyze, isValidBinding } from "./script.js";
|
|
|
2
2
|
//#region src/codegen.ts
|
|
3
3
|
/**
|
|
4
4
|
* Adds `{ key }` to single-argument `useState(...)` and `useMutation(...)`
|
|
5
|
-
* calls, bare or with `
|
|
5
|
+
* calls, bare or with `Jigs.`, named after the declaration, so state and a
|
|
6
6
|
* mutation's status survive recompiles and reordering. The calls come from the
|
|
7
7
|
* parser, so text that only looks like one, in a string or a comment, is left
|
|
8
8
|
* alone.
|
|
@@ -32,12 +32,12 @@ function injectStateKeys(code, baseKey) {
|
|
|
32
32
|
}
|
|
33
33
|
/** Names a spec may not declare: the runtime's own bindings. */
|
|
34
34
|
const RESERVED = /* @__PURE__ */ new Set([
|
|
35
|
-
"
|
|
35
|
+
"Jigs",
|
|
36
36
|
"useMutation",
|
|
37
37
|
"useQuery",
|
|
38
38
|
"useState"
|
|
39
39
|
]);
|
|
40
|
-
const isReserved = (name) => RESERVED.has(name) || name.startsWith("
|
|
40
|
+
const isReserved = (name) => RESERVED.has(name) || name.startsWith("__jigs");
|
|
41
41
|
/** Whether `name` can be bound as a loop variable without hiding the runtime's own names. */
|
|
42
42
|
function isBindingName(name) {
|
|
43
43
|
return !isReserved(name) && isValidBinding(name);
|
|
@@ -47,20 +47,20 @@ function statementProblem(code) {
|
|
|
47
47
|
const analysis = analyze(code, "statement");
|
|
48
48
|
if (!analysis.ok) return analysis.message.includes("'return' outside of function") ? "`return` can't be used in a {@body} line: the page always renders. To hide markup, wrap it in <if test={…}>. The line was removed" : `{@body} line is not valid JavaScript (${analysis.message}); it was removed`;
|
|
49
49
|
const reserved = analysis.declared.find(isReserved);
|
|
50
|
-
return reserved === void 0 ? null : `{@body} line declares "${reserved}", a name
|
|
50
|
+
return reserved === void 0 ? null : `{@body} line declares "${reserved}", a name Jigs uses itself; rename it. The line was removed`;
|
|
51
51
|
}
|
|
52
52
|
/** Why `code` can't be spliced in as an expression, or null when it can. */
|
|
53
53
|
function expressionProblem(code) {
|
|
54
54
|
const analysis = analyze(code, "expression");
|
|
55
55
|
return analysis.ok ? null : analysis.message;
|
|
56
56
|
}
|
|
57
|
-
const fragment = (children) => `
|
|
58
|
-
"
|
|
57
|
+
const fragment = (children) => `__jigs.jsx(${[
|
|
58
|
+
"__jigs.Fragment",
|
|
59
59
|
"null",
|
|
60
60
|
...children
|
|
61
61
|
].join(", ")})`;
|
|
62
62
|
const documentLines = { line: String };
|
|
63
|
-
const libraryLines = { line: () => "
|
|
63
|
+
const libraryLines = { line: () => "__jigsAt" };
|
|
64
64
|
function generateProgram(statements, tree, defines = []) {
|
|
65
65
|
const diagnostics = [];
|
|
66
66
|
const constants = {};
|
|
@@ -88,7 +88,7 @@ function generateProgram(statements, tree, defines = []) {
|
|
|
88
88
|
for (const name of names) declared.add(name);
|
|
89
89
|
const code = injectStateKeys(statement.code, names[0] ?? `statement:${statement.line}`);
|
|
90
90
|
const run = names.length === 0 ? `(() => {\n${code}\n})();` : `({ ${names.join(", ")} } = (() => {\n${code}\nreturn { ${names.join(", ")} };\n})());`;
|
|
91
|
-
body.push(`try {\n${run}\n} catch (error) {
|
|
91
|
+
body.push(`try {\n${run}\n} catch (error) { Jigs.reportError(${lines.line(statement.line)}, error); }`);
|
|
92
92
|
}
|
|
93
93
|
return declared.size > 0 ? [`let ${[...declared].join(", ")};`, ...body] : body;
|
|
94
94
|
};
|
|
@@ -123,7 +123,7 @@ function generateProgram(statements, tree, defines = []) {
|
|
|
123
123
|
const params = [node.item, ...node.index === null ? [] : [node.index]];
|
|
124
124
|
const iteration = [...genStatements(node.statements, lines), `return ${fragment(genChildren(node.children, lines))};`].join("\n");
|
|
125
125
|
const at = lines.line(node.line);
|
|
126
|
-
return `
|
|
126
|
+
return `__jigsSafe(() => __jigs.each((${node.items}\n), (${params.join(", ")}) => {\n${iteration}\n}, ${at}), null, ${at})`;
|
|
127
127
|
}
|
|
128
128
|
case "if": {
|
|
129
129
|
const problem = expressionProblem(node.test);
|
|
@@ -133,12 +133,12 @@ function generateProgram(statements, tree, defines = []) {
|
|
|
133
133
|
}
|
|
134
134
|
const shown = node.statements.length === 0 ? fragment(genChildren(node.children, lines)) : `(() => {\n${[...genStatements(node.statements, lines), `return ${fragment(genChildren(node.children, lines))};`].join("\n")}\n})()`;
|
|
135
135
|
const at = lines.line(node.line);
|
|
136
|
-
return `
|
|
136
|
+
return `__jigsSafe(() => (${node.test}\n) ? ${shown} : null, null, ${at})`;
|
|
137
137
|
}
|
|
138
138
|
case "text": {
|
|
139
139
|
const key = String(nextConstant++);
|
|
140
140
|
constants[key] = node.value;
|
|
141
|
-
return `
|
|
141
|
+
return `__jigsConstants[${JSON.stringify(key)}]`;
|
|
142
142
|
}
|
|
143
143
|
case "expr": {
|
|
144
144
|
const problem = expressionProblem(node.code);
|
|
@@ -146,19 +146,19 @@ function generateProgram(statements, tree, defines = []) {
|
|
|
146
146
|
invalid("invalid_expression", node.line, `{${node.code}} is not a valid expression (${problem}); it was removed`);
|
|
147
147
|
return null;
|
|
148
148
|
}
|
|
149
|
-
return `
|
|
149
|
+
return `__jigsSafe(() => (${node.code}\n), null, ${lines.line(node.line)})`;
|
|
150
150
|
}
|
|
151
151
|
case "element": {
|
|
152
152
|
const propEntries = genPropEntries(node.props, node.line, node.component);
|
|
153
153
|
const children = genChildren(node.children, lines);
|
|
154
|
-
const element = `
|
|
154
|
+
const element = `__jigs.jsx(${[
|
|
155
155
|
JSON.stringify(node.component),
|
|
156
156
|
`{ ${propEntries.props.join(", ")} }`,
|
|
157
157
|
...children
|
|
158
158
|
].join(", ")})`;
|
|
159
|
-
return propEntries.dynamic ? `
|
|
159
|
+
return propEntries.dynamic ? `__jigsSafe(() => ${element}, null, ${lines.line(node.line)})` : element;
|
|
160
160
|
}
|
|
161
|
-
case "slot": return "
|
|
161
|
+
case "slot": return "__jigsChildren";
|
|
162
162
|
case "call": {
|
|
163
163
|
const at = lines.line(node.line);
|
|
164
164
|
const entries = node.key === null ? node.props : {
|
|
@@ -169,12 +169,12 @@ function generateProgram(statements, tree, defines = []) {
|
|
|
169
169
|
const childExprs = genChildren(node.children, lines);
|
|
170
170
|
const children = childExprs.length === 0 ? "null" : childExprs.length === 1 ? childExprs[0] ?? "null" : fragment(childExprs);
|
|
171
171
|
const library = node.origin === "library" ? ", true" : "";
|
|
172
|
-
return `
|
|
172
|
+
return `__jigsSafe(() => __jigs.call(__jigsDef_${node.name}, { ${props.join(", ")} }, ${children}, ${at}, ${node.ordinal}, ${JSON.stringify(node.name)}${library}), null, ${at})`;
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
};
|
|
176
176
|
const genBinding = (prop, lines, line) => {
|
|
177
|
-
const read = `
|
|
177
|
+
const read = `__jigsProps.${prop.name}`;
|
|
178
178
|
if (prop.required || prop.defaultValue === null) return `const ${prop.name} = ${read};`;
|
|
179
179
|
if (prop.defaultValue.kind === "literal") return `const ${prop.name} = ${read} !== undefined ? ${read} : ${JSON.stringify(prop.defaultValue.value)};`;
|
|
180
180
|
const problem = expressionProblem(prop.defaultValue.code);
|
|
@@ -182,7 +182,7 @@ function generateProgram(statements, tree, defines = []) {
|
|
|
182
182
|
invalid("invalid_expression", line, `<define ${prop.name}={${prop.defaultValue.code}}> is not a valid expression (${problem}); the default was removed`);
|
|
183
183
|
return `const ${prop.name} = ${read};`;
|
|
184
184
|
}
|
|
185
|
-
return `const ${prop.name} = ${read} !== undefined ? ${read} :
|
|
185
|
+
return `const ${prop.name} = ${read} !== undefined ? ${read} : __jigsSafe(() => (${prop.defaultValue.code}\n), undefined, ${lines.line(line)});`;
|
|
186
186
|
};
|
|
187
187
|
const genDefine = (def) => {
|
|
188
188
|
const lines = def.origin === "library" ? libraryLines : documentLines;
|
|
@@ -190,7 +190,7 @@ function generateProgram(statements, tree, defines = []) {
|
|
|
190
190
|
const view = fragment(genChildren(def.body, lines));
|
|
191
191
|
const bodyStatements = genStatements(def.statements, lines);
|
|
192
192
|
const body = bodyStatements.length === 0 ? `return ${view};` : `return (() => {\n${bodyStatements.join("\n")}\nreturn ${view};\n})();`;
|
|
193
|
-
return `function
|
|
193
|
+
return `function __jigsDef_${def.name}(__jigsProps, __jigsChildren, __jigsAt) {\n${[...bindings, body].join("\n")}\n}`;
|
|
194
194
|
};
|
|
195
195
|
const functions = defines.map((def) => genDefine(def));
|
|
196
196
|
const children = genChildren(tree, documentLines);
|
|
@@ -199,13 +199,13 @@ function generateProgram(statements, tree, defines = []) {
|
|
|
199
199
|
output: {
|
|
200
200
|
code: [
|
|
201
201
|
"\"use strict\";",
|
|
202
|
-
"const
|
|
203
|
-
"const useState =
|
|
202
|
+
"const __jigsConstants = Jigs.useConstants();",
|
|
203
|
+
"const useState = Jigs.useState, useQuery = Jigs.useQuery, useMutation = Jigs.useMutation;",
|
|
204
204
|
...functions,
|
|
205
|
-
"
|
|
205
|
+
"Jigs.render(__jigs.jsx(() => {",
|
|
206
206
|
...genStatements(statements, documentLines),
|
|
207
|
-
`return
|
|
208
|
-
"
|
|
207
|
+
`return __jigs.jsx(${[
|
|
208
|
+
"__jigs.Fragment",
|
|
209
209
|
"null",
|
|
210
210
|
...children
|
|
211
211
|
].join(",\n")});`,
|
package/dist/compile.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ interface FixEdit {
|
|
|
14
14
|
/**
|
|
15
15
|
* A rewrite that makes a diagnostic's problem go away. The compiler attaches
|
|
16
16
|
* one only when there is a single way to write it, and applies it itself:
|
|
17
|
-
* see `
|
|
17
|
+
* see `compileJigs`.
|
|
18
18
|
*/
|
|
19
19
|
export interface Fix {
|
|
20
20
|
edits: Array<FixEdit>;
|
|
@@ -123,7 +123,7 @@ export interface Program {
|
|
|
123
123
|
statements: Array<Statement>;
|
|
124
124
|
tree: Array<RenderNode>;
|
|
125
125
|
}
|
|
126
|
-
/** A reusable component written in
|
|
126
|
+
/** A reusable component written in Jigs: source with exactly one `<define>`. */
|
|
127
127
|
export interface Recipe {
|
|
128
128
|
name: string;
|
|
129
129
|
source: string;
|
|
@@ -172,6 +172,6 @@ export declare function compileLibrary(recipes: ReadonlyArray<Recipe>, catalog:
|
|
|
172
172
|
* already is. Diagnostics, procedures and their spans stay those of `source`,
|
|
173
173
|
* which is what the model wrote and what the session keeps.
|
|
174
174
|
*/
|
|
175
|
-
export declare function
|
|
175
|
+
export declare function compileJigs(source: string, options: CompileOptions): CompileResult;
|
|
176
176
|
//#endregion
|
|
177
177
|
export {};
|
package/dist/compile.js
CHANGED
|
@@ -3,7 +3,7 @@ import { generateProgram, isBindingName } from "./codegen.js";
|
|
|
3
3
|
import { columnAt, lineAt, sourceRange } from "./offsets.js";
|
|
4
4
|
import { closest, nearest } from "./suggest.js";
|
|
5
5
|
import { checkCode } from "./check-code.js";
|
|
6
|
-
import {
|
|
6
|
+
import { parseJigs } from "./parse.js";
|
|
7
7
|
import { isValidValue } from "./spec.js";
|
|
8
8
|
import { blockHoists } from "./block-hoist.js";
|
|
9
9
|
//#region src/compile.ts
|
|
@@ -34,7 +34,7 @@ const HOOK_FORM = {
|
|
|
34
34
|
useState: "const [open, setOpen] = useState(false)"
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
|
-
* A hook used as a value, written `
|
|
37
|
+
* A hook used as a value, written `Jigs.useX`, called inside a handler
|
|
38
38
|
* or anywhere but as a top-level declaration's initializer, or declared
|
|
39
39
|
* inside `<each>` or `<if>`. Null when the code parses and uses hooks only
|
|
40
40
|
* as it may, or does not parse (the parse error is reported elsewhere).
|
|
@@ -50,7 +50,7 @@ function hookMisuse(code, kind, block) {
|
|
|
50
50
|
if (value !== void 0) return message(`\`${value.hook}\` is not a value: call it in a {@body} line at the top of the document or of a <define>, \`${HOOK_FORM[value.hook]}\``);
|
|
51
51
|
const [call] = facts.hookCalls;
|
|
52
52
|
if (call === void 0) return null;
|
|
53
|
-
const written = call.prefixed ? `
|
|
53
|
+
const written = call.prefixed ? `Jigs.${call.hook}` : call.hook;
|
|
54
54
|
if (block === "each" || block === "if") return message(`${written} ${HOOK_MESSAGE[call.hook][block]}`);
|
|
55
55
|
if (call.prefixed) return message(`\`${written}\` is not supported: write \`${call.hook}\` without the prefix, \`${HOOK_FORM[call.hook]}\``);
|
|
56
56
|
if (call.place === "handler") return message(call.hook === "useMutation" ? `\`useMutation(...)\` inside a handler is not supported: declare \`${HOOK_FORM.useMutation}\` in a {@body} line at the top of the document or of a <define>, and call \`archive.mutate(input)\` from the handler` : `\`${call.hook}(...)\` inside a handler is not supported: declare it in a {@body} line at the top of the document or of a <define>, \`${HOOK_FORM[call.hook]}\`, and read it from the handler`);
|
|
@@ -225,7 +225,7 @@ const TAG_ALIASES = /* @__PURE__ */ new Map([
|
|
|
225
225
|
["ul", "list"],
|
|
226
226
|
["view", "box"]
|
|
227
227
|
]);
|
|
228
|
-
/** Tags a model writes for control flow that
|
|
228
|
+
/** Tags a model writes for control flow that Jigs spells differently. */
|
|
229
229
|
const CONTROL_HINTS = /* @__PURE__ */ new Map([
|
|
230
230
|
["elif", "There is no <elif>: add another <if> with its own test"],
|
|
231
231
|
["else", "There is no <else>: add a second <if> with the opposite test"],
|
|
@@ -243,7 +243,7 @@ function unknownComponentMessage(name, defines, components) {
|
|
|
243
243
|
const hint = lookupHint(name, known) ?? (alias !== void 0 && known.includes(alias) ? alias : void 0) ?? nearest(name, known, 1)[0];
|
|
244
244
|
return hint === void 0 ? `<${name}> is not in the catalog; rendering its children only` : `<${name}> is not in the catalog; did you mean <${hint}>? Rendering its children only`;
|
|
245
245
|
}
|
|
246
|
-
/** Props other UI libraries use, and the name
|
|
246
|
+
/** Props other UI libraries use, and the name Jigs uses for the same thing. */
|
|
247
247
|
const PROP_ALIASES = /* @__PURE__ */ new Map([
|
|
248
248
|
["checked", "value"],
|
|
249
249
|
["onChangeText", "onChange"],
|
|
@@ -755,7 +755,7 @@ function countUses(tree, docDefines) {
|
|
|
755
755
|
return [...counts.values()].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
756
756
|
}
|
|
757
757
|
function compileDocument(source, library, components) {
|
|
758
|
-
const document =
|
|
758
|
+
const document = parseJigs(source);
|
|
759
759
|
const diagnostics = [...document.repairs.map((repair) => ({
|
|
760
760
|
code: "repaired",
|
|
761
761
|
...placeOf(repair),
|
|
@@ -926,7 +926,7 @@ const problems = (diagnostics) => diagnostics.filter((diagnostic) => diagnostic.
|
|
|
926
926
|
* already is. Diagnostics, procedures and their spans stay those of `source`,
|
|
927
927
|
* which is what the model wrote and what the session keeps.
|
|
928
928
|
*/
|
|
929
|
-
function
|
|
929
|
+
function compileJigs(source, options) {
|
|
930
930
|
const library = options.library ?? NO_LIBRARY;
|
|
931
931
|
const result = compileDocument(source, library, options.catalog);
|
|
932
932
|
const fixes = new Set(result.diagnostics.flatMap((diagnostic) => diagnostic.severity === "warning" && diagnostic.fix !== void 0 ? [diagnostic.fix] : []));
|
|
@@ -947,4 +947,4 @@ function compilePickle(source, options) {
|
|
|
947
947
|
};
|
|
948
948
|
}
|
|
949
949
|
//#endregion
|
|
950
|
-
export {
|
|
950
|
+
export { compileJigs, compileLibrary };
|
package/dist/hoist.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { lineAt } from "./offsets.js";
|
|
2
2
|
import { statementOrder } from "./check-code.js";
|
|
3
|
-
import {
|
|
3
|
+
import { parseJigs } from "./parse.js";
|
|
4
4
|
//#region src/hoist.ts
|
|
5
5
|
const blank = (line) => (line ?? "").trim() === "";
|
|
6
6
|
/** Tags whose `{@body}` lines are their own, not the page's. */
|
|
@@ -31,7 +31,7 @@ function hoistDeclarations(source) {
|
|
|
31
31
|
const lines = source.split("\n");
|
|
32
32
|
/** Each statement's first and last line, 0-based. */
|
|
33
33
|
const spans = [];
|
|
34
|
-
const document =
|
|
34
|
+
const document = parseJigs(source);
|
|
35
35
|
const statements = [...document.statements, ...nested(document.children)].toSorted((a, b) => a.start - b.start);
|
|
36
36
|
for (const statement of statements) {
|
|
37
37
|
const last = lineAt(source, statement.end - 1) - 1;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { columnAt, lineAt } from "./offsets.js";
|
|
2
|
-
import {
|
|
2
|
+
import { JigsNode, Statement } from "./ast.js";
|
|
3
3
|
import { CompileCatalog, ComponentSpec, PropSpec } from "./spec.js";
|
|
4
4
|
import { ProgramOutput } from "./codegen.js";
|
|
5
|
-
import { CompileOptions, CompileResult, CompiledDefine, Diagnostic, Library, Program, Recipe, RenderNode,
|
|
5
|
+
import { CompileOptions, CompileResult, CompiledDefine, Diagnostic, Library, Program, Recipe, RenderNode, compileJigs, compileLibrary } from "./compile.js";
|
|
6
6
|
import { DataRead, ELEMENT, ProcedureUse } from "./check-code.js";
|
|
7
7
|
import { Hoisted, hoistDeclarations } from "./hoist.js";
|
|
8
|
-
import {
|
|
8
|
+
import { parseJigs } from "./parse.js";
|
|
9
9
|
import { nearest } from "./suggest.js";
|
|
10
10
|
import { recipeSignature } from "./signature.js";
|
|
11
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
|
|
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 JigsNode, type Library, type ProcedureUse, type Program, type ProgramOutput, type PropSpec, type Recipe, type RenderNode, SEARCH_MARKER, type Statement, applyEdits, columnAt, compileJigs, compileLibrary, editBreakage, hoistDeclarations, isEditReply, lineAt, nearest, parseEdits, parseJigs, recipeSignature };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { columnAt, lineAt } from "./offsets.js";
|
|
2
2
|
import { nearest } from "./suggest.js";
|
|
3
3
|
import { ELEMENT } from "./check-code.js";
|
|
4
|
-
import {
|
|
4
|
+
import { parseJigs } from "./parse.js";
|
|
5
5
|
import { hoistDeclarations } from "./hoist.js";
|
|
6
|
-
import {
|
|
6
|
+
import { compileJigs, compileLibrary } from "./compile.js";
|
|
7
7
|
import { recipeSignature } from "./signature.js";
|
|
8
8
|
import { SEARCH_MARKER, applyEdits, editBreakage, isEditReply, parseEdits } from "./patch.js";
|
|
9
|
-
export { ELEMENT, SEARCH_MARKER, applyEdits, columnAt,
|
|
9
|
+
export { ELEMENT, SEARCH_MARKER, applyEdits, columnAt, compileJigs, compileLibrary, editBreakage, hoistDeclarations, isEditReply, lineAt, nearest, parseEdits, parseJigs, recipeSignature };
|
package/dist/parse.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JigsDocument } from "./ast.js";
|
|
2
2
|
//#region src/parse.d.ts
|
|
3
3
|
/**
|
|
4
|
-
* Parses
|
|
4
|
+
* Parses Jigs: Markdown blocks, catalog tags and `{@body ...}` statements.
|
|
5
5
|
*
|
|
6
6
|
* Never throws. Partial input (a response that is still being written) is
|
|
7
7
|
* repaired: open elements are closed and a trailing incomplete tag, expression
|
|
@@ -12,5 +12,5 @@ import { PickleDocument } from "./ast.js";
|
|
|
12
12
|
* blank line, unless one of its attributes has a `{` still open. Since that text can't be
|
|
13
13
|
* finished by more streaming, it is reported in `errors`.
|
|
14
14
|
*/
|
|
15
|
-
export declare function
|
|
15
|
+
export declare function parseJigs(source: string): JigsDocument;
|
|
16
16
|
//#endregion
|
package/dist/parse.js
CHANGED
|
@@ -201,7 +201,7 @@ function scanCloseTag(source, start, map) {
|
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
/**
|
|
204
|
-
* Splits a run of
|
|
204
|
+
* Splits a run of Jigs into tags, `{expressions}` and text. While more input
|
|
205
205
|
* may follow, an unclosed tag or `{` ends the run as `incomplete`. Once
|
|
206
206
|
* `final`, nothing more can arrive: an unclosed `{` is kept as text and an
|
|
207
207
|
* unclosed tag is dropped, each with a problem. `map` gives the source
|
|
@@ -436,7 +436,7 @@ function createTreeBuilder(repairs, errors) {
|
|
|
436
436
|
};
|
|
437
437
|
/**
|
|
438
438
|
* A Markdown block (heading, paragraph, list item) whose content is inline
|
|
439
|
-
*
|
|
439
|
+
* Jigs. `map` gives the source offsets of `content`.
|
|
440
440
|
*/
|
|
441
441
|
const inlineBlock = (name, props, content, line, final, map) => {
|
|
442
442
|
const range = sourceRange(map, 0, content.length);
|
|
@@ -488,7 +488,7 @@ const headingSizes = [
|
|
|
488
488
|
"md"
|
|
489
489
|
];
|
|
490
490
|
/**
|
|
491
|
-
* Parses
|
|
491
|
+
* Parses Jigs: Markdown blocks, catalog tags and `{@body ...}` statements.
|
|
492
492
|
*
|
|
493
493
|
* Never throws. Partial input (a response that is still being written) is
|
|
494
494
|
* repaired: open elements are closed and a trailing incomplete tag, expression
|
|
@@ -499,7 +499,7 @@ const headingSizes = [
|
|
|
499
499
|
* blank line, unless one of its attributes has a `{` still open. Since that text can't be
|
|
500
500
|
* finished by more streaming, it is reported in `errors`.
|
|
501
501
|
*/
|
|
502
|
-
function
|
|
502
|
+
function parseJigs(source) {
|
|
503
503
|
const lines = source.split(/\r?\n/);
|
|
504
504
|
const lineStarts = [0];
|
|
505
505
|
for (let newline = source.indexOf("\n"); newline !== -1; newline = source.indexOf("\n", newline + 1)) lineStarts.push(newline + 1);
|
|
@@ -687,4 +687,4 @@ function parsePickle(source) {
|
|
|
687
687
|
};
|
|
688
688
|
}
|
|
689
689
|
//#endregion
|
|
690
|
-
export {
|
|
690
|
+
export { parseJigs };
|
package/dist/patch.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface Edit {
|
|
|
6
6
|
export declare const SEARCH_MARKER = "<<<<<<< SEARCH";
|
|
7
7
|
/**
|
|
8
8
|
* Whether a reply is a set of edits rather than a full document. Null while
|
|
9
|
-
* too little has arrived to tell (
|
|
9
|
+
* too little has arrived to tell (Jigs can also start with `<`).
|
|
10
10
|
*/
|
|
11
11
|
export declare function isEditReply(text: string): boolean | null;
|
|
12
12
|
/** Complete SEARCH/REPLACE blocks in `text`; a block still being written is skipped. */
|
package/dist/patch.js
CHANGED
|
@@ -5,7 +5,7 @@ const REPLACE_MARKER = ">>>>>>> REPLACE";
|
|
|
5
5
|
const FENCE = /^`{3}[\w-]*$/;
|
|
6
6
|
/**
|
|
7
7
|
* Whether a reply is a set of edits rather than a full document. Null while
|
|
8
|
-
* too little has arrived to tell (
|
|
8
|
+
* too little has arrived to tell (Jigs can also start with `<`).
|
|
9
9
|
*/
|
|
10
10
|
function isEditReply(text) {
|
|
11
11
|
const start = text.trimStart().replace(/^`{3}[\w-]*\n/, "");
|
package/dist/script.js
CHANGED
|
@@ -55,10 +55,10 @@ const LOOPS = {
|
|
|
55
55
|
ForStatement: "for",
|
|
56
56
|
WhileStatement: "while"
|
|
57
57
|
};
|
|
58
|
-
const unsupported = (syntax, advice) => `${syntax} isn't part of
|
|
58
|
+
const unsupported = (syntax, advice) => `${syntax} isn't part of Jigs's JavaScript: ${advice}`;
|
|
59
59
|
const MESSAGES = {
|
|
60
60
|
class: unsupported("`class`", "use plain objects and arrow functions"),
|
|
61
|
-
fetch: "There is no `fetch` in
|
|
61
|
+
fetch: "There is no `fetch` in Jigs: read data with `useQuery` and a query the prompt lists",
|
|
62
62
|
function: (name) => unsupported(`\`function ${name}()\``, `write \`const ${name} = (…) => …\` instead`),
|
|
63
63
|
getter: unsupported("A getter or setter", "compute the value in a {@body} line instead"),
|
|
64
64
|
import: unsupported("`import()`", "everything a spec can use is built in"),
|
|
@@ -132,11 +132,11 @@ function varNames(node, out) {
|
|
|
132
132
|
if (node.type === "VariableDeclaration" && node.kind === "var") for (const declarator of node.declarations) patternNames(declarator.id, out);
|
|
133
133
|
for (const child of childNodes(node)) if (!isFunction(child)) varNames(child, out);
|
|
134
134
|
}
|
|
135
|
-
/** The name a callee is written as, bare or as `
|
|
135
|
+
/** The name a callee is written as, bare or as `Jigs.name`. */
|
|
136
136
|
function calleeName(node) {
|
|
137
|
-
return node.type === "Identifier" ? node.name : node.type === "MemberExpression" && !node.computed && node.object.type === "Identifier" && node.object.name === "
|
|
137
|
+
return node.type === "Identifier" ? node.name : node.type === "MemberExpression" && !node.computed && node.object.type === "Identifier" && node.object.name === "Jigs" && node.property.type === "Identifier" ? node.property.name : null;
|
|
138
138
|
}
|
|
139
|
-
/** `useQuery`, bare or as `
|
|
139
|
+
/** `useQuery`, bare or as `Jigs.useQuery`. */
|
|
140
140
|
function hookCallee(node) {
|
|
141
141
|
const name = calleeName(node);
|
|
142
142
|
return name === "useMutation" || name === "useQuery" || name === "useState" ? name : null;
|
package/dist/signature.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseJigs } from "./parse.js";
|
|
2
2
|
//#region src/signature.ts
|
|
3
3
|
function formatAttr(prop) {
|
|
4
4
|
if (prop.shorthand === true) return prop.name;
|
|
@@ -15,7 +15,7 @@ function hasSlot(nodes) {
|
|
|
15
15
|
}
|
|
16
16
|
/** `<statCard label value delta={0} note="">…</statCard>` when the define has a slot. */
|
|
17
17
|
function recipeSignature(recipe) {
|
|
18
|
-
const define =
|
|
18
|
+
const define = parseJigs(recipe.source).children.find((node) => node.type === "element" && node.name === "define");
|
|
19
19
|
if (define === void 0) return `<${recipe.name}>`;
|
|
20
20
|
const attrs = define.props.filter((prop) => prop.name !== "name").map((prop) => formatAttr(prop));
|
|
21
21
|
const open = attrs.length === 0 ? `<${recipe.name}>` : `<${recipe.name} ${attrs.join(" ")}>`;
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jigs.design/compiler",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Today's
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Today's Jigs compiler: parser, checker against a catalog, code generator, recipes, and the edit helpers. The v2 rewrite replaces it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/yoamomonstruos/
|
|
8
|
+
"url": "git+https://github.com/yoamomonstruos/jigs.git",
|
|
9
9
|
"directory": "packages/compiler"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|