rip-lang 3.13.91 → 3.13.93
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/CHANGELOG.md +2 -2
- package/README.md +3 -3
- package/bin/rip +11 -1
- package/docs/AGENTS.md +43 -0
- package/docs/RIP-LANG.md +7 -3
- package/docs/RIP-TYPES.md +72 -91
- package/docs/charts.html +15 -15
- package/docs/index.html +2 -2
- package/docs/ui/hljs-rip.js +2 -2
- package/package.json +1 -1
- package/src/AGENTS.md +456 -0
- package/src/lexer.js +1 -2
- package/src/typecheck.js +188 -6
- package/src/types.js +63 -38
package/src/typecheck.js
CHANGED
|
@@ -33,14 +33,55 @@ export function countLines(str) {
|
|
|
33
33
|
export function toVirtual(p) { return p + '.ts'; }
|
|
34
34
|
export function fromVirtual(p) { return p.endsWith('.rip.ts') ? p.slice(0, -3) : p; }
|
|
35
35
|
|
|
36
|
+
// Patch uninitialized, untyped variables with inferred types from their
|
|
37
|
+
// first assignment. This makes `let total; total = count + ratio;` behave
|
|
38
|
+
// like `let total: number;` — so a later `total = "string"` is caught.
|
|
39
|
+
// Called by both the LSP and the CLI type-checker to keep them aligned.
|
|
40
|
+
export function patchUninitializedTypes(ts, service, compiledEntries) {
|
|
41
|
+
const program = service.getProgram();
|
|
42
|
+
if (!program) return;
|
|
43
|
+
const checker = program.getTypeChecker();
|
|
44
|
+
for (const [filePath] of compiledEntries) {
|
|
45
|
+
const sf = program.getSourceFile(toVirtual(filePath));
|
|
46
|
+
if (!sf) continue;
|
|
47
|
+
const uninitialized = new Map();
|
|
48
|
+
for (const stmt of sf.statements) {
|
|
49
|
+
if (ts.isVariableStatement(stmt)) {
|
|
50
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
51
|
+
if (!decl.initializer && !decl.type && ts.isIdentifier(decl.name)) {
|
|
52
|
+
const sym = checker.getSymbolAtLocation(decl.name);
|
|
53
|
+
if (sym) uninitialized.set(decl.name.text, sym);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (ts.isExpressionStatement(stmt) && ts.isBinaryExpression(stmt.expression) &&
|
|
58
|
+
stmt.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
59
|
+
ts.isIdentifier(stmt.expression.left)) {
|
|
60
|
+
const name = stmt.expression.left.text;
|
|
61
|
+
const sym = uninitialized.get(name);
|
|
62
|
+
if (sym) {
|
|
63
|
+
const rhsType = checker.getTypeAtLocation(stmt.expression.right);
|
|
64
|
+
sym.flags |= ts.SymbolFlags.Transient;
|
|
65
|
+
sym.links = { type: rhsType };
|
|
66
|
+
uninitialized.delete(name);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
36
73
|
// TS error codes to skip — Rip resolves modules differently and
|
|
37
74
|
// treats async return types transparently.
|
|
38
75
|
export const SKIP_CODES = new Set([
|
|
39
76
|
2300, // Duplicate identifier (DTS declarations coexist with compiled class bodies)
|
|
40
77
|
2304, // Cannot find name
|
|
41
78
|
2307, // Cannot find module
|
|
79
|
+
2389, // Function implementation name must match overload (DTS + compiled body)
|
|
80
|
+
2391, // Function implementation is missing (DTS overload sigs separated from implementations)
|
|
42
81
|
2393, // Duplicate function implementation
|
|
82
|
+
2394, // Overload signature not compatible with implementation (untyped compiled params)
|
|
43
83
|
2451, // Cannot redeclare block-scoped variable
|
|
84
|
+
2567, // Enum declarations can only merge with namespace or other enum (DTS + compiled body)
|
|
44
85
|
1064, // Return type of async function must be Promise
|
|
45
86
|
2582, // Cannot find name 'test' (test runner globals)
|
|
46
87
|
2593, // Cannot find name 'describe' (test runner globals)
|
|
@@ -94,26 +135,120 @@ export function compileForCheck(filePath, source, compiler) {
|
|
|
94
135
|
// Ensure every file is treated as a module (not a global script)
|
|
95
136
|
if (!/\bexport\b/.test(code) && !/\bimport\b/.test(code)) code += '\nexport {};\n';
|
|
96
137
|
|
|
97
|
-
|
|
98
|
-
|
|
138
|
+
// Interleave function overload signatures from DTS header into the code
|
|
139
|
+
// section, immediately before their implementations. TypeScript requires
|
|
140
|
+
// overload signatures adjacent to the implementation — without this, TS
|
|
141
|
+
// reports error 2391 ("Function implementation is missing or not immediately
|
|
142
|
+
// following the declaration"). Moving signatures into the code also enables
|
|
143
|
+
// proper call-site type checking of function parameters.
|
|
144
|
+
let headerDts = dts;
|
|
145
|
+
if (hasTypes && dts && code) {
|
|
146
|
+
const dl = dts.split('\n');
|
|
147
|
+
const cl = code.split('\n');
|
|
148
|
+
const fnSigs = [];
|
|
149
|
+
for (let i = 0; i < dl.length; i++) {
|
|
150
|
+
const m = dl[i].match(/^(?:export\s+)?(?:declare\s+)?function\s+(\w+)/);
|
|
151
|
+
if (m) fnSigs.push({ name: m[1], sig: dl[i], idx: i });
|
|
152
|
+
}
|
|
153
|
+
if (fnSigs.length > 0) {
|
|
154
|
+
const injections = [];
|
|
155
|
+
const moved = new Set();
|
|
156
|
+
for (const fn of fnSigs) {
|
|
157
|
+
const pat = new RegExp(`^(?:export\\s+)?(?:async\\s+)?function\\s+${fn.name}\\s*[(<]`);
|
|
158
|
+
for (let j = 0; j < cl.length; j++) {
|
|
159
|
+
if (pat.test(cl[j])) {
|
|
160
|
+
injections.push({ codeLine: j, sig: fn.sig });
|
|
161
|
+
moved.add(fn.idx);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (injections.length > 0) {
|
|
167
|
+
injections.sort((a, b) => a.codeLine - b.codeLine);
|
|
168
|
+
// Adjust reverseMap: each injection shifts subsequent code lines down by 1
|
|
169
|
+
if (result.reverseMap) {
|
|
170
|
+
for (const [, entry] of result.reverseMap) {
|
|
171
|
+
let offset = 0;
|
|
172
|
+
for (const inj of injections) {
|
|
173
|
+
if (inj.codeLine <= entry.genLine + offset) offset++;
|
|
174
|
+
}
|
|
175
|
+
entry.genLine += offset;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Insert signatures bottom-up to preserve indices.
|
|
179
|
+
// Strip 'declare ' — signatures must be non-ambient to match implementations.
|
|
180
|
+
for (let k = injections.length - 1; k >= 0; k--) {
|
|
181
|
+
cl.splice(injections[k].codeLine, 0, injections[k].sig.replace(/^declare /, ''));
|
|
182
|
+
}
|
|
183
|
+
code = cl.join('\n');
|
|
184
|
+
// Rebuild header DTS without the moved function signatures
|
|
185
|
+
headerDts = dl.filter((_, i) => !moved.has(i)).join('\n').trimEnd() + '\n';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Annotate reactive/readonly/computed const assignments with their declared
|
|
191
|
+
// types from the DTS header, and remove the corresponding `declare const`
|
|
192
|
+
// from the header. This enables TypeScript to check initializer values
|
|
193
|
+
// against type annotations: `const x: Signal<number> = __state("oops")`
|
|
194
|
+
// produces a real type error, whereas two separate declarations
|
|
195
|
+
// (`declare const x: Signal<number>` + `const x = __state("oops")`)
|
|
196
|
+
// only produce a duplicate-identifier error (2451), which is suppressed.
|
|
197
|
+
if (hasTypes && headerDts && code) {
|
|
198
|
+
const dl = headerDts.split('\n');
|
|
199
|
+
const cl = code.split('\n');
|
|
200
|
+
const constTypes = new Map();
|
|
201
|
+
|
|
202
|
+
for (let i = 0; i < dl.length; i++) {
|
|
203
|
+
const m = dl[i].match(/^(?:export\s+)?declare\s+const\s+(\w+):\s+(.+);$/);
|
|
204
|
+
if (m) constTypes.set(m[1], { type: m[2], idx: i });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (constTypes.size > 0) {
|
|
208
|
+
const movedDts = new Set();
|
|
209
|
+
|
|
210
|
+
for (let j = 0; j < cl.length; j++) {
|
|
211
|
+
const cm = cl[j].match(/^((?:export\s+)?const\s+)(\w+)(\s*=\s*)/);
|
|
212
|
+
if (cm && constTypes.has(cm[2])) {
|
|
213
|
+
const entry = constTypes.get(cm[2]);
|
|
214
|
+
cl[j] = cm[1] + cm[2] + ': ' + entry.type + cm[3] + cl[j].slice(cm[0].length);
|
|
215
|
+
movedDts.add(entry.idx);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (movedDts.size > 0) {
|
|
220
|
+
code = cl.join('\n');
|
|
221
|
+
headerDts = dl.filter((_, i) => !movedDts.has(i)).join('\n').trimEnd() + '\n';
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let tsContent = (hasTypes ? headerDts + '\n' : '') + code;
|
|
227
|
+
const headerLines = hasTypes ? countLines(headerDts + '\n') : 1;
|
|
99
228
|
|
|
100
229
|
// Build bidirectional line maps
|
|
101
230
|
const { srcToGen, genToSrc } = buildLineMap(result.reverseMap, result.map, headerLines);
|
|
102
231
|
|
|
232
|
+
// Snapshot code-section mappings before DTS mapping can overwrite them.
|
|
233
|
+
// Needed by @ts-expect-error injection which must target code lines, not DTS.
|
|
234
|
+
const codeSrcToGen = new Map(srcToGen);
|
|
235
|
+
|
|
103
236
|
// Map DTS declaration lines back to source lines (bidirectional).
|
|
104
|
-
// Covers: let/var declarations, type aliases, interfaces, enums, classes.
|
|
237
|
+
// Covers: imports, let/var declarations, type aliases, interfaces, enums, classes.
|
|
105
238
|
// This enables hover, go-to-definition, and diagnostics for type-only code.
|
|
106
|
-
if (hasTypes &&
|
|
107
|
-
const dtsLines =
|
|
239
|
+
if (hasTypes && headerDts) {
|
|
240
|
+
const dtsLines = headerDts.split('\n');
|
|
108
241
|
const srcLines = source.split('\n');
|
|
109
242
|
for (let i = 0; i < dtsLines.length; i++) {
|
|
110
243
|
const line = dtsLines[i];
|
|
244
|
+
|
|
111
245
|
const m = line.match(/^(?:export\s+)?(?:declare\s+)?(?:let|var|type|interface|enum|class)\s+(\w+)/);
|
|
112
246
|
if (!m) continue;
|
|
113
247
|
const name = m[1];
|
|
114
248
|
for (let s = 0; s < srcLines.length; s++) {
|
|
115
249
|
const src = srcLines[s];
|
|
116
|
-
if (new RegExp('\\b' + name + '\\s
|
|
250
|
+
if (new RegExp('\\b' + name + '\\s*::').test(src) ||
|
|
251
|
+
new RegExp('^(?:export\\s+)?type\\s+' + name + '\\b').test(src) ||
|
|
117
252
|
new RegExp('^(?:export\\s+)?interface\\s+' + name + '\\b').test(src) ||
|
|
118
253
|
new RegExp('^(?:export\\s+)?enum\\s+' + name + '\\b').test(src) ||
|
|
119
254
|
new RegExp('^(?:export\\s+)?' + name + '\\s*=\\s*component\\b').test(src)) {
|
|
@@ -144,6 +279,50 @@ export function compileForCheck(filePath, source, compiler) {
|
|
|
144
279
|
}
|
|
145
280
|
}
|
|
146
281
|
|
|
282
|
+
// Inject @ts-expect-error directives from Rip source into the generated
|
|
283
|
+
// TypeScript. This lets TypeScript natively suppress expected errors and
|
|
284
|
+
// report TS2578 for unused directives — works in both CLI and LSP.
|
|
285
|
+
if (hasTypes) {
|
|
286
|
+
const srcLines = source.split('\n');
|
|
287
|
+
const injects = [];
|
|
288
|
+
for (let s = 0; s < srcLines.length; s++) {
|
|
289
|
+
const m = srcLines[s].match(/^\s*#\s*(@ts-expect-error\b.*)/);
|
|
290
|
+
if (m) {
|
|
291
|
+
const nextSrc = s + 1;
|
|
292
|
+
// Prefer code-section line (where the assignment lives and TS reports
|
|
293
|
+
// the error) over the DTS declaration line.
|
|
294
|
+
let genLine = codeSrcToGen.get(nextSrc);
|
|
295
|
+
if (genLine === undefined) {
|
|
296
|
+
genLine = srcToGen.get(nextSrc);
|
|
297
|
+
if (genLine !== undefined && genLine < headerLines) genLine = undefined;
|
|
298
|
+
}
|
|
299
|
+
if (genLine !== undefined) {
|
|
300
|
+
injects.push({ genLine, srcLine: s, comment: `// ${m[1]}` });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (injects.length > 0) {
|
|
305
|
+
// Sort descending so bottom-up insertion doesn't shift earlier positions
|
|
306
|
+
injects.sort((a, b) => b.genLine - a.genLine);
|
|
307
|
+
const tsLines = tsContent.split('\n');
|
|
308
|
+
for (const { genLine, srcLine, comment } of injects) {
|
|
309
|
+
tsLines.splice(genLine, 0, comment);
|
|
310
|
+
// Shift existing gen→src mappings at or after the insertion point
|
|
311
|
+
const shifted = new Map();
|
|
312
|
+
for (const [g, s] of genToSrc) shifted.set(g >= genLine ? g + 1 : g, s);
|
|
313
|
+
shifted.set(genLine, srcLine);
|
|
314
|
+
genToSrc.clear();
|
|
315
|
+
for (const [g, s] of shifted) genToSrc.set(g, s);
|
|
316
|
+
// Shift existing src→gen mappings that pointed at or past the insertion
|
|
317
|
+
for (const [s, g] of srcToGen) {
|
|
318
|
+
if (g >= genLine) srcToGen.set(s, g + 1);
|
|
319
|
+
}
|
|
320
|
+
srcToGen.set(srcLine, genLine);
|
|
321
|
+
}
|
|
322
|
+
tsContent = tsLines.join('\n');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
147
326
|
return { tsContent, headerLines, hasTypes, srcToGen, genToSrc, source, dts };
|
|
148
327
|
}
|
|
149
328
|
|
|
@@ -288,6 +467,9 @@ export async function runCheck(targetDir, opts = {}) {
|
|
|
288
467
|
|
|
289
468
|
const service = ts.createLanguageService(host, ts.createDocumentRegistry());
|
|
290
469
|
|
|
470
|
+
// Patch uninitialized variables with inferred types (same as LSP)
|
|
471
|
+
patchUninitializedTypes(ts, service, compiled);
|
|
472
|
+
|
|
291
473
|
// Collect diagnostics
|
|
292
474
|
let totalErrors = 0;
|
|
293
475
|
let totalWarnings = 0;
|
package/src/types.js
CHANGED
|
@@ -26,7 +26,7 @@ export function installTypeSupport(Lexer) {
|
|
|
26
26
|
//
|
|
27
27
|
// Scans the token stream for:
|
|
28
28
|
// :: (TYPE_ANNOTATION) — collects type string, stores on surviving token
|
|
29
|
-
//
|
|
29
|
+
// type Name = (contextual keyword) — collects type body, replaces with TYPE_DECL marker
|
|
30
30
|
// INTERFACE — collects body, replaces with TYPE_DECL marker
|
|
31
31
|
// DEF IDENTIFIER<...> — collects generic params via .spaced detection
|
|
32
32
|
//
|
|
@@ -50,29 +50,27 @@ export function installTypeSupport(Lexer) {
|
|
|
50
50
|
this.scanTokens((token, i, tokens) => {
|
|
51
51
|
let tag = token[0];
|
|
52
52
|
|
|
53
|
-
// ── Generic type parameters: DEF name<T>(...)
|
|
53
|
+
// ── Generic type parameters: DEF name<T>(...) ──────────────────────
|
|
54
|
+
// (Generic params on type aliases are handled by the `type` keyword handler below)
|
|
54
55
|
if (tag === 'IDENTIFIER') {
|
|
55
56
|
let next = tokens[i + 1];
|
|
56
57
|
if (next && next[0] === 'COMPARE' && next[1] === '<' && !next.spaced) {
|
|
57
58
|
let isDef = tokens[i - 1]?.[0] === 'DEF';
|
|
58
59
|
let genTokens = collectBalancedAngles(tokens, i + 1);
|
|
59
|
-
if (genTokens) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (d === 0) tokens[m][0] = 'CALL_END';
|
|
74
|
-
m++;
|
|
75
|
-
}
|
|
60
|
+
if (genTokens && isDef) {
|
|
61
|
+
if (!token.data) token.data = {};
|
|
62
|
+
token.data.typeParams = buildTypeString(genTokens);
|
|
63
|
+
tokens.splice(i + 1, genTokens.length);
|
|
64
|
+
// After removing <T>, retag ( as CALL_START if it follows DEF IDENTIFIER
|
|
65
|
+
if (tokens[i + 1]?.[0] === '(') {
|
|
66
|
+
tokens[i + 1][0] = 'CALL_START';
|
|
67
|
+
// Find matching ) and retag as CALL_END
|
|
68
|
+
let d = 1, m = i + 2;
|
|
69
|
+
while (m < tokens.length && d > 0) {
|
|
70
|
+
if (tokens[m][0] === '(' || tokens[m][0] === 'CALL_START') d++;
|
|
71
|
+
if (tokens[m][0] === ')' || tokens[m][0] === 'CALL_END') d--;
|
|
72
|
+
if (d === 0) tokens[m][0] = 'CALL_END';
|
|
73
|
+
m++;
|
|
76
74
|
}
|
|
77
75
|
}
|
|
78
76
|
}
|
|
@@ -125,14 +123,33 @@ export function installTypeSupport(Lexer) {
|
|
|
125
123
|
return 0;
|
|
126
124
|
}
|
|
127
125
|
|
|
128
|
-
// ──
|
|
129
|
-
if (tag === '
|
|
130
|
-
let
|
|
131
|
-
|
|
126
|
+
// ── type Name = ... — contextual type keyword ──────────────────────
|
|
127
|
+
if (tag === 'IDENTIFIER' && token[1] === 'type') {
|
|
128
|
+
let prevTag = tokens[i - 1]?.[0];
|
|
129
|
+
let atStatement = !prevTag || prevTag === 'TERMINATOR' || prevTag === 'INDENT' || prevTag === 'EXPORT';
|
|
130
|
+
if (!atStatement) return 1;
|
|
131
|
+
|
|
132
|
+
let nameIdx = i + 1;
|
|
133
|
+
let nameToken = tokens[nameIdx];
|
|
134
|
+
if (!nameToken || nameToken[0] !== 'IDENTIFIER') return 1;
|
|
132
135
|
let name = nameToken[1];
|
|
133
|
-
|
|
134
|
-
let
|
|
135
|
-
let
|
|
136
|
+
|
|
137
|
+
let exported = prevTag === 'EXPORT';
|
|
138
|
+
let removeFrom = exported ? i - 1 : i;
|
|
139
|
+
|
|
140
|
+
// Handle generic type parameters: type Name<T> = ...
|
|
141
|
+
let eqIdx = nameIdx + 1;
|
|
142
|
+
if (tokens[eqIdx]?.[0] === 'COMPARE' && tokens[eqIdx]?.[1] === '<' && !tokens[eqIdx].spaced) {
|
|
143
|
+
let genTokens = collectBalancedAngles(tokens, eqIdx);
|
|
144
|
+
if (genTokens) {
|
|
145
|
+
if (!nameToken.data) nameToken.data = {};
|
|
146
|
+
nameToken.data.typeParams = buildTypeString(genTokens);
|
|
147
|
+
tokens.splice(eqIdx, genTokens.length);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Must have = after name (or after stripped generics)
|
|
152
|
+
if (tokens[eqIdx]?.[0] !== '=') return 1;
|
|
136
153
|
|
|
137
154
|
let makeDecl = (typeText) => {
|
|
138
155
|
let dt = gen('TYPE_DECL', name, nameToken);
|
|
@@ -141,26 +158,29 @@ export function installTypeSupport(Lexer) {
|
|
|
141
158
|
return dt;
|
|
142
159
|
};
|
|
143
160
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
tokens[i + 2]?.[0] === 'INDENT') {
|
|
147
|
-
let endIdx = findMatchingOutdent(tokens, i + 2);
|
|
148
|
-
tokens.splice(removeFrom, endIdx - removeFrom + 1, makeDecl(collectStructuralType(tokens, i + 2)));
|
|
149
|
-
return 0;
|
|
150
|
-
}
|
|
161
|
+
let afterEq = eqIdx + 1;
|
|
162
|
+
let next = tokens[afterEq];
|
|
151
163
|
|
|
152
|
-
// Block union: Name
|
|
164
|
+
// Block union: type Name = (TERMINATOR?) INDENT | "a" | "b" ... OUTDENT
|
|
165
|
+
// Must check before structural — `=` suppresses TERMINATOR so INDENT follows directly
|
|
153
166
|
if (next && (next[0] === 'TERMINATOR' || next[0] === 'INDENT')) {
|
|
154
|
-
let result = collectBlockUnion(tokens,
|
|
167
|
+
let result = collectBlockUnion(tokens, afterEq);
|
|
155
168
|
if (result) {
|
|
156
169
|
tokens.splice(removeFrom, result.endIdx - removeFrom + 1, makeDecl(result.typeText));
|
|
157
170
|
return 0;
|
|
158
171
|
}
|
|
159
172
|
}
|
|
160
173
|
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
174
|
+
// Structural type: type Name = INDENT ... OUTDENT
|
|
175
|
+
if (next && next[0] === 'INDENT') {
|
|
176
|
+
let endIdx = findMatchingOutdent(tokens, afterEq);
|
|
177
|
+
tokens.splice(removeFrom, endIdx - removeFrom + 1, makeDecl(collectStructuralType(tokens, afterEq)));
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Simple alias: type Name = type-expression
|
|
182
|
+
let typeTokens = collectTypeExpression(tokens, afterEq);
|
|
183
|
+
tokens.splice(removeFrom, afterEq + typeTokens.length - removeFrom, makeDecl(buildTypeString(typeTokens)));
|
|
164
184
|
return 0;
|
|
165
185
|
}
|
|
166
186
|
|
|
@@ -932,9 +952,14 @@ export function emitTypes(tokens, sexpr = null) {
|
|
|
932
952
|
let preamble = [];
|
|
933
953
|
if (usesSignal) {
|
|
934
954
|
preamble.push('interface Signal<T> { value: T; read(): T; lock(): Signal<T>; free(): Signal<T>; kill(): T; }');
|
|
955
|
+
preamble.push('declare function __state<T>(value: T): Signal<T>;');
|
|
935
956
|
}
|
|
936
957
|
if (usesComputed) {
|
|
937
958
|
preamble.push('interface Computed<T> { readonly value: T; read(): T; lock(): Computed<T>; free(): Computed<T>; kill(): T; }');
|
|
959
|
+
preamble.push('declare function __computed<T>(fn: () => T): Computed<T>;');
|
|
960
|
+
}
|
|
961
|
+
if (usesSignal || usesComputed) {
|
|
962
|
+
preamble.push('declare function __effect(fn: () => void | (() => void)): () => void;');
|
|
938
963
|
}
|
|
939
964
|
if (preamble.length > 0) {
|
|
940
965
|
preamble.push('');
|