@vesk/compiler 0.0.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/README.md +23 -0
- package/dist/acorn-ts-plugin/index.d.ts +482 -0
- package/dist/acorn-ts-plugin/index.d.ts.map +1 -0
- package/dist/acorn-ts-plugin/index.js +5028 -0
- package/dist/actions.d.ts +22 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +167 -0
- package/dist/api-routes.d.ts +30 -0
- package/dist/api-routes.d.ts.map +1 -0
- package/dist/api-routes.js +350 -0
- package/dist/client-codegen.d.ts +19 -0
- package/dist/client-codegen.d.ts.map +1 -0
- package/dist/client-codegen.js +1523 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +106 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +210 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/ir-generator.d.ts +10 -0
- package/dist/ir-generator.d.ts.map +1 -0
- package/dist/ir-generator.js +1062 -0
- package/dist/ir.d.ts +160 -0
- package/dist/ir.d.ts.map +1 -0
- package/dist/ir.js +219 -0
- package/dist/middleware.d.ts +15 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +183 -0
- package/dist/package.json +15 -0
- package/dist/parser.d.ts +38 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +242 -0
- package/dist/router.d.ts +31 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +451 -0
- package/dist/scan.d.ts +106 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +438 -0
- package/dist/server-codegen.d.ts +5 -0
- package/dist/server-codegen.d.ts.map +1 -0
- package/dist/server-codegen.js +4 -0
- package/dist/server-cookies.d.ts +2 -0
- package/dist/server-cookies.d.ts.map +1 -0
- package/dist/server-cookies.js +15 -0
- package/dist/server-head.d.ts +10 -0
- package/dist/server-head.d.ts.map +1 -0
- package/dist/server-head.js +289 -0
- package/dist/server-jsgen.d.ts +6 -0
- package/dist/server-jsgen.d.ts.map +1 -0
- package/dist/server-jsgen.js +446 -0
- package/dist/server-render.d.ts +16 -0
- package/dist/server-render.d.ts.map +1 -0
- package/dist/server-render.js +397 -0
- package/dist/server-utils.d.ts +93 -0
- package/dist/server-utils.d.ts.map +1 -0
- package/dist/server-utils.js +747 -0
- package/dist/ssr-store.d.ts +4 -0
- package/dist/ssr-store.d.ts.map +1 -0
- package/dist/ssr-store.js +29 -0
- package/dist/strip-ts.d.ts +25 -0
- package/dist/strip-ts.d.ts.map +1 -0
- package/dist/strip-ts.js +171 -0
- package/dist/tokens.d.ts +50 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +334 -0
- package/dist/typecheck.d.ts +19 -0
- package/dist/typecheck.d.ts.map +1 -0
- package/dist/typecheck.js +365 -0
- package/dist/types.d.ts +147 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/vesk-plugin.d.ts +53 -0
- package/dist/vesk-plugin.d.ts.map +1 -0
- package/dist/vesk-plugin.js +413 -0
- package/dist/vsk-imports.d.ts +30 -0
- package/dist/vsk-imports.d.ts.map +1 -0
- package/dist/vsk-imports.js +141 -0
- package/dist/vsk-tsx.d.ts +3 -0
- package/dist/vsk-tsx.d.ts.map +1 -0
- package/dist/vsk-tsx.js +604 -0
- package/package.json +52 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr-store.d.ts","sourceRoot":"","sources":["../src/ssr-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAI9E,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAI9C;AAED,eAAO,MAAM,OAAO,EAAE,WAgBrB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { setSsrSink } from '@vesk/runtime/src/index-server';
|
|
3
|
+
const storage = new AsyncLocalStorage();
|
|
4
|
+
export function withSsrStore(fn) {
|
|
5
|
+
const existing = storage.getStore();
|
|
6
|
+
if (existing)
|
|
7
|
+
return fn();
|
|
8
|
+
return storage.run({}, fn);
|
|
9
|
+
}
|
|
10
|
+
export const ssrSink = {
|
|
11
|
+
set: (key, value) => {
|
|
12
|
+
const store = storage.getStore();
|
|
13
|
+
if (store)
|
|
14
|
+
store[key] = value;
|
|
15
|
+
},
|
|
16
|
+
get: (key) => {
|
|
17
|
+
const store = storage.getStore();
|
|
18
|
+
return store ? store[key] : undefined;
|
|
19
|
+
},
|
|
20
|
+
snapshot: () => ({ ...(storage.getStore() ?? {}) }),
|
|
21
|
+
clear: () => {
|
|
22
|
+
const store = storage.getStore();
|
|
23
|
+
if (store) {
|
|
24
|
+
for (const key of Object.keys(store))
|
|
25
|
+
delete store[key];
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
setSsrSink(ssrSink);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** True when the AST contains any TypeScript-only node. */
|
|
2
|
+
export declare function hasTsSyntax(ast: any): boolean;
|
|
3
|
+
/** True when a Program-body statement is type-only (safe to drop from emitted JS). */
|
|
4
|
+
export declare function isTypeOnlyStatement(node: any): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Removes TypeScript-only syntax from an estree AST:
|
|
7
|
+
* - type annotations on declarators, params, return types and class fields
|
|
8
|
+
* - `as` / `satisfies` / `!` / `<T>expr` / generic-call wrappers, replaced by
|
|
9
|
+
* their inner expression
|
|
10
|
+
* - type arguments on call/new expressions and type parameter declarations
|
|
11
|
+
* - whole type-only statements (interfaces, type aliases, declare/abstract
|
|
12
|
+
* members), replaced by null entries
|
|
13
|
+
*
|
|
14
|
+
* Returns a new tree (zimmerframe walks are immutable); callers must use the
|
|
15
|
+
* return value.
|
|
16
|
+
*/
|
|
17
|
+
export declare function stripTsTypes(ast: any): any;
|
|
18
|
+
/**
|
|
19
|
+
* Strips TypeScript-only syntax from a plain-JS source snippet (a top-level
|
|
20
|
+
* function like `load`/`getStaticProps`, or an action block). Returns the
|
|
21
|
+
* original source untouched when there is no TS syntax or the reprint fails,
|
|
22
|
+
* so byte-identical fast paths stay intact.
|
|
23
|
+
*/
|
|
24
|
+
export declare function stripCodeTypes(code: string): string;
|
|
25
|
+
//# sourceMappingURL=strip-ts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip-ts.d.ts","sourceRoot":"","sources":["../src/strip-ts.ts"],"names":[],"mappings":"AAwBA,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAY7C;AAYD,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAMtD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CA6D1C;AAWD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiBnD"}
|
package/dist/strip-ts.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { walk } from 'zimmerframe';
|
|
2
|
+
import { print } from 'esrap';
|
|
3
|
+
import ts from 'esrap/languages/ts';
|
|
4
|
+
import { parse } from '@vesk/compiler/src/parser';
|
|
5
|
+
const TS_NODE_TYPES = new Set([
|
|
6
|
+
'TSAsExpression',
|
|
7
|
+
'TSSatisfiesExpression',
|
|
8
|
+
'TSNonNullExpression',
|
|
9
|
+
'TSTypeAssertion',
|
|
10
|
+
'TSInstantiationExpression',
|
|
11
|
+
'TSTypeAnnotation',
|
|
12
|
+
'TSTypeParameterDeclaration',
|
|
13
|
+
'TSTypeParameterInstantiation',
|
|
14
|
+
'TSInterfaceDeclaration',
|
|
15
|
+
'TSTypeAliasDeclaration',
|
|
16
|
+
'TSDeclareFunction',
|
|
17
|
+
'TSDeclareStatement',
|
|
18
|
+
'TSEnumDeclaration',
|
|
19
|
+
'TSModuleDeclaration',
|
|
20
|
+
'TSImportEqualsDeclaration',
|
|
21
|
+
'TSParameterProperty',
|
|
22
|
+
]);
|
|
23
|
+
/** True when the AST contains any TypeScript-only node. */
|
|
24
|
+
export function hasTsSyntax(ast) {
|
|
25
|
+
let found = false;
|
|
26
|
+
walk(ast, null, {
|
|
27
|
+
_(node, context) {
|
|
28
|
+
if (TS_NODE_TYPES.has(node.type)) {
|
|
29
|
+
found = true;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
return context.next();
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
return found;
|
|
36
|
+
}
|
|
37
|
+
const TYPE_ONLY_STATEMENTS = new Set([
|
|
38
|
+
'TSInterfaceDeclaration',
|
|
39
|
+
'TSTypeAliasDeclaration',
|
|
40
|
+
'TSDeclareFunction',
|
|
41
|
+
'TSDeclareStatement',
|
|
42
|
+
'TSEnumDeclaration',
|
|
43
|
+
'TSModuleDeclaration',
|
|
44
|
+
'TSImportEqualsDeclaration',
|
|
45
|
+
]);
|
|
46
|
+
/** True when a Program-body statement is type-only (safe to drop from emitted JS). */
|
|
47
|
+
export function isTypeOnlyStatement(node) {
|
|
48
|
+
if (!node)
|
|
49
|
+
return true;
|
|
50
|
+
if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') {
|
|
51
|
+
return !!node.declaration && isTypeOnlyStatement(node.declaration);
|
|
52
|
+
}
|
|
53
|
+
return TYPE_ONLY_STATEMENTS.has(node.type);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Removes TypeScript-only syntax from an estree AST:
|
|
57
|
+
* - type annotations on declarators, params, return types and class fields
|
|
58
|
+
* - `as` / `satisfies` / `!` / `<T>expr` / generic-call wrappers, replaced by
|
|
59
|
+
* their inner expression
|
|
60
|
+
* - type arguments on call/new expressions and type parameter declarations
|
|
61
|
+
* - whole type-only statements (interfaces, type aliases, declare/abstract
|
|
62
|
+
* members), replaced by null entries
|
|
63
|
+
*
|
|
64
|
+
* Returns a new tree (zimmerframe walks are immutable); callers must use the
|
|
65
|
+
* return value.
|
|
66
|
+
*/
|
|
67
|
+
export function stripTsTypes(ast) {
|
|
68
|
+
return walk(ast, null, {
|
|
69
|
+
TSAsExpression(node, context) {
|
|
70
|
+
return context.visit(node.expression);
|
|
71
|
+
},
|
|
72
|
+
TSSatisfiesExpression(node, context) {
|
|
73
|
+
return context.visit(node.expression);
|
|
74
|
+
},
|
|
75
|
+
TSNonNullExpression(node, context) {
|
|
76
|
+
return context.visit(node.expression);
|
|
77
|
+
},
|
|
78
|
+
TSTypeAssertion(node, context) {
|
|
79
|
+
return context.visit(node.expression);
|
|
80
|
+
},
|
|
81
|
+
TSInstantiationExpression(node, context) {
|
|
82
|
+
return context.visit(node.expression);
|
|
83
|
+
},
|
|
84
|
+
VariableDeclarator(node, context) {
|
|
85
|
+
if (node.id && node.id.typeAnnotation)
|
|
86
|
+
node.id.typeAnnotation = null;
|
|
87
|
+
return context.next();
|
|
88
|
+
},
|
|
89
|
+
FunctionDeclaration(node, context) {
|
|
90
|
+
stripFunctionTypes(node);
|
|
91
|
+
return context.next();
|
|
92
|
+
},
|
|
93
|
+
FunctionExpression(node, context) {
|
|
94
|
+
stripFunctionTypes(node);
|
|
95
|
+
return context.next();
|
|
96
|
+
},
|
|
97
|
+
ArrowFunctionExpression(node, context) {
|
|
98
|
+
stripFunctionTypes(node);
|
|
99
|
+
return context.next();
|
|
100
|
+
},
|
|
101
|
+
CallExpression(node, context) {
|
|
102
|
+
if (node.typeArguments)
|
|
103
|
+
node.typeArguments = null;
|
|
104
|
+
return context.next();
|
|
105
|
+
},
|
|
106
|
+
NewExpression(node, context) {
|
|
107
|
+
if (node.typeArguments)
|
|
108
|
+
node.typeArguments = null;
|
|
109
|
+
return context.next();
|
|
110
|
+
},
|
|
111
|
+
PropertyDefinition(node, context) {
|
|
112
|
+
if (node.typeAnnotation)
|
|
113
|
+
node.typeAnnotation = null;
|
|
114
|
+
return context.next();
|
|
115
|
+
},
|
|
116
|
+
TSInterfaceDeclaration(node) {
|
|
117
|
+
return null;
|
|
118
|
+
},
|
|
119
|
+
TSTypeAliasDeclaration(node) {
|
|
120
|
+
return null;
|
|
121
|
+
},
|
|
122
|
+
TSDeclareFunction(node) {
|
|
123
|
+
return null;
|
|
124
|
+
},
|
|
125
|
+
TSEnumDeclaration(node) {
|
|
126
|
+
return null;
|
|
127
|
+
},
|
|
128
|
+
TSTypeParameterDeclaration(node) {
|
|
129
|
+
return null;
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function stripFunctionTypes(node) {
|
|
134
|
+
if (node.returnType)
|
|
135
|
+
node.returnType = null;
|
|
136
|
+
if (node.typeParameters)
|
|
137
|
+
node.typeParameters = null;
|
|
138
|
+
for (const p of node.params || []) {
|
|
139
|
+
if (p.typeAnnotation)
|
|
140
|
+
p.typeAnnotation = null;
|
|
141
|
+
if (p.optional)
|
|
142
|
+
p.optional = false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Strips TypeScript-only syntax from a plain-JS source snippet (a top-level
|
|
147
|
+
* function like `load`/`getStaticProps`, or an action block). Returns the
|
|
148
|
+
* original source untouched when there is no TS syntax or the reprint fails,
|
|
149
|
+
* so byte-identical fast paths stay intact.
|
|
150
|
+
*/
|
|
151
|
+
export function stripCodeTypes(code) {
|
|
152
|
+
let ast;
|
|
153
|
+
try {
|
|
154
|
+
ast = parse(code);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return code;
|
|
158
|
+
}
|
|
159
|
+
if (!hasTsSyntax(ast))
|
|
160
|
+
return code;
|
|
161
|
+
const stripped = stripTsTypes(ast);
|
|
162
|
+
if (stripped.type === 'Program' && Array.isArray(stripped.body)) {
|
|
163
|
+
stripped.body = stripped.body.filter((n) => !isTypeOnlyStatement(n));
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
return print(stripped, ts()).code;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return code;
|
|
170
|
+
}
|
|
171
|
+
}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-based syntax analysis built on the acorn tokenizer (via the Vesk
|
|
3
|
+
* TS/JSX parser). The tokenizer understands generics, JSX, template
|
|
4
|
+
* literals and the `&[...]` track-declaration sugar, so identifier-call
|
|
5
|
+
* detection and import parsing no longer need regexes. Every helper falls
|
|
6
|
+
* back to a character-level scan when the input cannot be tokenized (e.g.
|
|
7
|
+
* mid-edit content or partial fragments), so callers never crash on
|
|
8
|
+
* unparsable text.
|
|
9
|
+
*/
|
|
10
|
+
export interface CodeToken {
|
|
11
|
+
label: string;
|
|
12
|
+
value: string;
|
|
13
|
+
start: number;
|
|
14
|
+
end: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Tokenizes `code` with the Vesk parser's tokenizer. Returns an array of
|
|
18
|
+
* tokens (excluding EOF), or `null` when the input cannot be tokenized.
|
|
19
|
+
* `value` holds identifier/keyword/string text; punctuation tokens expose
|
|
20
|
+
* their span so callers can read the actual character via `code[start]`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function tokenizeCode(code: string): CodeToken[] | null;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the set of identifiers in `code` that are called — an identifier
|
|
25
|
+
* token immediately followed by `(` or by a `<...>` generic clause and then
|
|
26
|
+
* `(`. Member accesses (`obj.fn(`, `obj?.fn(`) are excluded so methods are
|
|
27
|
+
* not mistaken for imported runtime functions.
|
|
28
|
+
*/
|
|
29
|
+
export declare function collectCalledIdentifiers(code: string): Set<string>;
|
|
30
|
+
/**
|
|
31
|
+
* True when `code` contains the identifier `name` (as a plain identifier or
|
|
32
|
+
* a JSX name), outside strings and comments.
|
|
33
|
+
*/
|
|
34
|
+
export declare function containsIdentifier(code: string, name: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* True when an `import` statement in `code` imports the name `name`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isIdentifierImported(code: string, name: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Extracts the locally-bound names from a single import statement
|
|
41
|
+
* (`import { a as b, type C } from 'm'` → `['b']`; `import X from 'm'` and
|
|
42
|
+
* `import * as X from 'm'` → `['X']`). Type-only specifiers are dropped.
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractImportNames(importText: string): string[];
|
|
45
|
+
/**
|
|
46
|
+
* Returns the module specifier of an import statement — the string literal
|
|
47
|
+
* after `from` (or the bare side-effect string). `null` when none found.
|
|
48
|
+
*/
|
|
49
|
+
export declare function importModuleTarget(importText: string): string | null;
|
|
50
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,IAAI,CAqB7D;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiBlE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAStE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAqBxE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAuC/D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-based syntax analysis built on the acorn tokenizer (via the Vesk
|
|
3
|
+
* TS/JSX parser). The tokenizer understands generics, JSX, template
|
|
4
|
+
* literals and the `&[...]` track-declaration sugar, so identifier-call
|
|
5
|
+
* detection and import parsing no longer need regexes. Every helper falls
|
|
6
|
+
* back to a character-level scan when the input cannot be tokenized (e.g.
|
|
7
|
+
* mid-edit content or partial fragments), so callers never crash on
|
|
8
|
+
* unparsable text.
|
|
9
|
+
*/
|
|
10
|
+
import { createBaseParser } from '@vesk/compiler/src/parser';
|
|
11
|
+
import { skipString, skipComment, skipWhitespace, skipTrackGeneric, isIdentStart, isIdentChar, } from '@vesk/compiler/src/scan';
|
|
12
|
+
/**
|
|
13
|
+
* Tokenizes `code` with the Vesk parser's tokenizer. Returns an array of
|
|
14
|
+
* tokens (excluding EOF), or `null` when the input cannot be tokenized.
|
|
15
|
+
* `value` holds identifier/keyword/string text; punctuation tokens expose
|
|
16
|
+
* their span so callers can read the actual character via `code[start]`.
|
|
17
|
+
*/
|
|
18
|
+
export function tokenizeCode(code) {
|
|
19
|
+
try {
|
|
20
|
+
const ParserClass = createBaseParser();
|
|
21
|
+
const tok = ParserClass.tokenizer(code, {
|
|
22
|
+
ecmaVersion: 'latest',
|
|
23
|
+
sourceType: 'module',
|
|
24
|
+
});
|
|
25
|
+
const out = [];
|
|
26
|
+
let t;
|
|
27
|
+
while ((t = tok.getToken()) && t.type && t.type.label !== 'eof') {
|
|
28
|
+
out.push({
|
|
29
|
+
label: t.type.label,
|
|
30
|
+
value: typeof t.value === 'string' ? t.value : '',
|
|
31
|
+
start: t.start,
|
|
32
|
+
end: t.end,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns the set of identifiers in `code` that are called — an identifier
|
|
43
|
+
* token immediately followed by `(` or by a `<...>` generic clause and then
|
|
44
|
+
* `(`. Member accesses (`obj.fn(`, `obj?.fn(`) are excluded so methods are
|
|
45
|
+
* not mistaken for imported runtime functions.
|
|
46
|
+
*/
|
|
47
|
+
export function collectCalledIdentifiers(code) {
|
|
48
|
+
const tokens = tokenizeCode(code);
|
|
49
|
+
if (tokens !== null) {
|
|
50
|
+
const result = new Set();
|
|
51
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
52
|
+
const t = tokens[i];
|
|
53
|
+
if (t.label !== 'name')
|
|
54
|
+
continue;
|
|
55
|
+
const prev = i > 0 ? tokens[i - 1] : null;
|
|
56
|
+
if (prev && (prev.label === '.' || prev.label === '?.'))
|
|
57
|
+
continue;
|
|
58
|
+
const next = tokens[i + 1];
|
|
59
|
+
if (!next)
|
|
60
|
+
continue;
|
|
61
|
+
const nextCh = code[next.start];
|
|
62
|
+
if (nextCh === '(' || nextCh === '<')
|
|
63
|
+
result.add(t.value);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
return manualCollectCalledIdentifiers(code);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* True when `code` contains the identifier `name` (as a plain identifier or
|
|
71
|
+
* a JSX name), outside strings and comments.
|
|
72
|
+
*/
|
|
73
|
+
export function containsIdentifier(code, name) {
|
|
74
|
+
const tokens = tokenizeCode(code);
|
|
75
|
+
if (tokens !== null) {
|
|
76
|
+
for (const t of tokens) {
|
|
77
|
+
if ((t.label === 'name' || t.label === 'jsxName') && t.value === name)
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return manualContainsIdentifier(code, name);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* True when an `import` statement in `code` imports the name `name`.
|
|
86
|
+
*/
|
|
87
|
+
export function isIdentifierImported(code, name) {
|
|
88
|
+
const tokens = tokenizeCode(code);
|
|
89
|
+
if (tokens !== null) {
|
|
90
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
91
|
+
if (tokens[i].label !== 'import')
|
|
92
|
+
continue;
|
|
93
|
+
let j = i + 1;
|
|
94
|
+
while (j < tokens.length &&
|
|
95
|
+
tokens[j].label !== ';' &&
|
|
96
|
+
!(tokens[j].label === 'name' && tokens[j].value === 'from')) {
|
|
97
|
+
j++;
|
|
98
|
+
}
|
|
99
|
+
for (let k = i + 1; k < j; k++) {
|
|
100
|
+
if ((tokens[k].label === 'name' || tokens[k].label === 'jsxName') && tokens[k].value === name)
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
i = j;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return manualIsIdentifierImported(code, name);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Extracts the locally-bound names from a single import statement
|
|
111
|
+
* (`import { a as b, type C } from 'm'` → `['b']`; `import X from 'm'` and
|
|
112
|
+
* `import * as X from 'm'` → `['X']`). Type-only specifiers are dropped.
|
|
113
|
+
*/
|
|
114
|
+
export function extractImportNames(importText) {
|
|
115
|
+
const tokens = tokenizeCode(importText);
|
|
116
|
+
if (tokens !== null) {
|
|
117
|
+
const names = [];
|
|
118
|
+
let i = 0;
|
|
119
|
+
while (i < tokens.length && tokens[i].label !== 'import')
|
|
120
|
+
i++;
|
|
121
|
+
if (i >= tokens.length)
|
|
122
|
+
return names;
|
|
123
|
+
i++;
|
|
124
|
+
if (i < tokens.length && tokens[i].label === 'name' && tokens[i].value === 'type')
|
|
125
|
+
i++;
|
|
126
|
+
if (i < tokens.length && tokens[i].label === '{') {
|
|
127
|
+
i++;
|
|
128
|
+
while (i < tokens.length && tokens[i].label !== '}') {
|
|
129
|
+
const t = tokens[i];
|
|
130
|
+
if (t.label === 'name') {
|
|
131
|
+
if (t.value === 'type' || t.value === 'typeof') {
|
|
132
|
+
i++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
let name = t.value;
|
|
136
|
+
if (i + 2 < tokens.length &&
|
|
137
|
+
tokens[i + 1].label === 'name' && tokens[i + 1].value === 'as' &&
|
|
138
|
+
tokens[i + 2].label === 'name') {
|
|
139
|
+
name = tokens[i + 2].value;
|
|
140
|
+
i += 2;
|
|
141
|
+
}
|
|
142
|
+
names.push(name);
|
|
143
|
+
}
|
|
144
|
+
i++;
|
|
145
|
+
}
|
|
146
|
+
return names;
|
|
147
|
+
}
|
|
148
|
+
if (i < tokens.length && tokens[i].label === '*') {
|
|
149
|
+
while (i < tokens.length && tokens[i].label !== 'name')
|
|
150
|
+
i++;
|
|
151
|
+
if (i < tokens.length)
|
|
152
|
+
names.push(tokens[i].value);
|
|
153
|
+
}
|
|
154
|
+
else if (i < tokens.length && tokens[i].label === 'name') {
|
|
155
|
+
names.push(tokens[i].value);
|
|
156
|
+
}
|
|
157
|
+
return names;
|
|
158
|
+
}
|
|
159
|
+
return manualExtractImportNames(importText);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Returns the module specifier of an import statement — the string literal
|
|
163
|
+
* after `from` (or the bare side-effect string). `null` when none found.
|
|
164
|
+
*/
|
|
165
|
+
export function importModuleTarget(importText) {
|
|
166
|
+
const tokens = tokenizeCode(importText);
|
|
167
|
+
if (tokens === null)
|
|
168
|
+
return null;
|
|
169
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
170
|
+
if (tokens[i].label === 'string')
|
|
171
|
+
return tokens[i].value;
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
function manualCollectCalledIdentifiers(code) {
|
|
176
|
+
const result = new Set();
|
|
177
|
+
let i = 0;
|
|
178
|
+
while (i < code.length) {
|
|
179
|
+
const c = code[i];
|
|
180
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
181
|
+
i = skipString(code, i);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (c === '/' && (code[i + 1] === '/' || code[i + 1] === '*')) {
|
|
185
|
+
i = skipComment(code, i);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (isIdentStart(c)) {
|
|
189
|
+
let j = i + 1;
|
|
190
|
+
while (j < code.length && isIdentChar(code[j]))
|
|
191
|
+
j++;
|
|
192
|
+
const before = i === 0 ? '' : code[i - 1];
|
|
193
|
+
if (before !== '.' && !isIdentChar(before)) {
|
|
194
|
+
let k = skipWhitespace(code, j);
|
|
195
|
+
if (code[k] === '<')
|
|
196
|
+
k = skipTrackGeneric(code, k);
|
|
197
|
+
if (code[k] === '(')
|
|
198
|
+
result.add(code.slice(i, j));
|
|
199
|
+
}
|
|
200
|
+
i = j;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
i++;
|
|
204
|
+
}
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
function manualContainsIdentifier(code, name) {
|
|
208
|
+
let i = 0;
|
|
209
|
+
while (i < code.length) {
|
|
210
|
+
const c = code[i];
|
|
211
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
212
|
+
i = skipString(code, i);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (c === '/' && (code[i + 1] === '/' || code[i + 1] === '*')) {
|
|
216
|
+
i = skipComment(code, i);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (isIdentStart(c) && code.slice(i, i + name.length) === name) {
|
|
220
|
+
const after = i + name.length;
|
|
221
|
+
if ((after >= code.length || !isIdentChar(code[after])) && (i === 0 || !isIdentChar(code[i - 1])))
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
i++;
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
function manualIsIdentifierImported(code, name) {
|
|
229
|
+
let i = 0;
|
|
230
|
+
while (i < code.length) {
|
|
231
|
+
const c = code[i];
|
|
232
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
233
|
+
i = skipString(code, i);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (c === '/' && (code[i + 1] === '/' || code[i + 1] === '*')) {
|
|
237
|
+
i = skipComment(code, i);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (isIdentStart(c)) {
|
|
241
|
+
let j = i + 1;
|
|
242
|
+
while (j < code.length && isIdentChar(code[j]))
|
|
243
|
+
j++;
|
|
244
|
+
const word = code.slice(i, j);
|
|
245
|
+
if (word === 'import') {
|
|
246
|
+
let k = j;
|
|
247
|
+
while (k < code.length) {
|
|
248
|
+
if (code[k] === ';' || code[k] === '\n')
|
|
249
|
+
break;
|
|
250
|
+
if (code[k] === '"' || code[k] === "'" || code[k] === '`') {
|
|
251
|
+
k = skipString(code, k);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (isIdentStart(code[k]) && code.slice(k, k + name.length) === name) {
|
|
255
|
+
const a = k + name.length;
|
|
256
|
+
if ((a >= code.length || !isIdentChar(code[a])) && (k === 0 || !isIdentChar(code[k - 1])))
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
k++;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
i = j;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
i++;
|
|
266
|
+
}
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
function manualExtractImportNames(importText) {
|
|
270
|
+
const names = [];
|
|
271
|
+
const open = importText.indexOf('{');
|
|
272
|
+
if (open !== -1) {
|
|
273
|
+
const close = importText.indexOf('}', open + 1);
|
|
274
|
+
if (close !== -1) {
|
|
275
|
+
let i = open + 1;
|
|
276
|
+
while (i < close) {
|
|
277
|
+
const c = importText[i];
|
|
278
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
279
|
+
i = skipString(importText, i);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (isIdentStart(c)) {
|
|
283
|
+
let j = i + 1;
|
|
284
|
+
while (j < close && isIdentChar(importText[j]))
|
|
285
|
+
j++;
|
|
286
|
+
let word = importText.slice(i, j);
|
|
287
|
+
if (word === 'type' || word === 'typeof') {
|
|
288
|
+
i = j;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
let k = skipWhitespace(importText, j);
|
|
292
|
+
if (importText.slice(k, k + 2) === 'as' && !isIdentChar(importText[k + 2])) {
|
|
293
|
+
let m = skipWhitespace(importText, k + 2);
|
|
294
|
+
if (isIdentStart(importText[m])) {
|
|
295
|
+
let n = m + 1;
|
|
296
|
+
while (n < close && isIdentChar(importText[n]))
|
|
297
|
+
n++;
|
|
298
|
+
word = importText.slice(m, n);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
names.push(word);
|
|
302
|
+
i = j;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
i++;
|
|
306
|
+
}
|
|
307
|
+
return names;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
let i = 0;
|
|
311
|
+
while (i < importText.length && !isIdentStart(importText[i]))
|
|
312
|
+
i++;
|
|
313
|
+
while (i < importText.length && isIdentChar(importText[i]))
|
|
314
|
+
i++;
|
|
315
|
+
i = skipWhitespace(importText, i);
|
|
316
|
+
if (importText[i] === '*') {
|
|
317
|
+
i = skipWhitespace(importText, i + 1);
|
|
318
|
+
if (importText.slice(i, i + 2) === 'as')
|
|
319
|
+
i = skipWhitespace(importText, i + 2);
|
|
320
|
+
if (isIdentStart(importText[i])) {
|
|
321
|
+
let j = i + 1;
|
|
322
|
+
while (j < importText.length && isIdentChar(importText[j]))
|
|
323
|
+
j++;
|
|
324
|
+
names.push(importText.slice(i, j));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
else if (isIdentStart(importText[i])) {
|
|
328
|
+
let j = i + 1;
|
|
329
|
+
while (j < importText.length && isIdentChar(importText[j]))
|
|
330
|
+
j++;
|
|
331
|
+
names.push(importText.slice(i, j));
|
|
332
|
+
}
|
|
333
|
+
return names;
|
|
334
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface TypecheckOptions {
|
|
2
|
+
strict?: boolean;
|
|
3
|
+
appDir?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface TypecheckError {
|
|
6
|
+
file: string;
|
|
7
|
+
line: number;
|
|
8
|
+
column: number;
|
|
9
|
+
code: string;
|
|
10
|
+
message: string;
|
|
11
|
+
}
|
|
12
|
+
export interface TypecheckResult {
|
|
13
|
+
errors: TypecheckError[];
|
|
14
|
+
warnings: TypecheckError[];
|
|
15
|
+
}
|
|
16
|
+
export declare function typecheckProject(projectRoot: string, opts?: TypecheckOptions): TypecheckResult;
|
|
17
|
+
export declare function formatTypecheckErrors(errors: TypecheckError[]): string;
|
|
18
|
+
export declare function formatTypecheckWarnings(warnings: TypecheckError[]): string;
|
|
19
|
+
//# sourceMappingURL=typecheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typecheck.d.ts","sourceRoot":"","sources":["../src/typecheck.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAmQD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB,GAAG,eAAe,CA0FlG;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAItE;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAI1E"}
|