@hatchingpoint/point 0.0.5 → 0.0.7

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,124 @@
1
+ import type {
2
+ PointCoreDeclaration,
3
+ PointCoreExpression,
4
+ PointCoreFunctionDeclaration,
5
+ PointCoreParameter,
6
+ PointCoreProgram,
7
+ PointCoreStatement,
8
+ PointCoreTypeDeclaration,
9
+ PointCoreValueDeclaration,
10
+ } from "./ast.ts";
11
+
12
+ const BINARY_OPERATORS: Record<string, string> = {
13
+ and: "&&",
14
+ or: "||",
15
+ };
16
+
17
+ /** Emit JavaScript from a core AST program (no type syntax). Production path: parsePointSource → check → emit. */
18
+ export function emitPointCoreJavaScript(program: PointCoreProgram): string {
19
+ const lines: string[] = [];
20
+ lines.push("// Generated by Point. Do not edit directly.");
21
+ if (program.module) lines.push(`// Point module: ${program.module}`);
22
+ lines.push("");
23
+ for (const declaration of program.declarations) {
24
+ lines.push(...emitDeclaration(declaration), "");
25
+ }
26
+ return `${trimTrailingBlankLines(lines).join("\n")}\n`;
27
+ }
28
+
29
+ function emitDeclaration(declaration: PointCoreDeclaration): string[] {
30
+ if (declaration.kind === "import") {
31
+ return [`import { ${declaration.names.join(", ")} } from ${JSON.stringify(declaration.from)};`];
32
+ }
33
+ if (declaration.kind === "external") {
34
+ const imported = declaration.importName ? `${declaration.importName} as ${declaration.name}` : declaration.name;
35
+ return [`import { ${imported} } from ${JSON.stringify(declaration.from)};`];
36
+ }
37
+ if (declaration.kind === "type") return [];
38
+ if (declaration.kind === "value") return [emitValue(declaration, true)];
39
+ return emitFunction(declaration);
40
+ }
41
+
42
+ function emitFunction(declaration: PointCoreFunctionDeclaration): string[] {
43
+ const asyncPrefix = declaration.semantic?.kind === "action" || declaration.semantic?.kind === "workflow" || declaration.semantic?.kind === "command" ? "async " : "";
44
+ return [
45
+ `export ${asyncPrefix}function ${declaration.name}(${declaration.params.map(emitParam).join(", ")}) {`,
46
+ ...indentLines(declaration.body.flatMap((statement) => emitStatement(statement, declaration.semantic?.kind))),
47
+ "}",
48
+ ];
49
+ }
50
+
51
+ function emitStatement(statement: PointCoreStatement, semanticKind?: string): string[] {
52
+ if (statement.kind === "return") {
53
+ if (semanticKind === "view" && statement.value?.kind === "literal" && typeof statement.value.value === "string") {
54
+ return [`return ${JSON.stringify(statement.value.value)};`];
55
+ }
56
+ return [statement.value ? `return ${emitExpression(statement.value)};` : "return;"];
57
+ }
58
+ if (statement.kind === "value") return [emitValue(statement, false)];
59
+ if (statement.kind === "assignment") return [`${statement.name} ${statement.operator} ${emitExpression(statement.value)};`];
60
+ if (statement.kind === "if") {
61
+ const lines = [
62
+ `if (${emitCondition(statement.condition)}) {`,
63
+ ...indentLines(statement.thenBody.flatMap((child) => emitStatement(child, semanticKind))),
64
+ "}",
65
+ ];
66
+ if (statement.elseBody.length > 0) {
67
+ lines.push(
68
+ "else {",
69
+ ...indentLines(statement.elseBody.flatMap((child) => emitStatement(child, semanticKind))),
70
+ "}",
71
+ );
72
+ }
73
+ return lines;
74
+ }
75
+ if (statement.kind === "for") {
76
+ return [
77
+ `for (const ${statement.itemName} of ${emitExpression(statement.iterable)}) {`,
78
+ ...indentLines(statement.body.flatMap((child) => emitStatement(child, semanticKind))),
79
+ "}",
80
+ ];
81
+ }
82
+ return [`${emitExpression(statement.value)};`];
83
+ }
84
+
85
+ function emitValue(declaration: PointCoreValueDeclaration, exported: boolean): string {
86
+ const prefix = exported ? "export " : "";
87
+ const keyword = declaration.mutable ? "let" : "const";
88
+ return `${prefix}${keyword} ${declaration.name} = ${emitExpression(declaration.value)};`;
89
+ }
90
+
91
+ function emitParam(param: PointCoreParameter): string {
92
+ return param.name;
93
+ }
94
+
95
+ function emitExpression(expression: PointCoreExpression): string {
96
+ if (expression.kind === "literal") return JSON.stringify(expression.value);
97
+ if (expression.kind === "identifier") return expression.name;
98
+ if (expression.kind === "list") return `[${expression.items.map(emitExpression).join(", ")}]`;
99
+ if (expression.kind === "record") {
100
+ return `{ ${expression.fields.map((field) => `${field.name}: ${emitExpression(field.value)}`).join(", ")} }`;
101
+ }
102
+ if (expression.kind === "await") return `await ${emitExpression(expression.value)}`;
103
+ if (expression.kind === "property") return `${emitExpression(expression.target)}.${expression.name}`;
104
+ if (expression.kind === "call") {
105
+ if (expression.callee === "Error") return `{ message: ${expression.args[0] ? emitExpression(expression.args[0]) : JSON.stringify("")} }`;
106
+ return `${expression.callee}(${expression.args.map(emitExpression).join(", ")})`;
107
+ }
108
+ const operator = BINARY_OPERATORS[expression.operator] ?? expression.operator;
109
+ return `(${emitExpression(expression.left)} ${operator} ${emitExpression(expression.right)})`;
110
+ }
111
+
112
+ function emitCondition(expression: PointCoreExpression): string {
113
+ const emitted = emitExpression(expression);
114
+ return emitted.startsWith("(") && emitted.endsWith(")") ? emitted.slice(1, -1) : emitted;
115
+ }
116
+
117
+ function indentLines(lines: string[]): string[] {
118
+ return lines.map((line) => ` ${line}`);
119
+ }
120
+
121
+ function trimTrailingBlankLines(lines: string[]): string[] {
122
+ while (lines.at(-1) === "") lines.pop();
123
+ return lines;
124
+ }
@@ -1,133 +1,166 @@
1
- import type {
2
- PointCoreDeclaration,
3
- PointCoreExpression,
4
- PointCoreFunctionDeclaration,
5
- PointCoreParameter,
6
- PointCorePrimitiveType,
7
- PointCoreProgram,
8
- PointCoreStatement,
9
- PointCoreTypeDeclaration,
10
- PointCoreTypeExpression,
11
- PointCoreValueDeclaration,
12
- } from "./ast.ts";
13
-
14
- const BINARY_OPERATORS: Record<string, string> = {
15
- and: "&&",
16
- or: "||",
17
- };
18
-
19
- export function emitPointCoreTypeScript(program: PointCoreProgram): string {
20
- const lines: string[] = [];
21
- lines.push("// Generated by Point. Do not edit directly.");
22
- if (program.module) lines.push(`// Point module: ${program.module}`);
23
- lines.push("");
24
- for (const declaration of program.declarations) {
25
- lines.push(...emitDeclaration(declaration), "");
26
- }
27
- return `${trimTrailingBlankLines(lines).join("\n")}\n`;
28
- }
29
-
30
- function emitDeclaration(declaration: PointCoreDeclaration): string[] {
31
- if (declaration.kind === "import") {
32
- return [`import { ${declaration.names.join(", ")} } from ${JSON.stringify(declaration.from)};`];
33
- }
34
- if (declaration.kind === "type") return emitType(declaration);
35
- if (declaration.kind === "value") return [emitValue(declaration, true)];
36
- return emitFunction(declaration);
37
- }
38
-
39
- function emitType(declaration: PointCoreTypeDeclaration): string[] {
40
- return [
41
- `export interface ${declaration.name} {`,
42
- ...declaration.fields.map((field) => ` ${field.name}: ${emitTypeExpression(field.type)};`),
43
- "}",
44
- ];
45
- }
46
-
47
- function emitFunction(declaration: PointCoreFunctionDeclaration): string[] {
48
- return [
49
- `export function ${declaration.name}(${declaration.params.map(emitParam).join(", ")}): ${emitTypeExpression(declaration.returnType)} {`,
50
- ...indentLines(declaration.body.flatMap((statement) => emitStatement(statement))),
51
- "}",
52
- ];
53
- }
54
-
55
- function emitStatement(statement: PointCoreStatement): string[] {
56
- if (statement.kind === "return") {
57
- return [statement.value ? `return ${emitExpression(statement.value)};` : "return;"];
58
- }
59
- if (statement.kind === "value") return [emitValue(statement, false)];
60
- if (statement.kind === "assignment") return [`${statement.name} ${statement.operator} ${emitExpression(statement.value)};`];
61
- if (statement.kind === "if") {
62
- const lines = [
63
- `if (${emitCondition(statement.condition)}) {`,
64
- ...indentLines(statement.thenBody.flatMap((child) => emitStatement(child))),
65
- "}",
66
- ];
67
- if (statement.elseBody.length > 0) {
68
- lines.push(
69
- "else {",
70
- ...indentLines(statement.elseBody.flatMap((child) => emitStatement(child))),
71
- "}",
72
- );
73
- }
74
- return lines;
75
- }
76
- return [`${emitExpression(statement.value)};`];
77
- }
78
-
79
- function emitValue(declaration: PointCoreValueDeclaration, exported: boolean): string {
80
- const prefix = exported ? "export " : "";
81
- const keyword = declaration.mutable ? "let" : "const";
82
- return `${prefix}${keyword} ${declaration.name}: ${emitTypeExpression(declaration.type)} = ${emitExpression(declaration.value)};`;
83
- }
84
-
85
- function emitParam(param: PointCoreParameter): string {
86
- return `${param.name}: ${emitTypeExpression(param.type)}`;
87
- }
88
-
89
- function emitTypeExpression(type: PointCoreTypeExpression): string {
90
- if (type.name === "List") return `Array<${type.args[0] ? emitTypeExpression(type.args[0]) : "unknown"}>`;
91
- if (isPrimitiveType(type.name)) return emitPrimitiveType(type.name);
92
- return type.name;
93
- }
94
-
95
- function emitPrimitiveType(type: PointCorePrimitiveType): string {
96
- if (type === "Text") return "string";
97
- if (type === "Int" || type === "Float") return "number";
98
- if (type === "Bool") return "boolean";
99
- return "void";
100
- }
101
-
102
- function emitExpression(expression: PointCoreExpression): string {
103
- if (expression.kind === "literal") return JSON.stringify(expression.value);
104
- if (expression.kind === "identifier") return expression.name;
105
- if (expression.kind === "list") return `[${expression.items.map(emitExpression).join(", ")}]`;
106
- if (expression.kind === "record") {
107
- return `{ ${expression.fields.map((field) => `${field.name}: ${emitExpression(field.value)}`).join(", ")} }`;
108
- }
109
- if (expression.kind === "property") return `${emitExpression(expression.target)}.${expression.name}`;
110
- if (expression.kind === "call") {
111
- return `${expression.callee}(${expression.args.map(emitExpression).join(", ")})`;
112
- }
113
- const operator = BINARY_OPERATORS[expression.operator] ?? expression.operator;
114
- return `(${emitExpression(expression.left)} ${operator} ${emitExpression(expression.right)})`;
115
- }
116
-
117
- function emitCondition(expression: PointCoreExpression): string {
118
- const emitted = emitExpression(expression);
119
- return emitted.startsWith("(") && emitted.endsWith(")") ? emitted.slice(1, -1) : emitted;
120
- }
121
-
122
- function isPrimitiveType(type: string): type is PointCorePrimitiveType {
123
- return type === "Text" || type === "Int" || type === "Float" || type === "Bool" || type === "Void";
124
- }
125
-
126
- function indentLines(lines: string[]): string[] {
127
- return lines.map((line) => ` ${line}`);
128
- }
129
-
130
- function trimTrailingBlankLines(lines: string[]): string[] {
131
- while (lines.at(-1) === "") lines.pop();
132
- return lines;
133
- }
1
+ import type {
2
+ PointCoreDeclaration,
3
+ PointCoreExpression,
4
+ PointCoreFunctionDeclaration,
5
+ PointCoreParameter,
6
+ PointCorePrimitiveType,
7
+ PointCoreProgram,
8
+ PointCoreStatement,
9
+ PointCoreTypeDeclaration,
10
+ PointCoreTypeExpression,
11
+ PointCoreValueDeclaration,
12
+ } from "./ast.ts";
13
+
14
+ const BINARY_OPERATORS: Record<string, string> = {
15
+ and: "&&",
16
+ or: "||",
17
+ };
18
+
19
+ /** Emit TypeScript from a core AST program. Production path: parsePointSource → check → emit. */
20
+ export function emitPointCoreTypeScript(program: PointCoreProgram): string {
21
+ const lines: string[] = [];
22
+ lines.push("// Generated by Point. Do not edit directly.");
23
+ if (program.module) lines.push(`// Point module: ${program.module}`);
24
+ lines.push("");
25
+ for (const declaration of program.declarations) {
26
+ lines.push(...emitDeclaration(declaration), "");
27
+ }
28
+ return `${trimTrailingBlankLines(lines).join("\n")}\n`;
29
+ }
30
+
31
+ function emitDeclaration(declaration: PointCoreDeclaration): string[] {
32
+ if (declaration.kind === "import") {
33
+ return [`import { ${declaration.names.join(", ")} } from ${JSON.stringify(declaration.from)};`];
34
+ }
35
+ if (declaration.kind === "external") {
36
+ const imported = declaration.importName ? `${declaration.importName} as ${declaration.name}` : declaration.name;
37
+ return [`import { ${imported} } from ${JSON.stringify(declaration.from)};`];
38
+ }
39
+ if (declaration.kind === "type") return emitType(declaration);
40
+ if (declaration.kind === "value") return [emitValue(declaration, true)];
41
+ return emitFunction(declaration);
42
+ }
43
+
44
+ function emitType(declaration: PointCoreTypeDeclaration): string[] {
45
+ return [
46
+ `export interface ${declaration.name} {`,
47
+ ...declaration.fields.map((field) => ` ${field.name}: ${emitTypeExpression(field.type)};`),
48
+ "}",
49
+ ];
50
+ }
51
+
52
+ function emitFunction(declaration: PointCoreFunctionDeclaration): string[] {
53
+ const asyncPrefix = declaration.semantic?.kind === "action" || declaration.semantic?.kind === "workflow" || declaration.semantic?.kind === "command" ? "async " : "";
54
+ const returnType =
55
+ declaration.semantic?.kind === "action" || declaration.semantic?.kind === "workflow" || declaration.semantic?.kind === "command"
56
+ ? `Promise<${emitTypeExpression(declaration.returnType)}>`
57
+ : declaration.semantic?.kind === "view"
58
+ ? "JSX.Element"
59
+ : declaration.semantic?.kind === "route"
60
+ ? "Response | string"
61
+ : emitTypeExpression(declaration.returnType);
62
+ return [
63
+ `export ${asyncPrefix}function ${declaration.name}(${declaration.params.map(emitParam).join(", ")}): ${returnType} {`,
64
+ ...indentLines(declaration.body.flatMap((statement) => emitStatement(statement, declaration.semantic?.kind))),
65
+ "}",
66
+ ];
67
+ }
68
+
69
+ function emitStatement(statement: PointCoreStatement, semanticKind?: string): string[] {
70
+ if (statement.kind === "return") {
71
+ if (semanticKind === "view" && statement.value?.kind === "literal" && typeof statement.value.value === "string") {
72
+ return [`return <>${escapeJsxText(statement.value.value)}</>;`];
73
+ }
74
+ return [statement.value ? `return ${emitExpression(statement.value)};` : "return;"];
75
+ }
76
+ if (statement.kind === "value") return [emitValue(statement, false)];
77
+ if (statement.kind === "assignment") return [`${statement.name} ${statement.operator} ${emitExpression(statement.value)};`];
78
+ if (statement.kind === "if") {
79
+ const lines = [
80
+ `if (${emitCondition(statement.condition)}) {`,
81
+ ...indentLines(statement.thenBody.flatMap((child) => emitStatement(child, semanticKind))),
82
+ "}",
83
+ ];
84
+ if (statement.elseBody.length > 0) {
85
+ lines.push(
86
+ "else {",
87
+ ...indentLines(statement.elseBody.flatMap((child) => emitStatement(child, semanticKind))),
88
+ "}",
89
+ );
90
+ }
91
+ return lines;
92
+ }
93
+ if (statement.kind === "for") {
94
+ return [
95
+ `for (const ${statement.itemName} of ${emitExpression(statement.iterable)}) {`,
96
+ ...indentLines(statement.body.flatMap((child) => emitStatement(child, semanticKind))),
97
+ "}",
98
+ ];
99
+ }
100
+ return [`${emitExpression(statement.value)};`];
101
+ }
102
+
103
+ function escapeJsxText(value: string): string {
104
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
105
+ }
106
+
107
+ function emitValue(declaration: PointCoreValueDeclaration, exported: boolean): string {
108
+ const prefix = exported ? "export " : "";
109
+ const keyword = declaration.mutable ? "let" : "const";
110
+ return `${prefix}${keyword} ${declaration.name}: ${emitTypeExpression(declaration.type)} = ${emitExpression(declaration.value)};`;
111
+ }
112
+
113
+ function emitParam(param: PointCoreParameter): string {
114
+ return `${param.name}: ${emitTypeExpression(param.type)}`;
115
+ }
116
+
117
+ function emitTypeExpression(type: PointCoreTypeExpression): string {
118
+ if (type.name === "List") return `Array<${type.args[0] ? emitTypeExpression(type.args[0]) : "unknown"}>`;
119
+ if (type.name === "Maybe") return `${type.args[0] ? emitTypeExpression(type.args[0]) : "unknown"} | null`;
120
+ if (type.name === "Or") return type.args.map(emitTypeExpression).join(" | ");
121
+ if (type.name === "Error") return "{ message: string }";
122
+ if (isPrimitiveType(type.name)) return emitPrimitiveType(type.name);
123
+ return type.name;
124
+ }
125
+
126
+ function emitPrimitiveType(type: PointCorePrimitiveType): string {
127
+ if (type === "Text") return "string";
128
+ if (type === "Int" || type === "Float") return "number";
129
+ if (type === "Bool") return "boolean";
130
+ return "void";
131
+ }
132
+
133
+ function emitExpression(expression: PointCoreExpression): string {
134
+ if (expression.kind === "literal") return JSON.stringify(expression.value);
135
+ if (expression.kind === "identifier") return expression.name;
136
+ if (expression.kind === "list") return `[${expression.items.map(emitExpression).join(", ")}]`;
137
+ if (expression.kind === "record") {
138
+ return `{ ${expression.fields.map((field) => `${field.name}: ${emitExpression(field.value)}`).join(", ")} }`;
139
+ }
140
+ if (expression.kind === "await") return `await ${emitExpression(expression.value)}`;
141
+ if (expression.kind === "property") return `${emitExpression(expression.target)}.${expression.name}`;
142
+ if (expression.kind === "call") {
143
+ if (expression.callee === "Error") return `{ message: ${expression.args[0] ? emitExpression(expression.args[0]) : JSON.stringify("")} }`;
144
+ return `${expression.callee}(${expression.args.map(emitExpression).join(", ")})`;
145
+ }
146
+ const operator = BINARY_OPERATORS[expression.operator] ?? expression.operator;
147
+ return `(${emitExpression(expression.left)} ${operator} ${emitExpression(expression.right)})`;
148
+ }
149
+
150
+ function emitCondition(expression: PointCoreExpression): string {
151
+ const emitted = emitExpression(expression);
152
+ return emitted.startsWith("(") && emitted.endsWith(")") ? emitted.slice(1, -1) : emitted;
153
+ }
154
+
155
+ function isPrimitiveType(type: string): type is PointCorePrimitiveType {
156
+ return type === "Text" || type === "Int" || type === "Float" || type === "Bool" || type === "Void";
157
+ }
158
+
159
+ function indentLines(lines: string[]): string[] {
160
+ return lines.map((line) => ` ${line}`);
161
+ }
162
+
163
+ function trimTrailingBlankLines(lines: string[]): string[] {
164
+ while (lines.at(-1) === "") lines.pop();
165
+ return lines;
166
+ }
@@ -1,104 +1,6 @@
1
- import type {
2
- PointCoreDeclaration,
3
- PointCoreExpression,
4
- PointCoreFunctionDeclaration,
5
- PointCoreParameter,
6
- PointCoreProgram,
7
- PointCoreStatement,
8
- PointCoreTypeDeclaration,
9
- PointCoreValueDeclaration,
10
- } from "./ast.ts";
11
-
12
- export function formatPointCore(program: PointCoreProgram): string {
13
- const lines: string[] = [];
14
- if (program.module) {
15
- lines.push(`module ${program.module}`, "");
16
- }
17
- program.declarations.forEach((declaration, index) => {
18
- if (index > 0) lines.push("");
19
- lines.push(...formatDeclaration(declaration));
20
- });
21
- return `${lines.join("\n")}\n`;
22
- }
23
-
24
- function formatDeclaration(declaration: PointCoreDeclaration): string[] {
25
- if (declaration.kind === "import") {
26
- return [`import { ${declaration.names.join(", ")} } from ${JSON.stringify(declaration.from)}`];
27
- }
28
- if (declaration.kind === "value") return [formatValue(declaration)];
29
- if (declaration.kind === "type") return formatType(declaration);
30
- return formatFunction(declaration);
31
- }
32
-
33
- function formatType(declaration: PointCoreTypeDeclaration): string[] {
34
- return [
35
- `type ${declaration.name} {`,
36
- ...declaration.fields.map((field) => ` ${formatParam(field)}`),
37
- "}",
38
- ];
39
- }
40
-
41
- function formatFunction(declaration: PointCoreFunctionDeclaration): string[] {
42
- return [
43
- `fn ${declaration.name}(${declaration.params.map(formatParam).join(", ")}): ${formatTypeExpression(declaration.returnType)} {`,
44
- ...indentLines(declaration.body.flatMap((statement) => formatStatementLines(statement))),
45
- "}",
46
- ];
47
- }
48
-
49
- function formatStatementLines(statement: PointCoreStatement): string[] {
50
- if (statement.kind === "return") {
51
- return [statement.value ? `return ${formatExpression(statement.value)}` : "return"];
52
- }
53
- if (statement.kind === "value") return [formatValue(statement)];
54
- if (statement.kind === "assignment") return [`${statement.name} ${statement.operator} ${formatExpression(statement.value)}`];
55
- if (statement.kind === "if") return formatIf(statement);
56
- return [formatExpression(statement.value)];
57
- }
58
-
59
- function formatIf(statement: Extract<PointCoreStatement, { kind: "if" }>): string[] {
60
- const lines = [
61
- `if ${formatExpression(statement.condition)} {`,
62
- ...indentLines(statement.thenBody.flatMap((child) => formatStatementLines(child))),
63
- "}",
64
- ];
65
- if (statement.elseBody.length > 0) {
66
- lines.push(
67
- "else {",
68
- ...indentLines(statement.elseBody.flatMap((child) => formatStatementLines(child))),
69
- "}",
70
- );
71
- }
72
- return lines;
73
- }
74
-
75
- function formatValue(declaration: PointCoreValueDeclaration): string {
76
- return `${declaration.mutable ? "var" : "let"} ${declaration.name}: ${formatTypeExpression(declaration.type)} = ${formatExpression(declaration.value)}`;
77
- }
78
-
79
- function formatParam(param: PointCoreParameter): string {
80
- return `${param.name}: ${formatTypeExpression(param.type)}`;
81
- }
82
-
83
- function formatExpression(expression: PointCoreExpression): string {
84
- if (expression.kind === "literal") return JSON.stringify(expression.value);
85
- if (expression.kind === "identifier") return expression.name;
86
- if (expression.kind === "list") return `[${expression.items.map(formatExpression).join(", ")}]`;
87
- if (expression.kind === "record") {
88
- return `{ ${expression.fields.map((field) => `${field.name}: ${formatExpression(field.value)}`).join(", ")} }`;
89
- }
90
- if (expression.kind === "property") return `${formatExpression(expression.target)}.${expression.name}`;
91
- if (expression.kind === "binary") {
92
- return `${formatExpression(expression.left)} ${expression.operator} ${formatExpression(expression.right)}`;
93
- }
94
- return `${expression.callee}(${expression.args.map(formatExpression).join(", ")})`;
95
- }
96
-
97
- function formatTypeExpression(type: PointCoreParameter["type"]): string {
98
- if (type.args.length === 0) return String(type.name);
99
- return `${type.name}<${type.args.map(formatTypeExpression).join(", ")}>`;
100
- }
101
-
102
- function indentLines(lines: string[]): string[] {
103
- return lines.map((line) => ` ${line}`);
104
- }
1
+ import { formatSemanticProgram } from "../semantic/format.ts";
2
+ import { parseSemanticSource } from "../semantic/parse.ts";
3
+
4
+ export function formatPointSource(source: string): string {
5
+ return formatSemanticProgram(parseSemanticSource(source));
6
+ }
@@ -0,0 +1,53 @@
1
+ import { createHash } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+
4
+ const CACHE_DIR = ".point-cache";
5
+ const MANIFEST = "manifest.json";
6
+
7
+ export interface PointBuildCacheEntry {
8
+ sourceHash: string;
9
+ checkedAt: string;
10
+ ok: boolean;
11
+ }
12
+
13
+ export interface PointBuildCacheManifest {
14
+ schemaVersion: "point.cache.v1";
15
+ entries: Record<string, PointBuildCacheEntry>;
16
+ }
17
+
18
+ export function hashPointSource(source: string): string {
19
+ return createHash("sha256").update(source).digest("hex");
20
+ }
21
+
22
+ export async function readBuildCache(cwd = process.cwd()): Promise<PointBuildCacheManifest> {
23
+ const path = resolve(cwd, CACHE_DIR, MANIFEST);
24
+ if (!(await Bun.file(path).exists())) {
25
+ return { schemaVersion: "point.cache.v1", entries: {} };
26
+ }
27
+ return Bun.file(path).json();
28
+ }
29
+
30
+ export async function writeBuildCache(manifest: PointBuildCacheManifest, cwd = process.cwd()): Promise<void> {
31
+ const path = resolve(cwd, CACHE_DIR, MANIFEST);
32
+ await Bun.$`mkdir -p ${resolve(cwd, CACHE_DIR)}`.quiet();
33
+ await Bun.write(path, `${JSON.stringify(manifest, null, 2)}\n`);
34
+ }
35
+
36
+ export function isIncrementalEnabled(): boolean {
37
+ return process.env.POINT_INCREMENTAL === "1" || process.env.POINT_INCREMENTAL === "true";
38
+ }
39
+
40
+ export function isCacheHit(manifest: PointBuildCacheManifest, input: string, source: string): boolean {
41
+ const entry = manifest.entries[input];
42
+ return Boolean(entry && entry.sourceHash === hashPointSource(source) && entry.ok);
43
+ }
44
+
45
+ export function recordCacheEntry(manifest: PointBuildCacheManifest, input: string, source: string, ok: boolean): PointBuildCacheManifest {
46
+ return {
47
+ ...manifest,
48
+ entries: {
49
+ ...manifest.entries,
50
+ [input]: { sourceHash: hashPointSource(source), checkedAt: new Date().toISOString(), ok },
51
+ },
52
+ };
53
+ }
package/src/core/index.ts CHANGED
@@ -1,7 +1,12 @@
1
- export * from "./ast.ts";
2
- export * from "./check.ts";
3
- export * from "./context.ts";
4
- export * from "./emit-typescript.ts";
5
- export * from "./format.ts";
6
- export * from "./lexer.ts";
7
- export * from "./parser.ts";
1
+ export * from "./ast.ts";
2
+ export * from "./check.ts";
3
+ export { findRunEntryName } from "./cli.ts";
4
+ export * from "./context.ts";
5
+ export * from "./emit-typescript.ts";
6
+ export * from "./emit-javascript.ts";
7
+ export * from "./incremental.ts";
8
+ export * from "./serialize.ts";
9
+ export * from "../semantic/index.ts";
10
+ export * from "./format.ts";
11
+ export * from "./lexer.ts";
12
+ export * from "./parser.ts";